Example #1
0
        //Returns a variable from a local scope marked with NoInit flag
        //If such variable couldn't be found returns -1
        private ScopeVar GetNoInitVariable(ElaEquation s, out string name)
        {
            ScopeVar var;

            name = s.Left.GetName();

            if (s.IsFunction())
            {
                name = s.GetFunctionName();
            }

            if (CurrentScope.Locals.TryGetValue(name, out var))
            {
                //If it doesn't have a NoInit flag we are not good
                if ((var.Flags & ElaVariableFlags.NoInit) != ElaVariableFlags.NoInit)
                {
                    return(ScopeVar.Empty);
                }
                else
                {
                    var.Address = 0 | var.Address << 8; //Aligning it to local scope
                    return(var);
                }
            }

            return(ScopeVar.Empty);
        }
Example #2
0
        //Compiles a pattern as lazy by a making a right hand a thunk and then delegating
        //a job to 'CompileLazyPattern' routine.
        private void CompileLazyBindingPattern(ElaEquation eq, LabelMap map)
        {
            CompileLazyExpression(eq.Right, map, Hints.None);
            var sys = AddVariable();

            PopVar(sys);
            CompileLazyPattern(sys, eq.Left, false);
        }
Example #3
0
        //Used to compile an anonymous function (lambda). This function returns a number of parameters
        //in compiled lambda.
        private int CompileLambda(ElaEquation bid)
        {
            var fc = new ElaJuxtaposition();

            //Lambda is parsed as a an application of a lambda operator, e.g.
            //expr -> expr, therefore it needs to be transformed in order to be
            //able to use existing match compilation logic.
            if (bid.Left.Type == ElaNodeType.Juxtaposition &&
                !bid.Left.Parens)    //Parens flag is added if an expression is in parens and
                                     //therefore should be qualified as a pattern.
            {
                var f = (ElaJuxtaposition)bid.Left;
                fc.Parameters.Add(f.Target);
                fc.Parameters.AddRange(f.Parameters);
            }
            else
            {
                fc.Parameters.Add(bid.Left);
            }

            bid.Left = fc;
            var parLen = fc.Parameters.Count;

            StartFun(null, parLen);

            var funSkipLabel = cw.DefineLabel();
            var map          = new LabelMap();

            cw.Emit(Op.Br, funSkipLabel);

            //We start a real (VM based) lexical scope for a function.
            StartScope(true, bid.Right.Line, bid.Right.Column);
            StartSection();

            var address = cw.Offset;

            CompileFunctionMatch(parLen, bid, map, Hints.Scope | Hints.Tail);

            var funHandle = frame.Layouts.Count;
            var ss        = EndFun(funHandle);

            frame.Layouts.Add(new MemoryLayout(currentCounter, ss, address));
            EndScope();
            EndSection();

            cw.Emit(Op.Ret);
            cw.MarkLabel(funSkipLabel);

            AddLinePragma(bid);

            //Function is constructed
            cw.Emit(Op.PushI4, parLen);
            cw.Emit(Op.Newfun, funHandle);
            return(parLen);
        }
Example #4
0
        //Adds a variable with NoInit flag to the current scope
        //This method also calculates additional flags and metadata for variables.
        //If a given binding if defined by pattern matching then all variables from
        //patterns are traversed using AddPatternVariable method.
        private bool AddNoInitVariable(ElaEquation exp)
        {
            var flags = exp.VariableFlags | ElaVariableFlags.NoInit;

            //This binding is not defined by PM
            if (exp.IsFunction())
            {
                var name = exp.GetFunctionName();

                if (name == null || Char.IsUpper(name[0]))
                {
                    AddError(ElaCompilerError.InvalidFunctionDeclaration, exp, FormatNode(exp.Left));
                    return(false);
                }

                AddVariable(name, exp.Left, flags | ElaVariableFlags.Function, exp.GetArgumentNumber());
            }
            else if (exp.Left.Type == ElaNodeType.NameReference && !((ElaNameReference)exp.Left).Uppercase)
            {
                var data = -1;

                if (exp.Right.Type == ElaNodeType.Builtin)
                {
                    //Adding required hints for a built-in
                    data   = (Int32)((ElaBuiltin)exp.Right).Kind;
                    flags |= ElaVariableFlags.Builtin;
                }
                else if (exp.Right.Type == ElaNodeType.Lambda)
                {
                    var fun = (ElaLambda)exp.Right;
                    flags |= ElaVariableFlags.Function;
                    data   = fun.GetParameterCount();
                }
                else if (exp.Right.IsLiteral())
                {
                    flags |= ElaVariableFlags.ObjectLiteral;
                }

                AddVariable(exp.Left.GetName(), exp, flags, data);
            }
            else
            {
                AddPatternVariables(exp.Left);
            }

            return(true);
        }
