//Compiles built-in as function in place. It is compiled in such a manner each time
        //it is referenced. But normally its body is just one or two op codes, so this is not a problem.
        private void CompileBuiltin(ElaBuiltinKind kind, ElaExpression exp, LabelMap map, string name)
        {
            StartSection();

            //Here we determine the number of parameters based on the function kind.
            var pars = BuiltinParams(kind);
            cw.StartFrame(pars);
            var funSkipLabel = cw.DefineLabel();
            cw.Emit(Op.Br, funSkipLabel);
            var address = cw.Offset;
            pdb.StartFunction(name, address, pars);

            AddLinePragma(exp);
            //Gets the actual typeId for built-in
            CompileBuiltinInline(kind, exp, map, Hints.None);

            cw.Emit(Op.Ret);
            frame.Layouts.Add(new MemoryLayout(currentCounter, cw.FinishFrame(), address));
            EndSection();
            pdb.EndFunction(frame.Layouts.Count - 1, cw.Offset);

            cw.MarkLabel(funSkipLabel);
            cw.Emit(Op.PushI4, pars);
            cw.Emit(Op.Newfun, frame.Layouts.Count - 1);
        }
示例#2
0
 //This step compiles instances - this should be done after types (as soon as instances
 //reference types) and after the first step as well (as soon as instances reference type classes
 //and can reference any other local and non-local names). It is important however to compile
 //instances before any user typeId gets executed because they effectively mutate function tables.
 private void ProcessInstances(ElaProgram prog, LabelMap map)
 {
     if (prog.Instances != null)
     {
         CompileInstances(prog.Instances, map);
     }
 }
示例#3
0
        //An entry method for includes compilation
        private void CompileModuleIncludes(ElaProgram prog, LabelMap map)
        {
            var inc = prog.Includes;

            for (var i = 0; i < inc.Count; i++)
                CompileModuleInclude(inc[i], map);
        }
示例#4
0
        //Performs validation of overlapping for complex case of pattern matchine
        //(such as when pattern matching is done in a function definition).
        private void ValidateOverlapComplex(LabelMap map, IEnumerable <ElaEquation> seq)
        {
            var lst = new List <ElaJuxtaposition>();

            foreach (var ejx in seq)
            {
                var jx = (ElaJuxtaposition)ejx.Left;

                foreach (var ojx in lst)
                {
                    var can = false;

                    for (var i = 0; i < ojx.Parameters.Count; i++)
                    {
                        if (i < jx.Parameters.Count && jx.Parameters[i].CanFollow(ojx.Parameters[i]))
                        {
                            can = true;
                            break;
                        }
                    }

                    if (!can)
                    {
                        AddWarning(map, ElaCompilerWarning.MatchEntryNotReachable, jx, FormatNode(jx), FormatNode(ojx));
                    }
                }

                lst.Add(jx);
            }
        }
示例#5
0
        //Used to compile a 'try' expression.
        private void CompileTryExpression(ElaTry n, LabelMap map, Hints hints)
        {
            var catchLab = cw.DefineLabel();
            var exitLab  = cw.DefineLabel();

            //Generate a start of a 'try' section
            AddLinePragma(n);
            cw.Emit(Op.Start, catchLab);

            CompileExpression(n.Expression, map, Hints.None, n);

            //Leaving 'try' section
            cw.Emit(Op.Leave);
            cw.Emit(Op.Br, exitLab);
            cw.MarkLabel(catchLab);
            cw.Emit(Op.Leave);

            //Throw hint is to tell match compiler to generate a different typeId if
            //all pattern fail - to rethrow an original error instead of generating a
            //new MatchFailed error.
            CompileSimpleMatch(n.Entries.Equations, map, hints | Hints.Throw, null);

            cw.MarkLabel(exitLab);
            cw.Emit(Op.Nop);

            if ((hints & Hints.Left) == Hints.Left)
            {
                AddValueNotUsed(map, n);
            }
        }
示例#6
0
        //Now we can compile global user defined functions and lazy sections. This is
        //user typeId however it is not executed when bindings are done therefore we wouldn't need to enforce
        //laziness here. TopLevel done through pattern matching are rejected on this stage.
        private FastList <ElaEquation> ProcessFunctions(FastList <ElaEquation> exps, LabelMap map)
        {
            var len  = exps.Count;
            var list = new FastList <ElaEquation>(len);

            for (var i = 0; i < len; i++)
            {
                var b = exps[i];

                if (b.Right != null)
                {
                    //We need to ensure that this is a global binding that it is not defined by pattern matching
                    if (b.IsFunction() || (b.Right.Type == ElaNodeType.LazyLiteral &&
                                           (b.Left.Type == ElaNodeType.NameReference || b.Left.Type == ElaNodeType.LazyLiteral)))
                    {
                        CompileDeclaration(b, map, Hints.None);
                    }
                    else
                    {
                        list.Add(b);
                    }
                }
                else
                {
                    list.Add(b);
                }
            }

            return(list);
        }
示例#7
0
        //Compile conditional if-then-else operator
        private void CompileConditionalOperator(ElaCondition s, LabelMap map, Hints hints)
        {
            AddLinePragma(s);
            CompileExpression(s.Condition, map, Hints.Scope, s);
            var falseLab = cw.DefineLabel();
            cw.Emit(Op.Brfalse, falseLab);

            //Both the True and False parts may be the tail expressions
            //Also this whole operator can be used as a statement. Or can be compiled
            //in a situation when some of the referenced names are not initialized (Lazy)
            var left = (hints & Hints.Left) == Hints.Left ? Hints.Left : Hints.None;
            var tail = (hints & Hints.Tail) == Hints.Tail ? Hints.Tail : Hints.None;

            if (s.True != null)
                CompileExpression(s.True, map, left | tail | Hints.Scope, s);

            if (s.False != null)
            {
                var skipLabel = cw.DefineLabel();
                cw.Emit(Op.Br, skipLabel);
                cw.MarkLabel(falseLab);
                CompileExpression(s.False, map, left | tail | Hints.Scope, s);
                cw.MarkLabel(skipLabel);
                cw.Emit(Op.Nop);
            }
            else
            {
                AddError(ElaCompilerError.ElseMissing, s.True);
                AddHint(ElaCompilerHint.AddElse, s.True);
            }
        }
示例#8
0
        //Inlines a constructor call, method is called from TryOptimizeConstructor
        private void CompileConstructorCall(string prefix, string name, ElaJuxtaposition juxta, LabelMap map, ScopeVar sv, ScopeVar sv2)
        {
            var len = juxta.Parameters.Count;

            //For optimization purposes we use a simplified creation algorythm for constructors
            //with 1 and 2 parameters
            if (len == 1)
            {
                CompileExpression(juxta.Parameters[0], map, Hints.None, juxta);
                TypeCheckIf(prefix, name, 0);
            }
            else if (len == 2)
            {
                CompileExpression(juxta.Parameters[0], map, Hints.None, juxta);
                TypeCheckIf(prefix, name, 0);

                CompileExpression(juxta.Parameters[1], map, Hints.None, juxta);
                TypeCheckIf(prefix, name, 1);
            }
            else
                CompileConstructorParameters(prefix, name, juxta, map);

            PushVar(sv);
            PushVar(sv2);

            if (len == 1)
                cw.Emit(Op.Newtype1);
            else if (len == 2)
                cw.Emit(Op.Newtype2);
            else
                cw.Emit(Op.Newtype);
        }
