コード例 #1
0
ファイル: ParserHelper.cs プロジェクト: rizwan3d/elalang
 private ElaExpression GetPrefixFun(ElaExpression funexp, ElaExpression par, bool flip)
 {
     var fc = new ElaJuxtaposition(t) { Target = funexp };
     fc.Parameters.Add(par);
     fc.FlipParameters = flip;
     return fc;
 }
コード例 #2
0
ファイル: Builder.TypeCheck.cs プロジェクト: rizwan3d/elalang
        //This method check if a given name (qualified or not, prefix may be null) is a type or class
        //and emit an appropriate code to check it.
        //This method assumes that a value to check is already on the top of the stack.
        private void CheckTypeOrClass(string prefix, string name, Label failLab, ElaExpression exp)
        {
            //This item can either check a specific type or a trait.
            var isType = NameExists(prefix, "$$" + name); //A type with such a name exists
            var isClass = NameExists(prefix, "$$$" + name); //A class with such a name exists

            //OK, this is ambiguity, better to report about that. We will consider a symbol
            //to be a type and compile further.
            if (isType && isClass)
            {
                AddWarning(ElaCompilerWarning.TypeClassAmbiguity, exp, prefix == null ? name : prefix + "." + name, FormatNode(exp));

                //This hint suggests to use prefix, it is stupid to generate it, if we have a prefix already.
                if (prefix == null)
                    AddHint(ElaCompilerHint.TypeClassAmbiguity, exp);
            }

            cw.Emit(Op.Force);

            if (isType)
            {
                EmitSpecName(prefix, "$$" + name, exp, ElaCompilerError.UndefinedType);
                cw.Emit(Op.Ctype);
            }
            else
            {
                EmitSpecName(prefix, "$$$" + name, exp, ElaCompilerError.UnknownClass);
                cw.Emit(Op.Traitch);
            }

            cw.Emit(Op.Brfalse, failLab);
        }
コード例 #3
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);
        }
コード例 #4
0
ファイル: ElaNameReference.cs プロジェクト: rizwan3d/elalang
        internal override bool CanFollow(ElaExpression exp)
        {
            if (exp.IsIrrefutable())
                return false;

            if (exp.Type == ElaNodeType.NameReference)
                return Name != exp.GetName();

            return true;
        }
コード例 #5
0
ファイル: Builder.Lazy.cs プロジェクト: rizwan3d/elalang
        //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;
        }
コード例 #6
0
ファイル: Format.cs プロジェクト: rizwan3d/elalang
        public static void PutInBraces(ElaExpression e, StringBuilder sb)
        {
            var simple = IsSimpleExpression(e);

            if (!simple)
            {
                sb.Append('(');
                e.ToString(sb, 0);
                sb.Append(')');
            }
            else
                e.ToString(sb, 0);
        }
コード例 #7
0
ファイル: ElaPrimitive.cs プロジェクト: rizwan3d/elalang
        internal override bool CanFollow(ElaExpression exp)
        {
            if (exp.IsIrrefutable())
                return false;

            if (exp.Type == ElaNodeType.Primitive)
            {
                var lit = (ElaPrimitive)exp;
                return !Value.Equals(lit.Value);
            }

            return true;
        }
コード例 #8
0
ファイル: Format.cs プロジェクト: rizwan3d/elalang
 public static bool IsSimpleExpression(ElaExpression p)
 {
     return
         p == null ||
         p.Type == ElaNodeType.NameReference ||
         p.Type == ElaNodeType.Primitive ||
         p.Type == ElaNodeType.ListLiteral ||
         p.Type == ElaNodeType.RecordLiteral ||
         p.Type == ElaNodeType.TupleLiteral ||
         p.Type == ElaNodeType.Placeholder ||
         p.Type == ElaNodeType.UnitLiteral ||
         p.Type == ElaNodeType.FieldReference;
 }