Example #5
0
        private void ProcessBinding(ElaEquationSet block, ElaEquation bid, ElaExpression left, ElaExpression right)
        {
            bid.Left  = left;
            bid.Right = right;

            if (bindings.Peek() == unit)
            {
                block.Equations.Add(bid);
                return;
            }

            var fName = default(String);

            if (right != null && left.Type == ElaNodeType.Juxtaposition && !left.Parens)
            {
                var fc = (ElaJuxtaposition)left;

                if (fc.Target.Type == ElaNodeType.NameReference)
                {
                    fName = fc.Target.GetName();
                }
            }

            if (fName != null)
            {
                var lastB = bindings.Peek();

                if (lastB != null && ((ElaJuxtaposition)lastB.Left).Target.GetName() == fName)
                {
                    lastB.Next = bid;
                }
                else
                {
                    block.Equations.Add(bid);
                }

                bindings.Pop();
                bindings.Push(bid);
            }
            else
            {
                block.Equations.Add(bid);
            }
        }
Example #6
0
        //Validate correctness of a binding
        private void ValidateBinding(ElaEquation s)
        {
            //These errors are not critical and allow to continue compilation
            if (s.Right.Type == ElaNodeType.Builtin && s.Left.Type != ElaNodeType.NameReference)
            {
                AddError(ElaCompilerError.InvalidBuiltinBinding, s);
            }

            if ((s.VariableFlags & ElaVariableFlags.Private) == ElaVariableFlags.Private && CurrentScope != globalScope)
            {
                AddError(ElaCompilerError.PrivateOnlyGlobal, s);
            }

            //Bang patterns are only allowed in functions and constructors
            if (s.Left.Type == ElaNodeType.NameReference && ((ElaNameReference)s.Left).Bang)
            {
                AddError(ElaCompilerError.BangPatternNotValid, s, FormatNode(s.Left));
                AddHint(ElaCompilerHint.BangsOnlyFunctions, s);
            }
        }
Example #7
0
        //Compiles a binding defined by pattern matching.
        private void CompileBindingPattern(ElaEquation s, LabelMap map)
        {
            //Compile a right hand expression. Currently it is always compiled, event if right-hand
            //and both left-hand are tuples (however an optimization can be applied in such a case).
            var sys = AddVariable();

            CompileExpression(s.Right, map, Hints.None, s);
            AddLinePragma(s);
            PopVar(sys);

            //Labels needed for pattern compilation
            var next = cw.DefineLabel();
            var exit = cw.DefineLabel();

            //Here we compile a pattern a generate a 'handling logic' that raises a MatchFailed exception
            CompilePattern(sys, s.Left, next, false /*allowBang*/, false /*forceStrict*/);
            cw.Emit(Op.Br, exit);
            cw.MarkLabel(next);
            cw.Emit(Op.Failwith, (Int32)ElaRuntimeError.MatchFailed);
            cw.MarkLabel(exit);
            cw.Emit(Op.Nop);
        }
Example #8
0
        private void ProcessBinding(ElaEquationSet block, ElaEquation bid, ElaExpression left, ElaExpression right)
        {
            bid.Left = left;
            bid.Right = right;

            if (bindings.Peek() == unit)
            {
                block.Equations.Add(bid);
                return;
            }

            var fName = default(String);

            if (right != null && left.Type == ElaNodeType.Juxtaposition && !left.Parens)
            {
                var fc = (ElaJuxtaposition)left;

                if (fc.Target.Type == ElaNodeType.NameReference)
                    fName = fc.Target.GetName();
            }

            if (fName != null)
            {
                var lastB = bindings.Peek();

                if (lastB != null && ((ElaJuxtaposition)lastB.Left).Target.GetName() == fName)
                    lastB.Next = bid;
                else
                    block.Equations.Add(bid);

                bindings.Pop();
                bindings.Push(bid);
            }
            else
                block.Equations.Add(bid);
        }
