Beispiel #1
0
        //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);
        }
Beispiel #2
0
 internal void Emit(Op op, Label label)
 {
     if (!label.IsEmpty())
     {
         fixups.Add(ops.Count);
         Emit(op, label.GetIndex());
     }
     else
         Emit(op, 0);
 }
Beispiel #3
0
        //A generic case of constructor pattern
        private void CompileConstructorPattern(int sysVar, ElaJuxtaposition call, Label failLab, bool allowBang)
        {
            var n = String.Empty;
            PushVar(sysVar);

            //We have a qualified name here, in such case we don't just check
            //the presence of a constructor but ensure that this constructor originates
            //from a given module
            if (call.Target.Type == ElaNodeType.FieldReference)
            {
                var fr = (ElaFieldReference)call.Target;
                n = fr.FieldName;
                var alias = fr.TargetObject.GetName();

                if (fr.TargetObject.Type != ElaNodeType.NameReference)
                    AddError(ElaCompilerError.InvalidPattern, fr, FormatNode(fr));
                else
                    EmitSpecName(alias, "$$$$" + n, fr, ElaCompilerError.UndefinedName);
            }
            else
            {
                //Here we simply check that a constructor symbol is defined
                n = call.Target.GetName();
                EmitSpecName(null, "$$$$" + n, call.Target, 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); //We will skip this if tags are equal

            for (var i = 0; i < call.Parameters.Count; i++)
            {
                PushVar(sysVar);
                cw.Emit(Op.Untag, i); //Unwrap it

                //Now we need to create a new system variable to hold
                //an unwrapped value.
                var sysVar2 = -1;
                var p = call.Parameters[i];

                //Don't do redundant bindings for simple patterns
                if (!IsSimplePattern(p))
                {
                    sysVar2 = AddVariable();
                    PopVar(sysVar2);
                }

                CompilePattern(sysVar2, p, failLab, allowBang, false /*forceStrict*/);
            }
        }
Beispiel #4
0
        //Currently this method only compiles head/tail pattern which is processed by parser
        //as function application. However it can be extended to support custom 'infix' patterns in future.
        private void CompileComplexPattern(int sysVar, ElaJuxtaposition call, Label failLab, bool allowBang)
        {
            if (call.Target == null)
                CompileHeadTail(sysVar, call, failLab, allowBang);
            else if (call.Target.Type == ElaNodeType.NameReference)
            {
                var targetName = call.Target.GetName();
                var sv = GetVariable(call.Target.GetName(), CurrentScope, GetFlags.NoError, call.Target.Line, call.Target.Column);

                //The head symbol corresponds to a constructor, this is a special case of pattern
                if ((sv.VariableFlags & ElaVariableFlags.Builtin) == ElaVariableFlags.Builtin && (ElaBuiltinKind)sv.Data == ElaBuiltinKind.Cons)
                    CompileHeadTail(sysVar, call, failLab, allowBang);
                else
                    CompileConstructorPattern(sysVar, call, failLab, allowBang);
            }
            else if (call.Target.Type == ElaNodeType.FieldReference)
                CompileConstructorPattern(sysVar, call, failLab, allowBang);
            else
            {
                //We don't yet support other cases
                AddError(ElaCompilerError.InvalidPattern, call.Target, FormatNode(call.Target));
                return;
            }
        }
Beispiel #5
0
        //Compile a xs pattern in the form: {fieldName=pat,..}. This pattern can fail at
        //run-time if a given expression doesn't support Len and Pushelem op codes.
        private void CompileTuplePattern(int sysVar, ElaTupleLiteral tuple, Label failLab, bool allowBang)
        {
            var len = tuple.Parameters.Count;

            //Check the length first
            PushVar(sysVar);
            cw.Emit(Op.Len);
            cw.Emit(Op.PushI4, len);
            cw.Emit(Op.Cneq);
            cw.Emit(Op.Brtrue, failLab); //Length not equal, proceed to fail

            //Loops through all xs patterns
            for (var i = 0; i < len; i++)
            {
                var pat = tuple.Parameters[i];
                PushVar(sysVar);

                //Generate a 'short' op typeId for the first entry
                if (i == 0)
                    cw.Emit(Op.PushI4_0);
                else
                    cw.Emit(Op.PushI4, i);

                cw.Emit(Op.Pushelem);

                //Here we need to bind a value of an element to a new system
                //variable in order to match it.
                var sysVar2 = AddVariable();
                PopVar(sysVar2);

                //Match an element of a xs
                CompilePattern(sysVar2, pat, failLab, allowBang, false /*forceStrict*/);
            }
        }
Beispiel #6
0
        //Compile a record pattern in the form: {fieldName=pat,..}. Here we don't check the
        //type of an expression on the top of the stack - in a case if try to match a non-record
        //using this pattern the whole match would fail on Pushfld operation.
        private void CompileRecordPattern(int sysVar, ElaRecordLiteral rec, Label failLab, bool allowBang)
        {
            //Loops through all record fields
            for (var i = 0; i < rec.Fields.Count; i++)
            {
                var fld = rec.Fields[i];
                var addr = AddVariable();
                var si = AddString(fld.FieldName);

                PushVar(sysVar);
                cw.Emit(Op.Pushstr, si);
                cw.Emit(Op.Hasfld);
                cw.Emit(Op.Brfalse, failLab);

                PushVar(sysVar);
                cw.Emit(Op.Pushstr, si);
                cw.Emit(Op.Pushfld);
                PopVar(addr);

                //We obtain a value of field, now we need to match it using a pattern in
                //a field value (it could be a name reference or a non-irrefutable pattern).
                CompilePattern(addr, fld.FieldValue, failLab, allowBang, false /*forceStrict*/);
            }
        }
Beispiel #7
0
        //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;
            }
        }