コード例 #9
0
ファイル: Builder.Variables.cs プロジェクト: rizwan3d/elalang
        //This method is used to generate Push* op typeId for a special name prefixed by $$ or $$$.
        //Such names are used for hidden variables that contains unique codes of types and classes.
        //This method can emit a qualified name (with a module prefix) as well as a short name.
        //It also generates an appropriate error if a name is not found.
        //Specifying ElaCompilerError.None would force this method to not generate any error messages.
        private ScopeVar EmitSpecName(string ns, string specName, ElaExpression exp, ElaCompilerError err, out int modId)
        {
            if (ns != null)
            {
                var v = GetVariable(ns, exp.Line, exp.Column);

                //A prefix (ns) is not a module alias which is an error
                if ((v.Flags & ElaVariableFlags.Module) != ElaVariableFlags.Module && err != ElaCompilerError.None)
                    AddError(ElaCompilerError.InvalidQualident, exp, ns);

                ModuleReference mr;
                var lh = -1;

                if (frame.References.TryGetValue(ns, out mr))
                    lh = mr.LogicalHandle;

                var extVar = default(ScopeVar);
                var mod = lh > -1 && lh < refs.Count ? refs[lh] : null;

                //A name (or even module) not found, generate an error
                if ((mod == null || !mod.GlobalScope.Locals.TryGetValue(specName, out extVar)) &&
                    !options.IgnoreUndefined && err != ElaCompilerError.None)
                {
                    //No need to apped alias if we want to generate UndefinedName. That would be misleading.
                    if (err == ElaCompilerError.UndefinedName)
                        AddError(err, exp, specName.TrimStart('$'));
                    else
                        AddError(err, exp, ns + "." + specName.TrimStart('$'));
                }

                if ((extVar.Flags & ElaVariableFlags.Private) == ElaVariableFlags.Private && err != ElaCompilerError.None)
                    AddError(ElaCompilerError.PrivateNameInModule, exp, specName.TrimStart('$'), ns);

                modId = lh;
                extVar = new ScopeVar(extVar.Flags | ElaVariableFlags.External, lh | (extVar.Address << 8), extVar.Data);
                PushVar(extVar);
                return extVar;
            }
            else
            {
                //Without a qualident it is pretty straightforward
                var a = GetVariable(specName, CurrentScope, GetFlags.NoError, exp.Line, exp.Column);

                if (a.IsEmpty() && !options.IgnoreUndefined && err != ElaCompilerError.None)
                    AddError(err, exp, specName.TrimStart('$'));

                modId = (a.Flags & ElaVariableFlags.External) == ElaVariableFlags.External ? a.Address & Byte.MaxValue : -1;
                PushVar(a);
                return a;
            }
        }
コード例 #10
0
ファイル: Builder.Types.cs プロジェクト: rizwan3d/elalang
        //Generic compilation logic for a constructor, compiles both functions and constants.
        //Parameter sca is an address of variable that contains real type ID.
        private void CompileConstructor(string typeName, int sca, ElaExpression exp, ElaVariableFlags flags, int typeModuleId)
        {
            //Constructor name
            var name = String.Empty;

            switch (exp.Type)
            {
                case ElaNodeType.NameReference:
                    {
                        var n = (ElaNameReference)exp;
                        name = n.Name;

                        if (!n.Uppercase)
                            AddError(ElaCompilerError.InvalidConstructor, n, FormatNode(n));
                        else
                            CompileConstructorConstant(typeName, n, sca, flags, typeModuleId);
                    }
                    break;
                case ElaNodeType.Juxtaposition:
                    {
                        var n = (ElaJuxtaposition)exp;

                        if (n.Target.Type == ElaNodeType.NameReference)
                        {
                            var m = (ElaNameReference)n.Target;
                            name = m.Name;

                            //If a name is uppercase or if this is an infix/postfix/prefix constructor
                            //we assume that this is a correct case and is a constructor function
                            if (m.Uppercase || (Format.IsSymbolic(m.Name) && n.Parameters.Count <= 2))
                            {
                                CompileConstructorFunction(typeName, m.Name, n, sca, flags, typeModuleId);
                                break;
                            }
                        }

                        AddError(ElaCompilerError.InvalidConstructor, exp, FormatNode(exp));
                    }
                    break;
                default:
                    AddError(ElaCompilerError.InvalidConstructor, exp, FormatNode(exp));
                    break;
            }

            //To prevent redundant errors
            CurrentScope.Locals.Remove("$$$$" + name);
            var ab = AddVariable("$$$$" + name, exp, flags, -1);
            cw.Emit(Op.Ctorid, frame.InternalConstructors.Count - 1);
            PopVar(ab);
        }