示例#9
0
        //Compiles Ela 'is' expression.
        private void CompileTypeCheck(ElaTypeCheck n, LabelMap map, Hints hints)
        {
            var failLab = cw.DefineLabel();
            var endLab = cw.DefineLabel();

            var sysVar = AddVariable();
            CompileExpression(n.Expression, map, Hints.None, n);
            PopVar(sysVar);

            //Here we are checking all classes specified in a pattern. We have to loop
            //through all classes and generate a check instruction (Traitch) for each.
            for (var i = 0; i < n.Traits.Count; i++)
            {
                var t = n.Traits[i];
                PushVar(sysVar);
                CheckTypeOrClass(t.Prefix, t.Name, failLab, n);
            }

            cw.Emit(Op.PushI1_1);
            cw.Emit(Op.Br, endLab);
            cw.MarkLabel(failLab);
            cw.Emit(Op.PushI1_0);
            cw.MarkLabel(endLab);
            cw.Emit(Op.Nop);

            if ((hints & Hints.Left) == Hints.Left)
                AddValueNotUsed(n);
        }
示例#10
0
 private void CompileLazyList(ElaGenerator s, LabelMap map, Hints hints)
 {
     var fun = CompileRecursiveFor(s, map, hints, -1, -1);
     CompileExpression(s.Target, map, Hints.None, s);
     PushVar(fun);
     cw.Emit(Op.Call);
 }
示例#11
0
        //Compiles Ela 'is' expression.
        private void CompileTypeCheck(ElaTypeCheck n, LabelMap map, Hints hints)
        {
            var failLab = cw.DefineLabel();
            var endLab  = cw.DefineLabel();

            var sysVar = AddVariable();

            CompileExpression(n.Expression, map, Hints.None, n);
            PopVar(sysVar);

            //Here we are checking all classes specified in a pattern. We have to loop
            //through all classes and generate a check instruction (Traitch) for each.
            for (var i = 0; i < n.Traits.Count; i++)
            {
                var t = n.Traits[i];
                PushVar(sysVar);
                CheckTypeOrClass(t.Prefix, t.Name, failLab, n);
            }

            cw.Emit(Op.PushI1_1);
            cw.Emit(Op.Br, endLab);
            cw.MarkLabel(failLab);
            cw.Emit(Op.PushI1_0);
            cw.MarkLabel(endLab);
            cw.Emit(Op.Nop);

            if ((hints & Hints.Left) == Hints.Left)
            {
                AddValueNotUsed(map, n);
            }
        }
示例#12
0
        //Compile conditional if-then-else operator
        private void CompileConditionalOperator(ElaCondition s, LabelMap map, Hints hints)
        {
            AddLinePragma(s);
            CompileExpression(s.Condition, map, Hints.Scope, s);
            var falseLab = cw.DefineLabel();

            cw.Emit(Op.Brfalse, falseLab);

            //Both the True and False parts may be the tail expressions
            //Also this whole operator can be used as a statement. Or can be compiled
            //in a situation when some of the referenced names are not initialized (Lazy)
            var left = (hints & Hints.Left) == Hints.Left ? Hints.Left : Hints.None;
            var tail = (hints & Hints.Tail) == Hints.Tail ? Hints.Tail : Hints.None;

            if (s.True != null)
            {
                CompileExpression(s.True, map, left | tail | Hints.Scope, s);
            }

            if (s.False != null)
            {
                var skipLabel = cw.DefineLabel();
                cw.Emit(Op.Br, skipLabel);
                cw.MarkLabel(falseLab);
                CompileExpression(s.False, map, left | tail | Hints.Scope, s);
                cw.MarkLabel(skipLabel);
                cw.Emit(Op.Nop);
            }
            else
            {
                AddError(ElaCompilerError.ElseMissing, s.True);
                AddHint(ElaCompilerHint.AddElse, s.True);
            }
        }
示例#13
0
        //An entry method for instance compilation
        private void CompileInstances(ElaClassInstance s, LabelMap map)
        {
            var ins = s;

            //First we need to compile default instances so that
            //other instances in this class will be able to use them
            while (ins != null)
            {
                if (ins.TypeName == null)
                {
                    CompileInstanceBody(ins, map);
                }

                ins = ins.And;
            }

            ins = s;

            //Now we compile specific instances
            while (ins != null)
            {
                if (ins.TypeName != null)
                {
                    CompileInstanceBody(ins, map);
                }

                ins = ins.And;
            }
        }
示例#14
0
        //Compiles built-in as function in place. It is compiled in such a manner each time
        //it is referenced. But normally its body is just one or two op codes, so this is not a problem.
        private void CompileBuiltin(ElaBuiltinKind kind, ElaExpression exp, LabelMap map, string name)
        {
            StartSection();

            //Here we determine the number of parameters based on the function kind.
            var pars = BuiltinParams(kind);

            cw.StartFrame(pars);
            var funSkipLabel = cw.DefineLabel();

            cw.Emit(Op.Br, funSkipLabel);
            var address = cw.Offset;

            pdb.StartFunction(name, address, pars);

            AddLinePragma(exp);
            //Gets the actual typeId for built-in
            CompileBuiltinInline(kind, exp, map, Hints.None);

            cw.Emit(Op.Ret);
            frame.Layouts.Add(new MemoryLayout(currentCounter, cw.FinishFrame(), address));
            EndSection();
            pdb.EndFunction(frame.Layouts.Count - 1, cw.Offset);

            cw.MarkLabel(funSkipLabel);
            cw.Emit(Op.PushI4, pars);
            cw.Emit(Op.Newfun, frame.Layouts.Count - 1);
        }
示例#15
0
        //Compiles an 'open' module directive.
        private void CompileModuleInclude(ElaModuleInclude s, LabelMap map)
        {
            var modFrame = IncludeModule(
                s.Alias,             //alias
                s.Name,              //name
                s.DllName,           //dllname
                s.Path.ToArray(),    //path
                s.Line,              //line
                s.Column,            //col
                s.RequireQualified,  //qualified
                s,                   //ElaExpression
                true,                //add variable
                false);              //NoPrelude

            //Process explicit import list for a module if module is valid and has an import list
            if (s.HasImportList && modFrame != null)
            {
                var il = s.ImportList;

                for (var i = 0; i < il.Count; i++)
                {
                    var im = il[i];
                    var a = AddVariable(im.LocalName, im, im.Private ? ElaVariableFlags.Private : ElaVariableFlags.None, -1);
                    var sv = default(ScopeVar);

                    //Query a name from an import list directly in a module
                    if (!modFrame.GlobalScope.Locals.TryGetValue(im.Name, out sv))
                        AddError(ElaCompilerError.UndefinedNameInModule, im, s.Alias, im.Name);

                    AddLinePragma(im);
                    cw.Emit(Op.Pushext, (frame.HandleMap.Count - 1) | sv.Address << 8);
                    PopVar(a);
                }
            }
        }