Example #9
0
        //Compile an instance member. Argument classPrefix (module alias) is null if a class is
        //not prefixed, otherwise it should be used to lookup class members.
        private void CompileInstanceMember(string className, string classPrefix, ElaEquation s, LabelMap map,
                                           string currentTypePrefix, string currentType)
        {
            //Obtain a 'table' function of a class
            var name  = s.GetLeftName();
            var btVar = ObtainClassFunction(classPrefix, className, name, s.Line, s.Column);

            if (btVar.IsEmpty())
            {
                AddError(ElaCompilerError.MemberInvalid, s, name, className);
            }

            var builtin = (btVar.Flags & ElaVariableFlags.Builtin) == ElaVariableFlags.Builtin;
            var args    = 0;

            //Here we need to understand how many arguments a class function has.
            //If our declaration has less arguments (allowed by Ela) we need to do
            //eta expansion.
            if (builtin)
            {
                args = BuiltinParams((ElaBuiltinKind)btVar.Data);
            }
            else
            {
                args = btVar.Data;
            }

            //First we check if this binding is simply a function reference
            if (!TryResolveInstanceBinding(args, s.Right, map))
            {
                //Eta expansion should be done if a member is not a function literal
                //(it can be a partially applied function) or if a number of arguments
                //doesn't match.
                if (!s.IsFunction())
                {
                    EtaExpand(null, s.Right, map, args);
                }
                else if (s.GetArgumentNumber() < args)
                {
                    EtaExpand(null, s, map, args);
                }
                else
                {
                    CompileFunction(s, map);
                }
            }

            AddLinePragma(s);

            //It is possible if we are compiling a default instance (that can be
            //used to automatically generate instances).
            if (currentType != null)
            {
                //Depending whether this is a built-in class or a different approach is
                //used to add a member function.
                if (!builtin)
                {
                    PushVar(btVar);
                }
                else
                {
                    cw.Emit(Op.PushI4, (Int32)btVar.Data);
                }

                //We need to obtain a 'real' global ID of the type that we are extending. This
                //is done using this helper method.
                EmitSpecName(currentTypePrefix, "$$" + currentType, s, ElaCompilerError.UndefinedType);

                //Finally adding a member function.
                cw.Emit(Op.Addmbr);
            }
            else
            {
                var nm = "$default$" + name;

                //We are double binding the same default member which is not allowed within
                //the same module.
                if (globalScope.Locals.ContainsKey(nm))
                {
                    AddError(ElaCompilerError.DefaultMemberAlreadyExist, s, name, className);
                    globalScope.Locals.Remove(name);
                }

                var flags = ElaVariableFlags.None;
                var data  = -1;

                if (s.Right.Type == ElaNodeType.Builtin)
                {
                    flags = ElaVariableFlags.Builtin;
                    data  = (Int32)((ElaBuiltin)s.Right).Kind;
                }

                var dv = AddVariable(nm, s, flags, data);
                PopVar(dv);
            }
        }