コード例 #11
0
ファイル: Builder.Ranges.cs プロジェクト: rizwan3d/elalang
        //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);
            }
        }
コード例 #12
0
ファイル: ParserHelper.cs プロジェクト: rizwan3d/elalang
        private ElaExpression GetOperatorFun(string op, ElaExpression left, ElaExpression right)
        {
            var fc = new ElaJuxtaposition(t) {
                Target = new ElaNameReference(t) { Name = op }
            };

            if (left != null)
                fc.Parameters.Add(left);
            else
                fc.FlipParameters = true;

            if (right != null)
                fc.Parameters.Add(right);

            return fc;
        }
コード例 #13
0
ファイル: Builder.Match.cs プロジェクト: rizwan3d/elalang
        //Adds a provided variable to the current scope or modifies an existing
        //variable with the same name (this is required because match variables unlike
        //regular variables in the same scope can shadow each other).
        private int AddMatchVariable(string varName, ElaExpression exp, out bool newV)
        {
            var sv = ScopeVar.Empty;
            newV = false;

            //Here we first check if such a name is already added and if this is the case simply fetch an
            //existing name.
            if (CurrentScope.Parent != null && CurrentScope.Parent.Locals.TryGetValue(varName, out sv)
                && (sv.Flags & ElaVariableFlags.Parameter) == ElaVariableFlags.Parameter)
                return 0 | sv.Address << 8;
            else
            {
                var res = CurrentScope.TryChangeVariable(varName);
                newV = true;

                if (res != -1)
                    return 0 | res << 8;

                return AddVariable(varName, exp, ElaVariableFlags.None, -1);
            }
        }
コード例 #14
0
ファイル: ElaJuxtaposition.cs プロジェクト: rizwan3d/elalang
        internal override bool CanFollow(ElaExpression exp)
        {
            if (exp.IsIrrefutable())
                return false;

            if (exp.Type == ElaNodeType.Juxtaposition)
            {
                var jx = (ElaJuxtaposition)exp;

                if (jx.Parameters.Count != Parameters.Count || jx.Target.GetName() != Target.GetName())
                    return true;

                for (var i = 0; i < Parameters.Count; i++)
                    if (Parameters[i].CanFollow(jx.Parameters[i]))
                        return true;

                return false;
            }

            return true;
        }
コード例 #15
0
ファイル: Format.cs プロジェクト: ngoffee/ela
        public static void PutInParens(ElaExpression e, StringBuilder sb)
        {
            if (e == null)
            {
                sb.Append("<null>");
                return;
            }

            var simple = IsSimpleExpression(e);

            if (!simple)
            {
                sb.Append('(');
                e.ToString(sb, 0);
                sb.Append(')');
            }
            else
            {
                e.ToString(sb, 0);
            }
        }
コード例 #16
0
ファイル: ElaListLiteral.cs プロジェクト: rizwan3d/elalang
        internal override bool CanFollow(ElaExpression pat)
        {
            if (pat.IsIrrefutable())
                return false;

            if (pat.Type == ElaNodeType.ListLiteral)
            {
                var xs = (ElaListLiteral)pat;

                if (xs.Values.Count != Values.Count)
                    return true;

                for (var i = 0; i < Values.Count; i++)
                    if (Values[i].CanFollow(xs.Values[i]))
                        return true;

                return false;
            }

            return true;
        }