示例#16
0
        private void CompileLazyList(ElaGenerator s, LabelMap map, Hints hints)
        {
            var fun = CompileRecursiveFor(s, map, hints, -1, -1);

            CompileExpression(s.Target, map, Hints.None, s);
            PushVar(fun);
            cw.Emit(Op.Call);
        }
示例#17
0
 //An entry method for type compilation. Ensures that types ('type') are
 //always compiled before type extensions ('data').
 private void CompileTypes(ElaNewtype v, LabelMap map)
 {
     CompileHeaders(v);
     CompileTypeHeaderOnly(v, map);
     CompileDataHeaderOnly(v, map);
     CompileTypeBodyOnly(v, map);
     CompileDataBodyOnly(v, map);
 }
示例#18
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);
        }
示例#19
0
        //An entry method for includes compilation
        private void CompileModuleIncludes(ElaProgram prog, LabelMap map)
        {
            var inc = prog.Includes;

            for (var i = 0; i < inc.Count; i++)
            {
                CompileModuleInclude(inc[i], map);
            }
        }
示例#20
0
        //Compiles xs literal
        private ExprData CompileTuple(ElaTupleLiteral v, LabelMap map, Hints hints)
        {
            CompileTupleParameters(v, v.Parameters, map);

            if ((hints & Hints.Left) == Hints.Left)
                AddValueNotUsed(v);

            return new ExprData(DataKind.VarType, (Int32)ElaTypeCode.Tuple);
        }
示例#21
0
        private void CompileGenerator(ElaGenerator s, LabelMap map, Hints hints)
        {
            StartScope(false, s.Line, s.Column);
            var iter = cw.DefineLabel();
            var breakExit = cw.DefineLabel();
            var newMap = new LabelMap(map);

            var addr = -1;

            if (s.Pattern.Type == ElaNodeType.NameReference)
                addr = AddVariable(s.Pattern.GetName(), s.Pattern, ElaVariableFlags.None, -1);
            else
                addr = AddVariable();

            var serv = AddVariable();
            CompileExpression(s.Target, map, Hints.None, s);
            cw.Emit(Op.Dup);
            PopVar(serv);
            cw.Emit(Op.Isnil);
            cw.Emit(Op.Brtrue, breakExit);

            cw.MarkLabel(iter);
            PushVar(serv);

            cw.Emit(Op.Isnil);
            cw.Emit(Op.Brtrue, breakExit);
            PushVar(serv);
            cw.Emit(Op.Head);
            PopVar(addr);
            PushVar(serv);
            cw.Emit(Op.Tail);
            PopVar(0 | ((addr >> 8) + 1) << 8);

            if (s.Pattern.Type != ElaNodeType.NameReference)
                CompilePattern(addr, s.Pattern, iter, false /*allowBang*/, false /*forceStrict*/);

            if (s.Guard != null)
            {
                CompileExpression(s.Guard, map, Hints.None, s);
                cw.Emit(Op.Brfalse, iter);
            }

            if (s.Body != null)
            {
                CompileExpression(s.Body, newMap, Hints.Scope, s);

                if (s.Body.Type != ElaNodeType.Generator)
                    cw.Emit(Op.Cons);
            }

            cw.Emit(Op.Br, iter);
            cw.MarkLabel(breakExit);
            EndScope();

            cw.Emit(Op.Nop);
        }
示例#22
0
        //Main compilation method that runs compilation in seven steps by rewriting binding order.
        private void CompileProgram(ElaProgram prog, LabelMap map)
        {
            ProcessIncludesClassesTypes(prog, map);
            var list = RunForwardDeclaration(prog.TopLevel.Equations, map);

            list = ProcessFunctions(list, map);
            ProcessInstances(prog, map);
            list = ProcessBindings(list, map);
            ProcessExpressions(list, map);
        }
示例#23
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);
        }
示例#24
0
        //Compiles xs literal
        private ExprData CompileTuple(ElaTupleLiteral v, LabelMap map, Hints hints)
        {
            CompileTupleParameters(v, v.Parameters, map);

            if ((hints & Hints.Left) == Hints.Left)
            {
                AddValueNotUsed(map, v);
            }

            return(new ExprData(DataKind.VarType, (Int32)ElaTypeCode.Tuple));
        }
示例#25
0
        //Used to compile a 'match' expression.
        private void CompileMatchExpression(ElaMatch n, LabelMap map, Hints hints)
        {
            CompileExpression(n.Expression, map, Hints.None, n);
            AddLinePragma(n);
            CompileSimpleMatch(n.Entries.Equations, map, hints, n.Expression);

            if ((hints & Hints.Left) == Hints.Left)
            {
                AddValueNotUsed(map, n);
            }
        }
示例#26
0
        //Compiles constructor parameters. This method is called from CompileContructorCall, it is used
        //when inlining constructor and when constructor has >2 arguments.
        private void CompileConstructorParameters(string prefix, string name, ElaJuxtaposition juxta, LabelMap map)
        {
            var pars = juxta.Parameters;
            cw.Emit(Op.Newtup, pars.Count);

            for (var i = 0; i < pars.Count; i++)
            {
                CompileExpression(pars[i], map, Hints.None, juxta);
                TypeCheckIf(prefix, name, i);
                cw.Emit(Op.Tupcons, i);
            }
        }
示例#27
0
        //Compiles any given expression as lazy, can be used to automatically generate thunks
        //as well as to compile an explicit lazy literal.
        private ExprData CompileLazyExpression(ElaExpression exp, LabelMap map, Hints hints)
        {
            Label funSkipLabel;
            int address;
            LabelMap newMap;

            CompileFunctionProlog(null, 1, exp.Line, exp.Column, out funSkipLabel, out address, out newMap);
            var ed = CompileExpression(exp, newMap, Hints.Scope | Hints.FunBody, null);
            CompileFunctionEpilog(null, 1, address, funSkipLabel);
            cw.Emit(Op.Newlazy);

            return ed;
        }
示例#28
0
 private void CompileTypeBody(ElaNewtype v, LabelMap map)
 {
     if (v.HasBody)
     {
         for (var i = 0; i < v.Constructors.Count; i++)
         {
             var c  = v.Constructors[i];
             var cf = v.ConstructorFlags[i];
             cf = cf == ElaVariableFlags.None ? v.Flags : cf | v.Flags;
             CompileConstructor(v.Name, v.ScopeVar, c, cf, v.TypeModuleId);
         }
     }
 }