Example #10
0
        //**********************************TEMPORARY DISABLED
        //This method checks if a given expression is a regular function definition or a function
        //defined through partial application of another function.
        private bool IsFunction(ElaEquation eq)
        {
            //A simple case - regular function definition
            if (eq.IsFunction())
                return true;

            //This may be a function defined through partial application. Here we only
            //recognize two cases - when the head is an ordinary identifier and if a head is
            //a fully qualified name.
            if (eq.Right != null && eq.Right.Type == ElaNodeType.Juxtaposition)
            {
                var jx = (ElaJuxtaposition)eq.Right;

                //A head is an identifier
                if (jx.Target.Type == ElaNodeType.NameReference)
                {
                    var sv = GetVariable(jx.Target.GetName(), CurrentScope, GetFlags.NoError, 0, 0);

                    //This is a partially applied function, therefore we can "count" this as a function.
                    return IfFunction(eq, sv, jx.Parameters.Count, false);
                }
                else if (jx.Target.Type == ElaNodeType.FieldReference) //This might be a fully qualified name
                {
                    var tr = (ElaFieldReference)jx.Target;

                    //A target can only be a name reference; otherwise we don't see it as a function.
                    if (tr.TargetObject.Type == ElaNodeType.NameReference)
                    {
                        CodeFrame _;
                        var sv = FindByPrefix(tr.TargetObject.GetName(), tr.FieldName, out _);

                        return IfFunction(eq, sv, jx.Parameters.Count, false);
                    }
                }
            }
            else if (eq.Right != null && eq.Right.Type == ElaNodeType.NameReference)
            {
                //This may be a function defined as an alias for another function.
                var sv = GetVariable(eq.Right.GetName(), CurrentScope, GetFlags.NoError, 0, 0);

                //This is an alias, we can "count" this as a function.
                return IfFunction(eq, sv, 0, true);
            }

            return false;
        }
Example #11
0
        //**********************************TEMPORARY DISABLED
        //Here we try to check if a right side is a function reference of a partially applied function.
        //This function might (or might not) set a 'PartiallyApplied' flag. If this flag is set, than a
        //function is eta expanded during compilation. Normally this flag is not needed when functions
        //are defined as simple aliases for other functions.
        private bool IfFunction(ElaEquation eq, ScopeVar sv, int curArgs, bool noPartial)
        {
            //This is a partially applied function, therefore we can "count" this as a function.
            if ((sv.Flags & ElaVariableFlags.Function) == ElaVariableFlags.Function && sv.Data > curArgs)
            {
                if (!noPartial)
                {
                    eq.VariableFlags |= ElaVariableFlags.PartiallyApplied;
                    eq.Arguments = sv.Data - curArgs;
                }

                return true;
            }
            else if ((sv.Flags & ElaVariableFlags.Builtin) == ElaVariableFlags.Builtin)
            {
                //If this a built-in, we need to use another method to determine number of arguments.
                var args = BuiltinParams((ElaBuiltinKind)sv.Data);

                if (args > curArgs)
                {
                    if (!noPartial)
                    {
                        eq.VariableFlags |= ElaVariableFlags.PartiallyApplied;
                        eq.Arguments = args - curArgs;
                    }

                    return true;
                }
            }

            return false;
        }
Example #12
0
        //Main method used to compile functions. Can compile regular named functions,
        //named functions in-place (FunFlag.Inline) and type constructors (FunFlag.Newtype).
        //Return 'true' if a function is clean (no no-inits references).
        private bool CompileFunction(ElaEquation dec, LabelMap oldMap)
        {
            var fc   = (ElaJuxtaposition)dec.Left;
            var pars = fc.Parameters.Count;

            //Target can be null in a case of an anonymous function (lambda)
            var name = fc.Target != null?fc.Target.GetName() : String.Empty;

            var sv = name != null?GetVariable(name, dec.Line, dec.Column) : default(ScopeVar);

            StartFun(name, pars);
            var funSkipLabel = Label.Empty;
            var map          = new LabelMap();

            if ((sv.VariableFlags & ElaVariableFlags.NoWarnings) == ElaVariableFlags.NoWarnings || oldMap.NoWarnings)
            {
                map.NoWarnings = true; //Do not generate warning in this function
            }
            var startLabel = cw.DefineLabel();

            //Functions are always compiled in place, e.g. when met. Therefore a 'goto'
            //instruction is emitted to skip through function definition.
            funSkipLabel = cw.DefineLabel();
            cw.Emit(Op.Br, funSkipLabel);

            //FunStart label is needed for tail recursive calls when we emit a 'goto'
            //instead of an actual function call.
            map.FunStart = startLabel;

            //Preserve some information about a function we're currently in.
            map.FunctionName       = name;
            map.FunctionParameters = pars;
            map.FunctionScope      = CurrentScope;

            cw.MarkLabel(startLabel);

            if (dec.Right == null)
            {
                AddError(ElaCompilerError.InvalidFunctionDeclaration, dec, dec);
                return(false);
            }

            //We start a real (VM based) lexical scope for a function.
            StartScope(true, dec.Right.Line, dec.Right.Column);

            //We add a special 'context' variable; it is not initialized
            AddVariable("context", dec, ElaVariableFlags.Context | ElaVariableFlags.CompilerGenerated, -1);

            //StartSection create a real lexical scope.
            StartSection();

            var hints = Hints.Scope | Hints.Tail;

            AddLinePragma(dec);
            var address = cw.Offset;

            cleans.Push(true);
            CompileFunctionMatch(pars, dec, map, hints);
            var ret = cleans.Pop();

            //This logic creates a function (by finally emitting Newfun).
            var funHandle = frame.Layouts.Count;
            var ss        = EndFun(funHandle);

            frame.Layouts.Add(new MemoryLayout(currentCounter, ss, address));
            EndScope();
            EndSection();

            cw.Emit(Op.Ret);
            cw.MarkLabel(funSkipLabel);

            AddLinePragma(dec);

            //Function is constructed
            cw.Emit(Op.PushI4, pars);
            cw.Emit(Op.Newfun, funHandle);
            return(ret);
        }