コード例 #17
0
ファイル: ElaRecordLiteral.cs プロジェクト: rizwan3d/elalang
        internal override bool CanFollow(ElaExpression pat)
        {
            if (pat.IsIrrefutable())
                return false;

            if (pat.Type == ElaNodeType.RecordLiteral)
            {
                var rec = (ElaRecordLiteral)pat;

                if (rec.Fields.Count != Fields.Count)
                    return true;

                for (var i = 0; i < rec.Fields.Count; i++)
                    if (Fields[i].CanFollow(rec.Fields[i]))
                        return true;

                return false;
            }

            return true;
        }
コード例 #18
0
ファイル: ElaTupleLiteral.cs プロジェクト: rizwan3d/elalang
        internal override bool CanFollow(ElaExpression pat)
        {
            if (pat.IsIrrefutable())
                return false;

            if (pat.Type == ElaNodeType.TupleLiteral)
            {
                var tuple = (ElaTupleLiteral)pat;

                if (tuple.Parameters.Count != Parameters.Count)
                    return true;

                for (var i = 0; i < Parameters.Count; i++)
                    if (Parameters[i].CanFollow(tuple.Parameters[i]))
                        return true;

                return false;
            }

            return true;
        }
コード例 #19
0
ファイル: Builder.Literals.cs プロジェクト: rizwan3d/elalang
        //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);
                }
            }
        }
コード例 #20
0
ファイル: Builder.Variables.cs プロジェクト: rizwan3d/elalang
        //A main method to add named variables.
        private int AddVariable(string name, ElaExpression exp, ElaVariableFlags flags, int data)
        {
            //Hiding is allowed in parameter list
            if ((flags & ElaVariableFlags.Parameter) == ElaVariableFlags.Parameter)
                CurrentScope.Locals.Remove(name);
            else if (CurrentScope.Locals.ContainsKey(name))
            {
                //But for other cases hiding in the same scope is not allowed
                CurrentScope.Locals.Remove(name);
                AddError(ElaCompilerError.NoHindingSameScope, exp, name.TrimStart('$'));
            }

            CurrentScope.Locals.Add(name, new ScopeVar(flags, currentCounter, data));

            //Additional debug info is only generated when a debug flag is set.
            if (debug && exp != null)
            {
                cw.Emit(Op.Nop);
                AddVarPragma(name, currentCounter, cw.Offset, flags, data);
                AddLinePragma(exp);
            }

            return AddVariable();
        }
コード例 #21
0
ファイル: Builder.Operators.cs プロジェクト: rizwan3d/elalang
        //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);
        }
コード例 #22
0
ファイル: ElaExpression.cs プロジェクト: ngoffee/ela
 internal virtual bool CanFollow(ElaExpression exp)
 {
     return(!exp.IsIrrefutable());
 }
コード例 #23
0
ファイル: Format.cs プロジェクト: ngoffee/ela
 public static bool IsHiddenVar(ElaExpression p)
 {
     return(p != null && p.Type == ElaNodeType.NameReference &&
            ((ElaNameReference)p).Name[0] == '$');
 }
コード例 #24
0
ファイル: Builder.Debug.cs プロジェクト: rizwan3d/elalang
 //Generates a line pragma and remembers a provided location
 //via lastLine and lastColumn class fields.
 private void AddLinePragma(ElaExpression exp)
 {
     lastLine = exp.Line;
     lastColumn = exp.Column;
     pdb.AddLineSym(cw.Offset, exp.Line, exp.Column);
 }
コード例 #25
0
ファイル: Builder.Operators.cs プロジェクト: rizwan3d/elalang
        //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);
        }
コード例 #26
0
ファイル: ElaDebugPoint.cs プロジェクト: ngoffee/ela
 internal override bool CanFollow(ElaExpression _)
 {
     return(true);
 }
コード例 #27
0
ファイル: Builder.Match.cs プロジェクト: rizwan3d/elalang
 //Tests if a given expression is a name reference of a placeholder (_).
 private bool IsSimplePattern(ElaExpression exp)
 {
     return exp.Type == ElaNodeType.NameReference || exp.Type == ElaNodeType.Placeholder;
 }