示例#29
0
        //This method checks if an instance member binding is a function reference with a
        //correct number of arguments.
        private bool TryResolveInstanceBinding(int args, ElaExpression exp, LabelMap map)
        {
            if (exp == null)
            {
                return(false);
            }

            //This is not a function at all, but a polymorphic constant. We, however, cannot
            //execute it right away - as a result we need to defer its execution by creating
            //a thunk. Code should not be executing when instances are run.
            if (args == 0)
            {
                CompileLazyExpression(exp, map, Hints.None);
                return(true);
            }

            //A simple case - a direct name reference, need to check its type arguments
            if (exp.Type == ElaNodeType.NameReference)
            {
                var sv = GetVariable(exp.GetName(), CurrentScope, GetFlags.NoError, 0, 0);

                if ((sv.VariableFlags & ElaVariableFlags.Function) == ElaVariableFlags.Function && sv.Data == args)
                {
                    AddLinePragma(exp);
                    PushVar(sv);
                    return(true);
                }
            }
            else if (exp.Type == ElaNodeType.FieldReference)
            {
                //A more complex case - this can be a qualified name (with a module alias)
                var fr = (ElaFieldReference)exp;

                if (fr.TargetObject.Type != ElaNodeType.NameReference)
                {
                    return(false);
                }

                CodeFrame _;
                var       sv = FindByPrefix(fr.TargetObject.GetName(), fr.FieldName, out _);

                if ((sv.VariableFlags & ElaVariableFlags.Function) == ElaVariableFlags.Function && sv.Data == args)
                {
                    AddLinePragma(exp);
                    PushVar(sv);
                    return(true);
                }
            }

            return(false);
        }
示例#30
0
        //This is an old typeId that was used to optimize small ranges (to generate them in-place).
        //It is currently not used, but in future it may be rejuvenated.
        private bool TryOptimizeRange(ElaRange range, LabelMap map, Hints hints)
        {
            if (range.First.Type != ElaNodeType.Primitive ||
                (range.Second != null && range.Second.Type != ElaNodeType.Primitive) ||
                range.Last.Type != ElaNodeType.Primitive)
                return false;

            var fst = (ElaPrimitive)range.First;
            var snd = range.Second != null ? (ElaPrimitive)range.Second : null;
            var lst = (ElaPrimitive)range.Last;

            if (fst.Value.LiteralType != ElaTypeCode.Integer ||
                (snd != null && snd.Value.LiteralType != ElaTypeCode.Integer) ||
                lst.Value.LiteralType != ElaTypeCode.Integer)
                return false;

            var fstVal = fst.Value.AsInteger();
            var sndVal = snd != null ? snd.Value.AsInteger() : fstVal + 1;
            var lstVal = lst.Value.AsInteger();
            var step = sndVal - fstVal;

            if (Math.Abs((fstVal - lstVal) / step) > 20)
                return false;

            cw.Emit(Op.Newlist);

            if (snd != null)
            {
                cw.Emit(Op.PushI4, fstVal);
                fstVal = sndVal;
                cw.Emit(Op.Cons);
            }

            for (;;)
            {
                cw.Emit(Op.PushI4, fstVal);
                cw.Emit(Op.Cons);
                fstVal += step;

                if (step > 0)
                {
                    if (fstVal > lstVal)
                        break;
                }
                else if (fstVal < lstVal)
                    break;
            }

            cw.Emit(Op.Genfin);
            return true;
        }
示例#31
0
        //Type class declarations, modules includes and type declarations can be compiled in the first place.
        private void ProcessIncludesClassesTypes(ElaProgram prog, LabelMap map)
        {
            CompileModuleIncludes(prog, map);

            if (prog.Classes != null)
            {
                CompileClass(prog.Classes, map);
            }

            if (prog.Types != null)
            {
                CompileTypes(prog.Types, map);
            }
        }
示例#32
0
        //An entry method for range generation. Implementation of ranges are not provided by a compiler,
        //instead a particular data type implement ranges by providing an instance of Enum class. Functions
        //succ, enumFrom and enumFromTo are explicitly invoked by this method.
        private void CompileRange(ElaExpression parent, ElaRange range, LabelMap map, Hints hints)
        {
            AddLinePragma(range);

            var snd = AddVariable();
            var fst = AddVariable();

            //Compile the first element which should always be present.
            CompileExpression(range.First, map, Hints.None, range);
            PopVar(fst);

            //If the second element is missing we will calculate it using 'succ'.
            if (range.Second == null)
            {
                var sv = GetFunction("succ", range);
                PushVar(fst);
                PushVar(sv);
                cw.Emit(Op.Call);
                PopVar(snd);
            }
            else
            {
                CompileExpression(range.Second, map, Hints.None, range);
                PopVar(snd);
            }

            //If a last element is missing we need to generate an infinite range
            //using 'enumFrom' function.
            if (range.Last == null)
            {
                var sv = GetFunction("enumFrom", range);
                PushVar(snd);
                PushVar(fst);
                PushVar(sv);
                cw.Emit(Op.Call);
                cw.Emit(Op.Call);
            }
            else
            {
                //An ordinary strict range.
                var sv = GetFunction("enumFromTo", range);
                PushVar(snd);
                PushVar(fst);
                CompileExpression(range.Last, map, Hints.None, range);
                PushVar(sv);
                cw.Emit(Op.Call);
                cw.Emit(Op.Call);
                cw.Emit(Op.Call);
            }
        }
示例#33
0
        //An entry method for range generation. Implementation of ranges are not provided by a compiler,
        //instead a particular data type implement ranges by providing an instance of Enum class. Functions
        //succ, enumFrom and enumFromTo are explicitly invoked by this method.
        private void CompileRange(ElaExpression parent, ElaRange range, LabelMap map, Hints hints)
        {
            AddLinePragma(range);

            var snd = AddVariable();
            var fst = AddVariable();

            //Compile the first element which should always be present.
            CompileExpression(range.First, map, Hints.None, range);
            PopVar(fst);

            //If the second element is missing we will calculate it using 'succ'.
            if (range.Second == null)
            {
                var sv = GetFunction("succ", range);
                PushVar(fst);
                PushVar(sv);
                cw.Emit(Op.Call);
                PopVar(snd);
            }
            else
            {
                CompileExpression(range.Second, map, Hints.None, range);
                PopVar(snd);
            }

            //If a last element is missing we need to generate an infinite range
            //using 'enumFrom' function.
            if (range.Last == null)
            {
                var sv = GetFunction("enumFrom", range);
                PushVar(snd);
                PushVar(fst);
                PushVar(sv);
                cw.Emit(Op.Call);
                cw.Emit(Op.Call);
            }
            else
            {
                //An ordinary strict range.
                var sv = GetFunction("enumFromTo", range);
                PushVar(snd);
                PushVar(fst);
                CompileExpression(range.Last, map, Hints.None, range);
                PushVar(sv);
                cw.Emit(Op.Call);
                cw.Emit(Op.Call);
                cw.Emit(Op.Call);
            }
        }