Example #13
0
        void Binding(ElaEquationSet block)
        {
            var bid = default(ElaEquation);
            var left = default(ElaExpression);
            var right = default(ElaExpression);

            Expr(out left);
            bid = new ElaEquation(t);
            if (la.kind == 23 || la.kind == 51 || la.kind == 57) {
            if (la.kind == 57) {
                Get();
                Expr(out right);
            } else if (la.kind == 23) {
                Get();
                Guard(out right);
            } else {
                Attribute(ref left);
            }
            }
            if (la.kind == 43) {
            var cb = default(ElaEquationSet);
            WhereBinding(out cb);
            if (left != null && left.Type == ElaNodeType.Header)
               AddError(ElaParserError.InvalidAttributeWhere);

                var letb = new ElaLetBinding();
                   if (cb != null) letb.SetLinePragma(cb.Line, cb.Column);
                   letb.Equations = cb;

                if (right != null)
                {
                    letb.Expression = right;
                       right = letb;
                }
                else
                {
                    letb.Expression = left;
                    left = letb;
                }

            }
            ProcessBinding(block, bid, left, right);
        }
Example #14
0
        private ElaExpression ValidateDoBlock(ElaExpression exp)
        {
            if (exp.Type == ElaNodeType.Juxtaposition && ((ElaJuxtaposition)exp).Parameters[0] == null)
            {
                var ext = ((ElaJuxtaposition)exp).Parameters[1];
                var ctx = default(ElaContext);

                if (ext.Type == ElaNodeType.Context)
                {
                    ctx = (ElaContext)ext;
                    ext = ctx.Expression;
                }

                var eqt = new ElaJuxtaposition { Spec = true };
                eqt.SetLinePragma(exp.Line, exp.Column);
                eqt.Target = new ElaNameReference(t) { Name = ">>=" };
                eqt.Parameters.Add(ext);

                var jux = new ElaJuxtaposition();
                jux.SetLinePragma(exp.Line, exp.Column);
                jux.Target = new ElaNameReference { Name = "point" };
                jux.Parameters.Add(new ElaUnitLiteral());
                eqt.Parameters.Add(new ElaLambda { Left = new ElaPlaceholder(), Right = jux });

                if (ctx != null)
                {
                    ctx.Expression = eqt;
                    exp = ctx;
                }
                else
                    exp = eqt;
            }

            var root = exp;

            while (true)
            {
                if (exp.Type == ElaNodeType.Juxtaposition)
                {
                    var juxta = (ElaJuxtaposition)exp;

                    if (juxta.Parameters.Count == 2)
                    {
                        juxta.Parameters[0] = Reduce(juxta.Parameters[0], juxta);
                        juxta.Parameters[1] = Reduce(juxta.Parameters[1], juxta);
                        exp = juxta.Parameters[1];
                    }
                    else
                        break;
                }
                else if (exp.Type == ElaNodeType.LetBinding)
                {
                    var lb = (ElaLetBinding)exp;
                    lb.Expression = Reduce(lb.Expression, lb);
                    exp = lb.Expression;
                }
                else if (exp.Type == ElaNodeType.Lambda)
                {
                    var lb = (ElaLambda)exp;
                    lb.Right = Reduce(lb.Right, lb);

                    if (lb.Left.Type != ElaNodeType.NameReference &&
                        lb.Left.Type != ElaNodeType.Placeholder)
                    {
                        var em = new ElaMatch();
                        em.SetLinePragma(lb.Left.Line, lb.Left.Column);
                        em.Expression = new ElaNameReference { Name = "$x01" };
                        em.Entries = new ElaEquationSet();

                        var eq1 = new ElaEquation();
                        eq1.SetLinePragma(lb.Left.Line, lb.Left.Column);
                        eq1.Left = lb.Left;
                        eq1.Right = lb.Right;
                        em.Entries.Equations.Add(eq1);

                        var eq2 = new ElaEquation();
                        eq2.SetLinePragma(lb.Left.Line, lb.Left.Column);
                        eq2.Left = new ElaNameReference { Name = "$x02" };
                        var errExp = new ElaJuxtaposition();
                        errExp.SetLinePragma(lb.Left.Line, lb.Left.Column);
                        errExp.Target = new ElaNameReference { Name = "failure" };
                        errExp.Parameters.Add(new ElaNameReference { Name = "$x02" });
                        eq2.Right = errExp;
                        em.Entries.Equations.Add(eq2);

                        lb.Left = new ElaNameReference { Name = "$x01" };
                        lb.Right = em;
                        exp = lb;
                    }
                    else
                        exp = lb.Right;
                }
                else
                    break;
            }

            var ret = new ElaLazyLiteral { Expression = root };
            ret.SetLinePragma(root.Line, root.Column);
            return ret;
        }