コード例 #28
0
ファイル: Builder.Match.cs プロジェクト: rizwan3d/elalang
        //Performs validation of overlapping for simple case of pattern matching
        //(such as 'match' expression with a single pattern per entry).
        private void ValidateOverlapSimple(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(ElaCompilerWarning.MatchEntryNotReachable, e.Left, FormatNode(e.Left), FormatNode(o));

                lst.Add(e.Left);
            }
        }
コード例 #29
0
ファイル: Builder.Match.cs プロジェクト: rizwan3d/elalang
        //Builds a lists of all variables that are declared in the given pattern.
        private void ExtractPatternNames(ElaExpression pat, List<String> names)
        {
            switch (pat.Type)
            {
                case ElaNodeType.LazyLiteral:
                    {
                        //This whole method is used to extract names before making a
                        //pattern lazy, so we need to extract all names here as well,
                        //because this lazy literal will be compiled strict anyway (because
                        //the whole pattern is lazy already).
                        var vp = (ElaLazyLiteral)pat;
                        ExtractPatternNames(vp.Expression, names);
                    }
                    break;
                case ElaNodeType.UnitLiteral: //Idle
                    break;
                case ElaNodeType.As:
                    {
                        var asPat = (ElaAs)pat;
                        names.Add(asPat.Name);
                        ExtractPatternNames(asPat.Expression, names);
                    }
                    break;
                case ElaNodeType.Primitive: //Idle
                    break;
                case ElaNodeType.NameReference:
                    {
                        var vexp = (ElaNameReference)pat;

                        if (!vexp.Uppercase) //Uppercase is constructor
                            names.Add(vexp.Name);
                    }
                    break;
                case ElaNodeType.RecordLiteral:
                    {
                        var rexp = (ElaRecordLiteral)pat;

                        foreach (var e in rexp.Fields)
                            if (e.FieldValue != null)
                                ExtractPatternNames(e.FieldValue, names);
                    }
                    break;
                case ElaNodeType.TupleLiteral:
                    {
                        var texp = (ElaTupleLiteral)pat;

                        foreach (var e in texp.Parameters)
                            ExtractPatternNames(e, names);
                    }
                    break;
                case ElaNodeType.Placeholder: //Idle
                    break;
                case ElaNodeType.Juxtaposition:
                    {
                        var hexp = (ElaJuxtaposition)pat;

                        foreach (var e in hexp.Parameters)
                            ExtractPatternNames(e, names);
                    }
                    break;
                case ElaNodeType.ListLiteral:
                    {
                        var l = (ElaListLiteral)pat;

                        if (l.HasValues())
                        {
                            foreach (var e in l.Values)
                                ExtractPatternNames(e, names);
                        }
                    }
                    break;
            }
        }
コード例 #30
0
ファイル: Builder.Match.cs プロジェクト: rizwan3d/elalang
        //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);
        }