示例#34
0
        //This method only compiles types declared through 'type' keyword
        //and not type extensions ('data' declarations).
        private void CompileTypeBodyOnly(ElaNewtype nt, LabelMap map)
        {
            var v = nt;

            while (v != null)
            {
                if (!v.Extends && !v.Header)
                {
                    CompileTypeBody(v, map);
                }

                v = v.And;
            }
        }
示例#35
0
        //Validates a built-in class definition and compile a built-in member
        private void CompileBuiltinMember(ElaBuiltinKind kind, ElaTypeClass s, int memIndex, LabelMap map)
        {
            var flags = ElaVariableFlags.Builtin | ElaVariableFlags.ClassFun;

            if (memIndex > s.Members.Count - 1)
            {
                AddError(ElaCompilerError.InvalidBuiltinClassDefinition, s, s.BuiltinName);
                return;
            }

            var m = s.Members[memIndex];
            CompileBuiltin(kind, m, map, m.Name);
            AddLinePragma(m);
            PopVar(AddVariable(m.Name, m, flags, (Int32)kind));
        }
示例#36
0
        //Compiles a primitive literal value
        private ExprData CompilePrimitive(ElaPrimitive p, LabelMap map, Hints hints)
        {
            AddLinePragma(p);
            PushPrimitive(p.Value);

            if ((hints & Hints.Left) == Hints.Left)
                AddValueNotUsed(p);

            if (p.Parens && p.Value.IsNegative())
            {
                AddWarning(ElaCompilerWarning.SectionAmbiguity, p, p.Value.ToString());
                AddHint(ElaCompilerHint.AddSpaceSection, p, p.Value.ToString().TrimStart('-'));
            }

            return new ExprData(DataKind.VarType, (Int32)p.Value.LiteralType);
        }
示例#37
0
 //Warnings can be ignored or generated as errors
 private void AddWarning(LabelMap map, ElaCompilerWarning warning, int line, int col, params object[] args)
 {
     if (map.NoWarnings)
     {
         return;
     }
     else if (options.WarningsAsErrors)
     {
         AddError((ElaCompilerError)warning, line, col, args);
     }
     else if (!options.NoWarnings)
     {
         Errors.Add(new ElaMessage(Strings.GetWarning(warning, args), MessageType.Warning,
                                   (Int32)warning, line, col));
     }
 }
示例#38
0
        //Compiles a provided set of equations. This method is to be used for local
        //scopes only.
        private void CompileEquationSet(ElaEquationSet set, LabelMap map)
        {
            var list = RunForwardDeclaration(set.Equations, map);

            list = ProcessFunctions(list, map);
            list = ProcessBindings(list, map);

            //Expressions are not allowed in this context
            if (list.Count > 0)
            {
                for (var i = 0; i < list.Count; i++)
                {
                    AddError(ElaCompilerError.InvalidExpression, list[i], FormatNode(list[i]));
                }
            }
        }
示例#39
0
        //Performs validation of overlapping for simple case of pattern matching
        //(such as 'match' expression with a single pattern per entry).
        private void ValidateOverlapSimple(LabelMap map, IEnumerable <ElaEquation> seq, ElaExpression mexp)
        {
            var lst = new List <ElaExpression>();

            foreach (var e in seq)
            {
                foreach (var o in lst)
                {
                    if (!e.Left.CanFollow(o))
                    {
                        AddWarning(map, ElaCompilerWarning.MatchEntryNotReachable, e.Left, FormatNode(e.Left), FormatNode(o));
                    }
                }

                lst.Add(e.Left);
            }
        }
示例#40
0
        //Perform an eta expansion for a given expression
        private void EtaExpand(string name, ElaExpression exp, LabelMap map, int args)
        {
            //Here we generate a function which has a provided number of
            //arguments
            if (name != null)
            {
                StartFun(name, args);
            }

            StartSection();
            StartScope(true, exp.Line, exp.Column);
            cw.StartFrame(args);
            var funSkipLabel = cw.DefineLabel();

            cw.Emit(Op.Br, funSkipLabel);
            var address = cw.Offset;

            if (exp.Type != ElaNodeType.Equation)
            {
                CompileExpression(exp, map, Hints.None, null);
            }
            else
            {
                CompileFunction((ElaEquation)exp, map);
            }

            //Functions are curried so generate a call for each argument
            for (var i = 0; i < args; i++)
            {
                cw.Emit(Op.Call);
            }

            cw.Emit(Op.Ret);
            frame.Layouts.Add(new MemoryLayout(currentCounter, cw.FinishFrame(), address));
            EndSection();
            EndScope();

            if (name != null)
            {
                EndFun(frame.Layouts.Count - 1);
            }

            cw.MarkLabel(funSkipLabel);
            cw.Emit(Op.PushI4, args);
            cw.Emit(Op.Newfun, frame.Layouts.Count - 1);
        }
示例#41
0
        //Generates the first part of the typeId needed to create a simple argument function. After
        //calling this method one should compile an expression that will become a function body.
        private void CompileFunctionProlog(string name, int pars, int line, int col, out Label funSkipLabel, out int address, out LabelMap newMap)
        {
            if (name != null)
            {
                StartFun(name, pars);
            }

            StartSection();
            StartScope(true, line, col);
            cw.StartFrame(pars);
            funSkipLabel = cw.DefineLabel();
            cw.Emit(Op.Br, funSkipLabel);
            address = cw.Offset;
            newMap  = new LabelMap();
            newMap.FunctionScope      = CurrentScope;
            newMap.FunctionParameters = pars;
        }
示例#42
0
        //Compiles record literal
        private ExprData CompileRecord(ElaRecordLiteral p, LabelMap map, Hints hints)
        {
            AddLinePragma(p);
            cw.Emit(Op.Newrec, p.Fields.Count);

            for (var i = 0; i < p.Fields.Count; i++)
            {
                var f = p.Fields[i];
                CompileExpression(f.FieldValue, map, Hints.None, f);
                cw.Emit(Op.Pushstr, AddString(f.FieldName));
                cw.Emit(Op.Reccons, i);
            }

            if ((hints & Hints.Left) == Hints.Left)
                AddValueNotUsed(p);

            return new ExprData(DataKind.VarType, (Int32)ElaTypeCode.Record);
        }