Example #15
0
        //Used to compile an anonymous function (lambda). This function returns a number of parameters
        //in compiled lambda.
        private int CompileLambda(ElaEquation bid)
        {
            var fc = new ElaJuxtaposition();

            //Lambda is parsed as a an application of a lambda operator, e.g.
            //expr -> expr, therefore it needs to be transformed in order to be
            //able to use existing match compilation logic.
            if (bid.Left.Type == ElaNodeType.Juxtaposition
                && !bid.Left.Parens) //Parens flag is added if an expression is in parens and
                                    //therefore should be qualified as a pattern.
            {
                var f = (ElaJuxtaposition)bid.Left;
                fc.Parameters.Add(f.Target);
                fc.Parameters.AddRange(f.Parameters);
            }
            else
                fc.Parameters.Add(bid.Left);

            bid.Left = fc;
            var parLen = fc.Parameters.Count;
            StartFun(null, parLen);

            var funSkipLabel = cw.DefineLabel();
            var map = new LabelMap();
            cw.Emit(Op.Br, funSkipLabel);

            //We start a real (VM based) lexical scope for a function.
            StartScope(true, bid.Right.Line, bid.Right.Column);
            StartSection();

            var address = cw.Offset;
            CompileFunctionMatch(parLen, bid, map, Hints.Scope | Hints.Tail);

            var funHandle = frame.Layouts.Count;
            var ss = EndFun(funHandle);
            frame.Layouts.Add(new MemoryLayout(currentCounter, ss, address));
            EndScope();
            EndSection();

            cw.Emit(Op.Ret);
            cw.MarkLabel(funSkipLabel);

            AddLinePragma(bid);

            //Function is constructed
            cw.Emit(Op.PushI4, parLen);
            cw.Emit(Op.Newfun, funHandle);
            return parLen;
        }