コード例 #31
0
ファイル: Builder.Match.cs プロジェクト: rizwan3d/elalang
        //Compile a given expression as a pattern. If match fails proceed to failLab.
        private void CompilePattern(int sysVar, ElaExpression exp, Label failLab, bool allowBang, bool forceStrict)
        {
            AddLinePragma(exp);

            switch (exp.Type)
            {
                case ElaNodeType.LazyLiteral:
                    {
                        var n = (ElaLazyLiteral)exp;

                        //Normally this flag is set when everything is already compiled as lazy.
                        if (forceStrict)
                            CompilePattern(sysVar, n.Expression, failLab, allowBang, forceStrict);
                        else
                            CompileLazyPattern(sysVar, exp, allowBang);
                    }
                    break;
                case ElaNodeType.FieldReference:
                    {
                        //We treat this expression as a constructor with a module alias
                        var n = (ElaFieldReference)exp;
                        var fn = n.FieldName;
                        var alias = n.TargetObject.GetName();
                        PushVar(sysVar);

                        if (n.TargetObject.Type != ElaNodeType.NameReference)
                            AddError(ElaCompilerError.InvalidPattern, n, FormatNode(n));
                        else
                            EmitSpecName(alias, "$$$$" + fn, n, ElaCompilerError.UndefinedName);

                        cw.Emit(Op.Skiptag);
                        cw.Emit(Op.Br, failLab);
                    }
                    break;
                case ElaNodeType.NameReference:
                    {
                        //Irrefutable pattern, always binds expression to a name, unless it is
                        //a constructor pattern
                        var n = (ElaNameReference)exp;

                        //Bang pattern are only allowed in constructors and functions
                        if (n.Bang && !allowBang)
                        {
                            AddError(ElaCompilerError.BangPatternNotValid, exp, FormatNode(exp));
                            AddHint(ElaCompilerHint.BangsOnlyFunctions, exp);
                        }

                        if (n.Uppercase) //This is a constructor
                        {
                            if (sysVar != -1)
                                PushVar(sysVar);

                            EmitSpecName(null, "$$$$" + n.Name, n, ElaCompilerError.UndefinedName);

                            //This op codes skips one offset if an expression
                            //on the top of the stack has a specified tag.
                            cw.Emit(Op.Skiptag);
                            cw.Emit(Op.Br, failLab);
                        }
                        else
                        {
                            var newV = false;
                            var addr = AddMatchVariable(n.Name, n, out newV);

                            //This is a valid situation, it means that the value is
                            //already on the top of the stack.
                            if (sysVar > -1 && newV)
                                PushVar(sysVar);

                            if (n.Bang)
                                cw.Emit(Op.Force);

                            //The binding is already done, so just idle.
                            if (newV)
                                PopVar(addr);
                        }
                    }
                    break;
                case ElaNodeType.UnitLiteral:
                    {
                        //Unit pattern is redundant, it is essentially the same as checking
                        //the type of an expression which is what we do here.
                        PushVar(sysVar);
                        cw.Emit(Op.Force);
                        cw.Emit(Op.PushI4, (Int32)ElaTypeCode.Unit);
                        cw.Emit(Op.Ctype);

                        //Types are not equal, proceed to fail.
                        cw.Emit(Op.Brfalse, failLab);
                    }
                    break;
                case ElaNodeType.Primitive:
                    {
                        var n = (ElaPrimitive)exp;

                        //Compare a given value with a primitive
                        PushVar(sysVar);
                        PushPrimitive(n.Value);
                        cw.Emit(Op.Cneq);

                        //Values not equal, proceed to fail.
                        cw.Emit(Op.Brtrue, failLab);
                    }
                    break;
                case ElaNodeType.As:
                    {
                        var n = (ElaAs)exp;
                        CompilePattern(sysVar, n.Expression, failLab, allowBang, false /*forceStrict*/);
                        var newV = false;
                        var addr = AddMatchVariable(n.Name, n, out newV);
                        PushVar(sysVar);
                        PopVar(addr);
                    }
                    break;
                case ElaNodeType.Placeholder:
                    //This is a valid situation, it means that the value is
                    //already on the top of the stack. Otherwise - nothing have to be done.
                    if (sysVar == -1)
                        cw.Emit(Op.Pop);
                    break;
                case ElaNodeType.RecordLiteral:
                    {
                        var n = (ElaRecordLiteral)exp;
                        CompileRecordPattern(sysVar, n, failLab, allowBang);
                    }
                    break;
                case ElaNodeType.TupleLiteral:
                    {
                        var n = (ElaTupleLiteral)exp;
                        CompileTuplePattern(sysVar, n, failLab, allowBang);
                    }
                    break;
                case ElaNodeType.Juxtaposition:
                    {
                        //An infix pattern, currently the only case is head/tail pattern.
                        var n = (ElaJuxtaposition)exp;
                        CompileComplexPattern(sysVar, n, failLab, allowBang);
                    }
                    break;
                case ElaNodeType.ListLiteral:
                    {
                        var n = (ElaListLiteral)exp;

                        //We a have a nil pattern '[]'
                        if (!n.HasValues())
                        {
                            PushVar(sysVar);
                            cw.Emit(Op.Isnil);
                            cw.Emit(Op.Brfalse, failLab);
                        }
                        else
                        {
                            //We don't want to write the same compilation logic twice,
                            //so here we transform a list literal into a chain of function calls, e.g.
                            //[1,2,3] goes to 1::2::3::[] with a mandatory nil at the end.
                            var len = n.Values.Count;
                            ElaExpression last = ElaListLiteral.Empty;
                            var fc = default(ElaJuxtaposition);

                            //Loops through all elements in literal backwards
                            for (var i = 0; i < len; i++)
                            {
                                var nn = n.Values[len - i - 1];
                                fc = new ElaJuxtaposition();
                                fc.SetLinePragma(nn.Line, nn.Column);
                                fc.Parameters.Add(nn);
                                fc.Parameters.Add(last);
                                last = fc;
                            }

                            //Now we can compile it as head/tail pattern
                            CompilePattern(sysVar, fc, failLab, allowBang, false /*forceStrict*/);
                        }
                    }
                    break;
                default:
                    AddError(ElaCompilerError.InvalidPattern, exp, FormatNode(exp));
                    break;
            }
        }