示例#43
0
        //Compiles list literal
        private ExprData CompileList(ElaListLiteral p, LabelMap map, Hints hints)
        {
            var len = p.Values.Count;
            AddLinePragma(p);
            cw.Emit(Op.Newlist);

            //If len is 0 than we have an empty (nil) list which is created by Newlist.
            for (var i = 0; i < len; i++)
            {
                CompileExpression(p.Values[p.Values.Count - i - 1], map, Hints.None, p);
                cw.Emit(Op.Cons);
            }

            if ((hints & Hints.Left) == Hints.Left)
                AddValueNotUsed(p);

            return new ExprData(DataKind.VarType, (Int32)ElaTypeCode.List);
        }
示例#44
0
        private ExprData CompileLazy(ElaLazyLiteral exp, LabelMap map, Hints hints)
        {
            var ed = default(ExprData);

            //Try to optimize lazy section for a case
            //when a function application is marked as lazy
            if (!TryOptimizeLazy(exp, map, hints))
            {
                //Regular lazy section compilation
                //Create a closure around section
                ed = CompileLazyExpression(exp.Expression, map, hints);
            }

            if ((hints & Hints.Left) == Hints.Left)
                AddValueNotUsed(exp);

            return ed;
        }
示例#45
0
        //Compiles a primitive literal value
        private ExprData CompilePrimitive(ElaPrimitive p, LabelMap map, Hints hints)
        {
            AddLinePragma(p);
            PushPrimitive(p, p.Value);

            if ((hints & Hints.Left) == Hints.Left)
            {
                AddValueNotUsed(map, p);
            }

            if (p.Parens && p.Value.IsNegative())
            {
                AddWarning(map, ElaCompilerWarning.SectionAmbiguity, p, p.Value.ToString());
                AddHint(ElaCompilerHint.AddSpaceSection, p, p.Value.ToString().TrimStart('-'));
            }

            return(new ExprData(DataKind.VarType, (Int32)p.Value.LiteralType));
        }
示例#46
0
        //Compiles record literal
        private ExprData CompileRecord(ElaRecordLiteral p, LabelMap map, Hints hints)
        {
            AddLinePragma(p);
            cw.Emit(Op.Newrec, p.Fields.Count);

            for (var i = 0; i < p.Fields.Count; i++)
            {
                var f = p.Fields[i];
                CompileExpression(f.FieldValue, map, Hints.None, f);
                cw.Emit(Op.Pushstr, AddString(f.FieldName));
                cw.Emit(Op.Reccons, i);
            }

            if ((hints & Hints.Left) == Hints.Left)
            {
                AddValueNotUsed(map, p);
            }

            return(new ExprData(DataKind.VarType, (Int32)ElaTypeCode.Record));
        }
示例#47
0
        //Compiles any given expression as lazy, can be used to automatically generate thunks
        //as well as to compile an explicit lazy literal.
        private ExprData CompileLazyExpression(ElaExpression exp, LabelMap map, Hints hints)
        {
            Label    funSkipLabel;
            int      address;
            LabelMap newMap;

            CompileFunctionProlog(null, 1, exp.Line, exp.Column, out funSkipLabel, out address, out newMap);
            var ed = CompileExpression(exp, newMap, Hints.Scope | Hints.FunBody, null);

            CompileFunctionEpilog(null, 1, address, funSkipLabel);

            if (ed.Type == DataKind.BuiltinError)
            {
                cw.Emit(Op.Api, 18);
            }

            cw.Emit(Op.Newlazy);

            return(default(ExprData));
        }
示例#48
0
        private ExprData CompileLazy(ElaLazyLiteral exp, LabelMap map, Hints hints)
        {
            var ed = default(ExprData);

            //Try to optimize lazy section for a case
            //when a function application is marked as lazy
            if (!TryOptimizeLazy(exp, map, hints))
            {
                //Regular lazy section compilation
                //Create a closure around section
                ed = CompileLazyExpression(exp.Expression, map, hints);
            }

            if ((hints & Hints.Left) == Hints.Left)
            {
                AddValueNotUsed(map, exp);
            }

            return(ed);
        }
示例#49
0
        //Compiles xs parameters, can be used in all cases where xs creation is needed.
        private void CompileTupleParameters(ElaExpression v, List<ElaExpression> pars, LabelMap map)
        {
            //Optimize xs creates for a case of pair (a single op typeId is used).
            if (pars.Count == 2)
            {
                CompileExpression(pars[0], map, Hints.None, v);
                CompileExpression(pars[1], map, Hints.None, v);
                AddLinePragma(v);
                cw.Emit(Op.Newtup_2);
            }
            else
            {
                AddLinePragma(v);
                cw.Emit(Op.Newtup, pars.Count);

                for (var i = 0; i < pars.Count; i++)
                {
                    CompileExpression(pars[i], map, Hints.None, v);
                    cw.Emit(Op.Tupcons, i);
                }
            }
        }
示例#50
0
        //Compiles logical OR operator in a lazy manner.
        private void CompileLogicalOr(ElaExpression parent, ElaExpression left, ElaExpression right, LabelMap map, Hints hints)
        {
            //Logical OR is executed in a lazy manner
            var exitLab = default(Label);
            var termLab = default(Label);
            var ut = hints;

            if ((ut & Hints.Left) == Hints.Left)
                ut ^= Hints.Left;

            if ((ut & Hints.Tail) == Hints.Tail)
                ut ^= Hints.Tail;

            CompileExpression(left, map, ut, parent);
            termLab = cw.DefineLabel();
            exitLab = cw.DefineLabel();
            cw.Emit(Op.Brtrue, termLab);
            CompileExpression(right, map, ut, parent);
            cw.Emit(Op.Br, exitLab);
            cw.MarkLabel(termLab);
            cw.Emit(Op.PushI1_1);
            cw.MarkLabel(exitLab);
            cw.Emit(Op.Nop);
        }