Example #16
0
        //Main method used to compile functions. Can compile regular named functions,
        //named functions in-place (FunFlag.Inline) and type constructors (FunFlag.Newtype).
        //Return 'true' if a function is clean (no no-inits references).
        private bool CompileFunction(ElaEquation dec)
        {
            var fc = (ElaJuxtaposition)dec.Left;
            var pars = fc.Parameters.Count;

            //Target can be null in a case of an anonymous function (lambda)
            var name = fc.Target != null ? fc.Target.GetName() : String.Empty;

            StartFun(name, pars);
            var funSkipLabel = Label.Empty;
            var map = new LabelMap();
            var startLabel = cw.DefineLabel();

            //Functions are always compiled in place, e.g. when met. Therefore a 'goto'
            //instruction is emitted to skip through function definition.
            funSkipLabel = cw.DefineLabel();
            cw.Emit(Op.Br, funSkipLabel);

            //FunStart label is needed for tail recursive calls when we emit a 'goto'
            //instead of an actual function call.
            map.FunStart = startLabel;

            //Preserve some information about a function we're currently in.
            map.FunctionName = name;
            map.FunctionParameters = pars;
            map.FunctionScope = CurrentScope;

            cw.MarkLabel(startLabel);

            //We start a real (VM based) lexical scope for a function.
            StartScope(true, dec.Right.Line, dec.Right.Column);

            //We add a special 'context' variable; it is not initialized
            AddVariable("context", dec, ElaVariableFlags.Context, -1);

            //StartSection create a real lexical scope.
            StartSection();

            var hints = Hints.Scope|Hints.Tail;
            AddLinePragma(dec);
            var address = cw.Offset;

            cleans.Push(true);
            CompileFunctionMatch(pars, dec, map, hints);
            var ret = cleans.Pop();

            //This logic creates a function (by finally emitting Newfun).
            var funHandle = frame.Layouts.Count;
            var ss = EndFun(funHandle);
            frame.Layouts.Add(new MemoryLayout(currentCounter, ss, address));
            EndScope();
            EndSection();

            cw.Emit(Op.Ret);
            cw.MarkLabel(funSkipLabel);

            AddLinePragma(dec);

            //Function is constructed
            cw.Emit(Op.PushI4, pars);
            cw.Emit(Op.Newfun, funHandle);
            return ret;
        }
Example #17
0
        //Checks if a given binding is a recursive binding, e.g. contains a reference to itself
        //in the right side.
        private bool IsRecursive(ElaEquation eq)
        {
            //For a simple case we don't need to construct a name list and can do the
            //things much easier.
            if (eq.Left.Type == ElaNodeType.NameReference && !((ElaNameReference)eq.Left).Uppercase)
            {
                var sv = GetVariable(eq.Left.GetName(), CurrentScope, GetFlags.NoError, 0, 0);
                return IsRecursive(eq.Right, sv.Address, null);
            }
            else
            {
                //Complex case, a binding contains a pattern in the left hand side. We then need to
                //extract all names declared in this pattern first, and the build a list of all addresses
                //that can be referenced by right hand side.
                var names = new List<String>();
                ExtractPatternNames(eq.Left, names);
                var arr = new int[names.Count];

                for (var i = 0; i < names.Count; i++)
                    arr[i] = GetVariable(names[i], CurrentScope, GetFlags.NoError, 0, 0).Address;

                if (IsRecursive(eq.Right, -1, arr))
                    return true;

                return false;
            }
        }