Beispiel #8
0
        //Compiles a special case of constructor pattern - head/tail pattern.
        private void CompileHeadTail(int sysVar, ElaJuxtaposition call, Label failLab, bool allowBang)
        {
            var fst = call.Parameters[0];
            var snd = call.Parameters[1];

            //Now check if a list is nil. If this is the case proceed to fail.
            PushVar(sysVar);
            cw.Emit(Op.Isnil);
            cw.Emit(Op.Brtrue, failLab);

            //Take a head of a list
            PushVar(sysVar);
            cw.Emit(Op.Head);
            var sysVar2 = -1;

            //For a case of a simple pattern we don't need to create to additional system
            //variable - these patterns are aware that they might accept -1 and it means that
            //the value is already on the top of the stack.
            if (!IsSimplePattern(fst))
            {
                sysVar2 = AddVariable();
                PopVar(sysVar2);
            }

            CompilePattern(sysVar2, fst, failLab, allowBang, false /*forceStrict*/);

            //Take a tail of a list
            PushVar(sysVar);
            cw.Emit(Op.Tail);
            sysVar2 = -1;

            //Again, don't do redundant bindings for simple patterns
            if (!IsSimplePattern(snd))
            {
                sysVar2 = AddVariable();
                PopVar(sysVar2);
            }

            CompilePattern(sysVar2, snd, failLab, allowBang, false /*forceStrict*/);
        }
Beispiel #9
0
 internal Label DefineLabel()
 {
     var lab = new Label(labels.Count);
     labels.Add(Label.EmptyLabel);
     return lab;
 }
Beispiel #10
0
 internal void MarkLabel(Label label)
 {
     labels[label.GetIndex()] = ops.Count;
 }
Beispiel #11
0
        //Generates the last past of a simple function by emitting Ret, initializing function and creating
        //a new function through Newfun. This method should be called right after compiling an expression
        //that should be a body of a function.
        private void CompileFunctionEpilog(string name, int pars, int address, Label funSkipLabel)
        {
            cw.Emit(Op.Ret);
            var ff = 0;

            if (name != null)
                ff = EndFun(frame.Layouts.Count);
            else
                ff = cw.FinishFrame();

            frame.Layouts.Add(new MemoryLayout(currentCounter, ff, address));
            EndSection();
            EndScope();
            cw.MarkLabel(funSkipLabel);
            cw.Emit(Op.PushI4, pars);
            cw.Emit(Op.Newfun, frame.Layouts.Count - 1);
        }
Beispiel #12
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;
        }