示例#51
0
        //This method contains main compilation logic for 'match' expressions and for 'try' expressions.
        //This method only supports a single pattern per entry.
        //A 'mexp' (matched expression) parameter can be null and is used for additional validation only.
        private void CompileSimpleMatch(IEnumerable<ElaEquation> bindings, LabelMap map, Hints hints, ElaExpression mexp)
        {
            ValidateOverlapSimple(bindings, mexp);

            var failLab = cw.DefineLabel();
            var endLab = cw.DefineLabel();

            //We need to remembers the first set of system addresses because we will have
            //to push them manually for the entries other than first.
            var firstSys = -1;
            var first = true;

            //Loops through all bindings
            //For the first iteration we assume that all values are already on stack.
            //For the next iteration we manually push all values.
            foreach (var b in bindings)
            {
                //Each match entry starts a lexical scope
                StartScope(false, b.Line, b.Column);

                //For second+ entries we put a label where we would jump if a previous
                //pattern fails
                if (!first)
                {
                    cw.MarkLabel(failLab);
                    failLab = cw.DefineLabel();
                }

                var p = b.Left;
                var sysVar = -1;

                //We apply an optimization if a we have a name reference (only for the first iteration).
                if (p.Type == ElaNodeType.NameReference && first)
                    sysVar = AddVariable(p.GetName(), p, ElaVariableFlags.Parameter, -1);
                else
                    sysVar = AddVariable(); //Create an unnamed system variable

                //This is not the first entry, so we have to push values first
                if (!first)
                    PushVar(firstSys);

                PopVar(sysVar);

                //We have to remember addresses calculated in a first iteration
                //to be able to push them once again in a second iteration.
                if (first)
                    firstSys = sysVar;

                CompilePattern(sysVar, p, failLab, false /*allowBang*/, false /*forceStrict*/);
                first = false;

                //Compile entry expression
                if (b.Right == null)
                    AddError(ElaCompilerError.InvalidMatchEntry, b.Left, FormatNode(b));
                else
                    CompileExpression(b.Right, map, Hints.None, b);

                //Close current scope
                EndScope();

                //If everything is OK skip through 'fail' clause
                cw.Emit(Op.Br, endLab);
            }

            //We fall here if all patterns have failed
            cw.MarkLabel(failLab);

            //If this hints is set than we generate a match for a 'try'
            //(exception handling) block. Instead of MatchFailed we need
            //to rethrow an original exception. Block 'try' always have
            //just a single expression to match, therefore firstSys[0] is
            //pretty safe here.
            if ((hints & Hints.Throw) == Hints.Throw)
            {
                PushVar(firstSys);
                cw.Emit(Op.Rethrow);
            }
            else
                cw.Emit(Op.Failwith, (Int32)ElaRuntimeError.MatchFailed);

            //Happy path for match
            cw.MarkLabel(endLab);
            cw.Emit(Op.Nop);
        }
示例#52
0
        //Compiles a sequencing operator
        private void CompileSeq(ElaExpression parent, ElaExpression left, ElaExpression right, LabelMap map, Hints hints)
        {
            var ut = hints;

            if ((ut & Hints.Left) == Hints.Left)
                ut ^= Hints.Left;

            //Sequence operators forces left expression, pops it and yields a value
            //of a right expression. Evaliation is done in a strict order.
            CompileExpression(left, map, Hints.None, parent);
            cw.Emit(Op.Force);
            cw.Emit(Op.Pop);
            CompileExpression(right, map, ut, parent);
        }
示例#53
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;
        }
示例#54
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;
        }
示例#55
0
        //Perform an eta expansion for a given expression
        private void EtaExpand(string name, ElaExpression exp, LabelMap map, int args)
        {
            //Here we generate a function which has a provided number of
            //arguments
            if (name != null)
                StartFun(name, args);

            StartSection();
            StartScope(true, exp.Line, exp.Column);
            cw.StartFrame(args);
            var funSkipLabel = cw.DefineLabel();
            cw.Emit(Op.Br, funSkipLabel);
            var address = cw.Offset;

            if (exp.Type != ElaNodeType.Equation)
                CompileExpression(exp, map, Hints.None, null);
            else
                CompileFunction((ElaEquation)exp);

            //Functions are curried so generate a call for each argument
            for (var i = 0; i < args; i++)
                cw.Emit(Op.Call);

            cw.Emit(Op.Ret);
            frame.Layouts.Add(new MemoryLayout(currentCounter, cw.FinishFrame(), address));
            EndSection();
            EndScope();

            if (name != null)
                EndFun(frame.Layouts.Count - 1);

            cw.MarkLabel(funSkipLabel);
            cw.Emit(Op.PushI4, args);
            cw.Emit(Op.Newfun, frame.Layouts.Count - 1);
        }
示例#56
0
        //Pattern match compilation method which is used by functions defined by pattern matching.
        //Argument patNum contains number of patterns that should be in each.
        private void CompileFunctionMatch(int patNum, IEnumerable<ElaEquation> bindings, LabelMap map, Hints hints)
        {
            ValidateOverlapComplex(bindings);

            var failLab = cw.DefineLabel();
            var endLab = cw.DefineLabel();

            //We need to remembers the first set of system addresses because we will have
            //to push them manually for the entries other than first.
            var firstSys = new int[patNum];
            var first = true;

            //Loops through all bindings
            //For the first iteration we assume that all values are already on stack.
            //For the next iteration we manually push all values.
            foreach (var b in bindings)
            {
                //Each match entry starts a lexical scope
                StartScope(false, b.Line, b.Column);

                //This match compilation is used for functions only,
                //therefore the left-hand should always be a juxtaposition.
                var fc = (ElaJuxtaposition)b.Left;
                var len = fc.Parameters.Count;
                var pars = fc.Parameters;

                //Entries have different number of patterns, so generate an error and continue
                //compilation in order to prevent generation of 'redundant' error messages.
                if (len < patNum)
                    AddError(ElaCompilerError.PatternsTooFew, b.Left, FormatNode(b.Left), patNum, len);
                else if (len > patNum)
                    AddError(ElaCompilerError.PatternsTooMany, b.Left, FormatNode(b.Left), patNum, len);

                //For second+ entries we put a label where we would jump if a previous
                //pattern fails
                if (!first)
                {
                    cw.MarkLabel(failLab);
                    failLab = cw.DefineLabel();
                }

                for (var i = 0; i < len; i++)
                {
                    var p = pars[i];
                    var sysVar = -1;

                    //We apply an optimization if a we have a name reference (only for the first iteration).
                    if (p.Type == ElaNodeType.NameReference && first)
                        sysVar = AddVariable(p.GetName(), p, ElaVariableFlags.Parameter, -1);
                    else
                        sysVar = AddVariable(); //Create an unnamed system variable

                    //This is not the first entry, so we have to push values first
                    if (!first && firstSys.Length > i)
                        PushVar(firstSys[i]);

                    PopVar(sysVar);

                    //We have to remember addresses calculated in a first iteration
                    //to be able to push them once again in a second iteration.
                    if (first && firstSys.Length > i)
                        firstSys[i] = sysVar;

                }

                //Now compile patterns
                for (var i = 0; i < len; i++)
                    if (firstSys.Length > i && pars.Count > i)
                        CompilePattern(firstSys[i], pars[i], failLab, true /*allowBang*/, false /*forceStrict*/);

                first = false;

                //Compile entry expression
                if (b.Right == null)
                    AddError(ElaCompilerError.InvalidMatchEntry, b.Left, FormatNode(b));
                else
                    CompileExpression(b.Right, map, hints, b);

                //Close current scope
                EndScope();

                //If everything is OK skip through 'fail' clause
                cw.Emit(Op.Br, endLab);
            }

            //We fall here if all patterns have failed
            cw.MarkLabel(failLab);
            cw.Emit(Op.Failwith, (Int32)ElaRuntimeError.MatchFailed);

            //Happy path for match
            cw.MarkLabel(endLab);
            cw.Emit(Op.Nop);
        }