Example #18
0
        //Compile all declarations including function bindings, name bindings and bindings
        //defined by pattern matching.
        private void CompileDeclaration(ElaEquation s, LabelMap map, Hints hints)
        {
            //Check for some errors
            ValidateBinding(s);
            var partial = (s.VariableFlags & ElaVariableFlags.PartiallyApplied) == ElaVariableFlags.PartiallyApplied;
            var fun     = partial || s.IsFunction();

            if ((s.Left.Type != ElaNodeType.NameReference || ((ElaNameReference)s.Left).Uppercase) && !fun)
            {
                if ((hints & Hints.Lazy) == Hints.Lazy)
                {
                    CompileLazyBindingPattern(s, map);
                }
                else
                {
                    CompileBindingPattern(s, map);
                }
            }
            else
            {
                var nm   = default(String);
                var sv   = GetNoInitVariable(s, out nm);
                var addr = sv.Address;

                if (sv.IsEmpty())
                {
                    addr = AddVariable(s.Left.GetName(), s, s.VariableFlags, -1);
                }
                else
                {
                    CurrentScope.AddFlags(nm, ElaVariableFlags.Self);
                }

                //Compile expression and write it to a variable
                if (fun)
                {
                    //*****CURRENTLY NOT USED!
                    //Here we automatically eta expand functions defined through partial application
                    if (partial)
                    {
                        EtaExpand(s.Left.GetName(), s.Right, map, s.Arguments);
                    }
                    else if (CompileFunction(s, map))
                    {
                        CurrentScope.AddFlags(nm, ElaVariableFlags.Clean); //A function is clean
                    }
                }
                else
                {
                    map.BindingName = s.Left.GetName();

                    if ((hints & Hints.Lazy) == Hints.Lazy)
                    {
                        CompileLazyExpression(s.Right, map, Hints.None);
                    }
                    else
                    {
                        var ed = CompileExpression(s.Right, map, Hints.None, s);

                        if (ed.Type == DataKind.Builtin)
                        {
                            CurrentScope.AddFlagsAndData(nm, ElaVariableFlags.Builtin, ed.Data);
                        }
                    }
                }

                //Now, when done initialization, when can remove NoInit flags.
                if (!sv.IsEmpty())
                {
                    CurrentScope.RemoveFlags(nm, ElaVariableFlags.NoInit | ElaVariableFlags.Self);
                }

                AddLinePragma(s);
                PopVar(addr);
            }
        }
Example #19
0
        //Compile an instance member. Argument classPrefix (module alias) is null if a class is
        //not prefixed, otherwise it should be used to lookup class members.
        private void CompileInstanceMember(string className, string classPrefix, ElaEquation s, LabelMap map, 
            string currentTypePrefix, string currentType)
        {
            //Obtain a 'table' function of a class
            var name = s.GetLeftName();
            var btVar = ObtainClassFunction(classPrefix, className, name, s.Line, s.Column);

            if (btVar.IsEmpty())
                AddError(ElaCompilerError.MemberInvalid, s, name, className);

            var builtin = (btVar.Flags & ElaVariableFlags.Builtin) == ElaVariableFlags.Builtin;
            var args = 0;

            //Here we need to understand how many arguments a class function has.
            //If our declaration has less arguments (allowed by Ela) we need to do
            //eta expansion.
            if (builtin)
                args = BuiltinParams((ElaBuiltinKind)btVar.Data);
            else
                args = btVar.Data;

            //First we check if this binding is simply a function reference
            if (!TryResolveInstanceBinding(args, s.Right, map))
            {
                //Eta expansion should be done if a member is not a function literal
                //(it can be a partially applied function) or if a number of arguments
                //doesn't match.
                if (!s.IsFunction())
                    EtaExpand(null, s.Right, map, args);
                else if (s.GetArgumentNumber() < args)
                    EtaExpand(null, s, map, args);
                else
                    CompileFunction(s);
            }

            AddLinePragma(s);

            //It is possible if we are compiling a default instance (that can be
            //used to automatically generate instances).
            if (currentType != null)
            {
                //Depending whether this is a built-in class or a different approach is
                //used to add a member function.
                if (!builtin)
                    PushVar(btVar);
                else
                    cw.Emit(Op.PushI4, (Int32)btVar.Data);

                //We need to obtain a 'real' global ID of the type that we are extending. This
                //is done using this helper method.
                EmitSpecName(currentTypePrefix, "$$" + currentType, s, ElaCompilerError.UndefinedType);

                //Finally adding a member function.
                cw.Emit(Op.Addmbr);
            }
            else
            {
                var nm = "$default$" + name;

                //We are double binding the same default member which is not allowed within
                //the same module.
                if (globalScope.Locals.ContainsKey(nm))
                {
                    AddError(ElaCompilerError.DefaultMemberAlreadyExist, s, name, className);
                    globalScope.Locals.Remove(name);
                }

                var flags = ElaVariableFlags.None;
                var data = -1;

                if (s.Right.Type == ElaNodeType.Builtin)
                {
                    flags = ElaVariableFlags.Builtin;
                    data = (Int32)((ElaBuiltin)s.Right).Kind;
                }

                var dv = AddVariable(nm, s, flags, data);
                PopVar(dv);
            }
        }