コード例 #32
0
ファイル: Builder.Match.cs プロジェクト: rizwan3d/elalang
        //Compiles a pattern as lazy by creating a thunk an initializing all pattern names with these thunks.
        private void CompileLazyPattern(int sys, ElaExpression pat, bool allowBang)
        {
            //First we need to obtain a list of all names, that declared in this pattern.
            var names = new List<String>();
            ExtractPatternNames(pat, names);

            //Walk through all names and create thunks
            for (var i = 0; i < names.Count; i++)
            {
                var n = names[i];
                Label funSkipLabel;
                int address;
                LabelMap newMap;

                CompileFunctionProlog(null, 1, pat.Line, pat.Column, out funSkipLabel, out address, out newMap);

                var next = cw.DefineLabel();
                var exit = cw.DefineLabel();

                //As soon as already compiling pattern as lazy, we should enforce strictness even if
                //pattern is declared as lazy. Otherwise everything would crash, because here assume
                //that names are bound in this bound in the same scope.
                CompilePattern(1 | ((sys >> 8) << 8), pat, next, allowBang, true /*forceStrict*/);
                cw.Emit(Op.Br, exit);
                cw.MarkLabel(next);
                cw.Emit(Op.Failwith, (Int32)ElaRuntimeError.MatchFailed);
                cw.MarkLabel(exit);
                cw.Emit(Op.Nop);

                //Here we expect that our target variable is already declared by a pattern in the
                //same physical scope - so we can obtain and push it as a return value.
                var sv = GetVariable(n, pat.Line, pat.Column);
                PushVar(sv);
                CompileFunctionEpilog(null, 1, address, funSkipLabel);
                cw.Emit(Op.Newlazy);

                ScopeVar var;
                if (CurrentScope.Locals.TryGetValue(n, out var))
                {
                    //If it doesn't have a NoInit flag we are not good
                    if ((var.Flags & ElaVariableFlags.NoInit) == ElaVariableFlags.NoInit)
                    {
                        PopVar(var.Address = 0 | var.Address << 8); //Aligning it to local scope
                        CurrentScope.RemoveFlags(n, ElaVariableFlags.NoInit);
                    }
                }
            }
        }
コード例 #33
0
ファイル: Builder.TypeCheck.cs プロジェクト: rizwan3d/elalang
 //Performs a type check and throws and error if the type is not as expected.
 //This method assumes that a value to be checked is on the top of the stack.
 private ScopeVar TypeCheckConstructor(string cons, string prefix, string name, ElaExpression par, bool force)
 {
     var sv = EmitSpecName(prefix, "$$" + name, par, ElaCompilerError.UndefinedType);
     TypeCheckConstructorAllStack(cons, force);
     return sv;
 }
コード例 #34
0
ファイル: Builder.Functions.cs プロジェクト: rizwan3d/elalang
        //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);
        }
コード例 #35
0
 internal override bool CanFollow(ElaExpression exp)
 {
     return(Expression.CanFollow(exp));
 }