示例#57
0
        //Used to compile a 'match' expression.
        private void CompileMatchExpression(ElaMatch n, LabelMap map, Hints hints)
        {
            CompileExpression(n.Expression, map, Hints.None, n);
            AddLinePragma(n);
            CompileSimpleMatch(n.Entries.Equations, map, hints, n.Expression);

            if ((hints & Hints.Left) == Hints.Left)
                AddValueNotUsed(n);
        }
示例#58
0
        //Used to compile a 'try' expression.
        private void CompileTryExpression(ElaTry n, LabelMap map, Hints hints)
        {
            var catchLab = cw.DefineLabel();
            var exitLab = cw.DefineLabel();

            //Generate a start of a 'try' section
            AddLinePragma(n);
            cw.Emit(Op.Start, catchLab);

            CompileExpression(n.Expression, map, Hints.None, n);

            //Leaving 'try' section
            cw.Emit(Op.Leave);
            cw.Emit(Op.Br, exitLab);
            cw.MarkLabel(catchLab);
            cw.Emit(Op.Leave);

            //Throw hint is to tell match compiler to generate a different typeId if
            //all pattern fail - to rethrow an original error instead of generating a
            //new MatchFailed error.
            CompileSimpleMatch(n.Entries.Equations, map, hints | Hints.Throw, null);

            cw.MarkLabel(exitLab);
            cw.Emit(Op.Nop);

            if ((hints & Hints.Left) == Hints.Left)
                AddValueNotUsed(n);
        }
示例#59
0
        private int CompileRecursiveFor(ElaGenerator s, LabelMap map, Hints hints, int parent, int parentTail)
        {
            var funAddr = AddVariable();
            StartSection();
            StartScope(true, s.Line, s.Column);
            cw.StartFrame(1);
            var funSkipLabel = cw.DefineLabel();
            cw.Emit(Op.Br, funSkipLabel);
            var address = cw.Offset;

            var exitLab = cw.DefineLabel();
            var endLab = cw.DefineLabel();
            var iterLab = cw.DefineLabel();
            var head = AddVariable();
            var tail = AddVariable();

            var sys = AddVariable();
            cw.Emit(Op.Dup);
            PopVar(sys);
            cw.Emit(Op.Isnil);
            cw.Emit(Op.Brtrue, endLab);
            PushVar(sys);
            cw.Emit(Op.Head);
            PopVar(head);
            PushVar(sys);
            cw.Emit(Op.Tail);
            PopVar(tail);

            if (s.Pattern.Type == ElaNodeType.NameReference)
            {
                var addr = AddVariable(s.Pattern.GetName(), s.Pattern, ElaVariableFlags.None, -1);
                PushVar(head);
                PopVar(addr);
            }
            else
                CompilePattern(head, s.Pattern, iterLab, false /*allowBang*/, false /*forceStrict*/);

            if (s.Guard != null)
            {
                CompileExpression(s.Guard, map, Hints.None, s);
                cw.Emit(Op.Brfalse, iterLab);
            }

            if (s.Body.Type == ElaNodeType.Generator)
            {
                var f = (ElaGenerator)s.Body;
                var child = CompileRecursiveFor(f, map, hints, funAddr, tail);
                CompileExpression(f.Target, map, Hints.None, f);
                PushVar(child);
                cw.Emit(Op.Call);
                cw.Emit(Op.Br, exitLab);//
            }
            else
            {
                PushVar(tail);
                PushVar(1 | (funAddr >> 8) << 8);
                cw.Emit(Op.LazyCall);
                CompileExpression(s.Body, map, Hints.None, s);
                cw.Emit(Op.Cons);
                cw.Emit(Op.Br, exitLab);
            }

            cw.MarkLabel(iterLab);
            PushVar(tail);
            PushVar(1 | (funAddr >> 8) << 8);
            cw.Emit(Op.Call);
            cw.Emit(Op.Br, exitLab);//

            cw.MarkLabel(endLab);

            if (parent == -1)
                cw.Emit(Op.Newlist);
            else
            {
                PushVar(1 | (parentTail >> 8) << 8);
                PushVar(2 | (parent >> 8) << 8);
                cw.Emit(Op.Call);
            }

            cw.MarkLabel(exitLab);
            cw.Emit(Op.Ret);
            frame.Layouts.Add(new MemoryLayout(currentCounter, cw.FinishFrame(), address));
            EndSection();
            EndScope();

            cw.MarkLabel(funSkipLabel);
            cw.Emit(Op.PushI4, 1);
            cw.Emit(Op.Newfun, frame.Layouts.Count - 1);
            PopVar(funAddr);
            return funAddr;
        }
示例#60
0
        //This methods tries to optimize lazy section. It would only work when a lazy
        //section if a function application that result in saturation (no partial applications)
        //allowed. In such a case this method eliminates "double" function call (which would be
        //the result of a regular compilation logic). If this method fails than regular compilation
        //logic is used.
        private bool TryOptimizeLazy(ElaLazyLiteral lazy, LabelMap map, Hints hints)
        {
            var body = default(ElaExpression);

            //Only function application is accepted
            if ((body = lazy.Expression).Type != ElaNodeType.Juxtaposition)
                return false;

            var funCall = (ElaJuxtaposition)body;

            //If a target is not a variable we can't check what is actually called
            if (funCall.Target.Type != ElaNodeType.NameReference)
                return false;

            var varRef = (ElaNameReference)funCall.Target;
            var scopeVar = GetVariable(varRef.Name, varRef.Line, varRef.Column);
            var len = funCall.Parameters.Count;

            //Only one parameter is allowed
            if (len > 1)
                return false;

            //If a target is not function we can't optimize it
            if ((scopeVar.VariableFlags & ElaVariableFlags.Function) != ElaVariableFlags.Function)
                return false;

            //Only saturation case is optimized
            if (scopeVar.Data != funCall.Parameters.Count)
                return false;

            //We can only optimize a thunk if a last parameter (that will be executed in a strict manner)
            //is either a primitive value or an already initialized variable.
            for (var i = 0; i < len; i++)
            {
                var p = funCall.Parameters[i];

                //Need to check if variable is already initialized.
                if (p.Type == ElaNodeType.NameReference)
                {
                    var ssv = GetVariable(p.GetName(), CurrentScope, GetFlags.NoError, 0, 0);

                    if ((ssv.Flags & ElaVariableFlags.NoInit) == ElaVariableFlags.NoInit)
                        return false;
                }
                else if (p.Type != ElaNodeType.Primitive)
                    return false;
            }

            for (var i = 0; i < len; i++)
                CompileExpression(funCall.Parameters[len - i - 1], map, Hints.None, funCall);

            var sl = len - 1;
            AddLinePragma(varRef);
            PushVar(scopeVar);

            //We partially apply function and create a new function
            for (var i = 0; i < sl; i++)
                cw.Emit(Op.Call);

            AddLinePragma(lazy);

            //LazyCall uses a function provided to create a thunk
            //and remembers last function arguments as ElaFunction.LastParameter
            cw.Emit(Op.LazyCall, len);
            return true;
        }