//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);
        }
Beispiel #2
0
    private void Build(DMatch node, Hints hints, CompilerContext ctx)
    {
        ValidateMatch(node);
        StartScope(ScopeKind.Lexical, node.Location);

        ctx = new(ctx)
        {
            MatchExit = cw.DefineLabel()
        };

        var sys  = AddVariable();
        var push = hints.Append(Push);

        if (node.Expression != null)
        {
            Build(node.Expression, push.Remove(Last), ctx);
        }

        cw.PopVar(sys);
        var sysVar = new ScopeVar(sys);

        foreach (var e in node.Entries)
        {
            BuildEntry(e, sysVar, push, ctx);
        }

        //It is some kind of a hack, but Expression can be null
        //only if this match is inside try/catch
        if (node.Expression != null)
        {
            ThrowError(DyError.MatchFailed);
            cw.Fail();
        }
        else
        {
            cw.PushVar(sysVar);
            cw.Fail();
        }

        cw.MarkLabel(ctx.MatchExit);
        cw.Nop();
        PopIf(hints);
        EndScope();
    }
Beispiel #3
0
    private void BuildEntry(DMatchEntry node, ScopeVar sys, Hints hints, CompilerContext ctx)
    {
        StartScope(ScopeKind.Lexical, node.Location);
        var skip = cw.DefineLabel();

        cw.PushVar(sys);
        BuildPattern(node.Pattern, hints.Remove(Last), ctx);
        cw.Brfalse(skip);

        if (node.Guard != null)
        {
            Build(node.Guard, hints.Remove(Last), ctx);
            cw.Brfalse(skip);
        }

        Build(node.Expression, hints, ctx);
        cw.Br(ctx.MatchExit);
        cw.MarkLabel(skip);
        cw.Nop();
        EndScope();
    }
Beispiel #4
0
        public override dynamic Execute(Ast_Scope scope)
        {
            dynamic value = Expression.Execute(scope);

            if (value is Ast_Procedure procedure)
            {
                value = procedure.Clone(Variable.Token);
            }
            else if (value is Ast_Function function)
            {
                value = function.Clone(Variable.Token);
            }
            else if (value is Ast_Struct strct)
            {
                value = strct.Clone(Variable.Token);
            }
            else if (value is List <VT_Any> )
            {
                value = CloneArray(value);
            }
            else if (value is Dictionary <string, VT_Any> )
            {
                value = CloneRecord(value);
            }

            dynamic index = null;

            if (Variable.Index != null)
            {
                index = Variable.Index;
            }

            Ast_Variable ScopeVar;
            var          ve = scope.VariableExists(Variable.Name);

            if (!ve)
            {
                ScopeVar = scope.Variables.Append(Variable.Name, value);
            }
            else
            {
                ScopeVar = scope.GetVariable(Variable.Name);
            }

            if (index != null)
            {
                dynamic indexedv = null;
                if (Operand.Type != TokenType.OpAssign)
                {
                    indexedv = ScopeVar.DoGetValue(index, scope);
                }
                switch (Operand.Type)
                {
                case TokenType.OpAssign:
                    ScopeVar.DoSetValue(value, index, scope);
                    break;

                case TokenType.OpAssignAdd:
                    ScopeVar.DoSetValue(indexedv + value, index, scope);
                    break;

                case TokenType.OpAssignDivide:
                    if (value == 0)
                    {
                        throw new RuntimeError(Token, "Division by zero.");
                    }
                    ScopeVar.DoSetValue(indexedv / value, index, scope);
                    break;

                case TokenType.OpAssignMultiply:
                    ScopeVar.DoSetValue(indexedv * value, index, scope);
                    break;

                case TokenType.OpAssignSubtract:
                    ScopeVar.DoSetValue(indexedv - value, index, scope);
                    break;
                }
            }
            else
            {
                dynamic v = null;
                if (Operand.Type != TokenType.OpAssign)
                {
                    v = ScopeVar.DoGetValue();
                }
                switch (Operand.Type)
                {
                case TokenType.OpAssign:
                    ScopeVar.DoSetValue(value);
                    break;

                case TokenType.OpAssignAdd:
                    ScopeVar.DoSetValue(v + value);
                    break;

                case TokenType.OpAssignDivide:
                    if (value == 0)
                    {
                        throw new RuntimeError(Token, "Division by zero.");
                    }
                    ScopeVar.DoSetValue(v / value);
                    break;

                case TokenType.OpAssignMultiply:
                    ScopeVar.DoSetValue(v * value);
                    break;

                case TokenType.OpAssignSubtract:
                    ScopeVar.DoSetValue(v - value);
                    break;
                }
            }
            return(false);
        }
Beispiel #5
0
 internal void AddFlagsAndData(string name, ElaVariableFlags flags, int data)
 {
     var sv = Locals[name];
     sv = new ScopeVar(sv.Flags | flags, sv.Address, data);
     Locals[name] = sv;
 }
Beispiel #6
0
        internal int TryChangeVariable(string name)
        {
            var v = default(ScopeVar);

            if (Locals.TryGetValue(name, out v))
            {
                v = new ScopeVar(ElaVariableFlags.None, v.Address, -1);
                Locals[name] = v;
                return v.Address;
            }

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

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

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

                    return true;
                }
            }

            return false;
        }
Beispiel #9
0
        //Compiles a type constructor
        private void CompileConstructorFunction(string typeName, string name, ElaJuxtaposition juxta, int sca, ElaVariableFlags flags, int typeModuleId)
        {
            Label funSkipLabel;
            int address;
            LabelMap newMap;
            var pars = new List<String>();
            var len = juxta.Parameters.Count;

            AddLinePragma(juxta);
            CompileFunctionProlog(name, len, juxta.Line, juxta.Column, out funSkipLabel, out address, out newMap);

            var sys = new int[len];
            var types = new ScopeVar[len];
            var bangs = new bool[len];

            //Here we have to validate all constructor parameters
            for (var i = 0; i < len; i++)
            {
                var ce = juxta.Parameters[i];
                sys[i] = AddVariable();

                if (bangs[i] = IsBang(ce))
                    cw.Emit(Op.Force);

                PopVar(sys[i]);

                //This can be type a type constraint so we should compile here type check logic
                if (ce.Type == ElaNodeType.Juxtaposition)
                {
                    var jc = (ElaJuxtaposition)ce;

                    //First we check if a constraint is actually valid
                    if (IsInvalidConstructorParameterConstaint(jc))
                        AddError(ElaCompilerError.InvalidConstructorParameter, juxta, FormatNode(ce), name);
                    else if (jc.Target.Type == ElaNodeType.NameReference)
                    {
                        //A simple direct type reference
                        var nt = (ElaNameReference)jc.Target;
                        PushVar(sys[i]);
                        types[i] = TypeCheckConstructor(name, null, nt.Name, nt, false);
                        pars.Add(jc.Parameters[0].GetName());
                    }
                    else if (jc.Target.Type == ElaNodeType.FieldReference)
                    {
                        //A type is qualified with a module alias
                        var fr = (ElaFieldReference)jc.Target;
                        PushVar(sys[i]);
                        types[i] = TypeCheckConstructor(name, fr.TargetObject.GetName(), fr.FieldName, fr, false);
                        pars.Add(jc.Parameters[0].GetName());
                    }
                }
                else if (ce.Type == ElaNodeType.NameReference && !IsInvalidConstructorParameter(ce))
                {
                    pars.Add(ce.GetName());
                    types[i] = ScopeVar.Empty;
                }
                else
                    AddError(ElaCompilerError.InvalidConstructorParameter, juxta, FormatNode(ce), name);
            }

            frame.InternalConstructors.Add(new ConstructorData
            {
                TypeName = typeName,
                Name = name,
                Code = -1,
                Parameters = pars,
                TypeModuleId = typeModuleId
            });

            //For optimization purposes we use a simplified creation algorythm for constructors
            //with 1 and 2 parameters
            if (len == 1)
                PushVar(sys[0]);
            else if (len == 2)
            {
                PushVar(sys[0]);
                PushVar(sys[1]);
            }
            else
            {
                cw.Emit(Op.Newtup, len);

                for (var i = 0; i < len; i++)
                {
                    PushVar(sys[i]);
                    cw.Emit(Op.Tupcons, i);
                }
            }

            var ctid = frame.InternalConstructors.Count - 1;
            cw.Emit(Op.Ctorid, ctid);
            //Refering to captured name, need to recode its address
            PushVar((Byte.MaxValue & sca) + 1 | (sca << 8) >> 8);

            if (len == 1)
                cw.Emit(Op.Newtype1);
            else if (len == 2)
                cw.Emit(Op.Newtype2);
            else
                cw.Emit(Op.Newtype);

            CompileFunctionEpilog(name, len, address, funSkipLabel);
            var a = AddVariable(name, juxta, ElaVariableFlags.TypeFun|ElaVariableFlags.Function|flags, len);
            PopVar(a);

            //We need to add special variable that would indicate that a constructor parameter
            //should be strictly evaluated. Used when inlining constructors. This variable is for
            //metadata only, it is never initialized.
            for (var i = 0; i < bangs.Length; i++)
            {
                if (bangs[i])
                {
                    CurrentScope.Locals.Remove("$-!" + i + name); //To prevent redundant errors
                    AddVariable("$-!" + i + name, juxta, ElaVariableFlags.None, -1);
                }
            }

            //We need to add special variable that would store type check information.
            //This information is used when inlining constructors.
            for (var i = 0; i < types.Length; i++)
            {
                var sv = types[i];

                //There is a type constraint, we have to memoize it for inlining
                if (!sv.IsEmpty())
                {
                    CurrentScope.Locals.Remove("$-" + i + name); //To prevent redundant errors
                    var av = AddVariable("$-" + i + name, juxta, ElaVariableFlags.None, -1);

                    //This ScopeVar was obtained in a different scope, we have to patch it here
                    if ((sv.Flags & ElaVariableFlags.External) == ElaVariableFlags.External)
                        PushVar(sv); //No need to patch an external
                    else
                    {
                        //Roll up one scope
                        sv.Address = ((sv.Address & Byte.MaxValue) - 1) | (sv.Address >> 8) << 8;
                        PushVar(sv);
                    }

                    PopVar(av);
                }
            }

            //To prevent redundant errors
            CurrentScope.Locals.Remove("$-" + name);
            CurrentScope.Locals.Remove("$--" + name);
            //We add special variables that can be used lately to inline this constructor call.
            var consVar = AddVariable("$-" + name, juxta, flags, len);
            var typeVar = AddVariable("$--" + name, juxta, flags, len);
            cw.Emit(Op.Ctorid, ctid);
            PopVar(consVar);
            PushVar(sca);
            PopVar(typeVar);
        }
Beispiel #10
0
        //Some members may be generated directly by compiler. Here we check if this is the case for this
        //particular member.
        private bool TryAutogenerate(ScopeVar var, ElaClassInstance inst)
        {
            if ((var.Flags & ElaVariableFlags.Builtin) != ElaVariableFlags.Builtin)
                return false;

            switch ((ElaBuiltinKind)var.Data)
            {
                //Used to generate Bounded.maxBound constant
                case ElaBuiltinKind.GenMaxBound:
                    cw.Emit(Op.PushI4, 1);
                    //Obtain type ID, no errors, they are captured elsewhere
                    EmitSpecName(inst.TypePrefix, "$$" + inst.TypeName, inst, ElaCompilerError.None);
                    cw.Emit(Op.Api, (Int32)Api.TypeConsNumber);
                    cw.Emit(Op.Sub);

                    EmitSpecName(inst.TypePrefix, "$$" + inst.TypeName, inst, ElaCompilerError.None);
                    cw.Emit(Op.Api2, (Int32)Api.ConsCodeByIndex);
                    cw.Emit(Op.Api, (Int32)Api.ConsDefault);
                    return true;
                //Used to generate Bounded.minBound constant
                case ElaBuiltinKind.GenMinBound:
                case ElaBuiltinKind.GenDefault:
                    cw.Emit(Op.PushI4_0);
                    //Obtain type ID, no errors, they are captured elsewhere
                    EmitSpecName(inst.TypePrefix, "$$" + inst.TypeName, inst, ElaCompilerError.None);
                    cw.Emit(Op.Api2, (Int32)Api.ConsCodeByIndex);
                    cw.Emit(Op.Api, (Int32)Api.ConsDefault);
                    return true;
                default:
                    return false;
            }
        }
Beispiel #11
0
        //Looks for a 'table' function (a class function to which we would add instances).
        //It can be optionally qualified with a 'classPrefix' (which is a module alias).
        private ScopeVar ObtainClassFunction(string classPrefix, string className, string name, int line, int col)
        {
            var btVar = default(ScopeVar);

            if (classPrefix == null && !frame.InternalClasses.ContainsKey(className))
            {
                //We can't look for this name directly, because it can be shadowed.
                //First, we need to obtain a class reference, which is used to obtain a local
                //module ID (encoded in address). Then we directly look for this name in the module.
                var cv = GetGlobalVariable("$$$" + className, GetFlags.NoError, line, col);
                var moduleHandle = cv.Address & Byte.MaxValue;

                if (moduleHandle < 0 || moduleHandle >= refs.Count ||
                    !refs[moduleHandle].GlobalScope.Locals.TryGetValue(name, out btVar))
                    btVar = ScopeVar.Empty;
                else
                {
                    //OK, found, but now we need to patch this variable, so it will be correctly
                    //encoded as an external name.
                    btVar = new ScopeVar(btVar.Flags | ElaVariableFlags.External,
                        moduleHandle | btVar.Address << 8, btVar.Data);
                }
            }
            else if (classPrefix == null)
                btVar = GetVariable(name, CurrentScope, GetFlags.NoError, line, col);
            else
            {
                CodeFrame _;
                btVar = FindByPrefix(classPrefix, name, out _);
            }

            return btVar;
        }
Beispiel #12
0
        //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;
            }
        }
Beispiel #13
0
 //This method should be used always instead of direct emitting Pushvar/Pushloc op codes.
 //It first checks if a given variable is an external and in such a case generates
 //a different op typeId. For addr it uses PushVar(int) to generate an appropriate op typeId.
 private void PushVar(ScopeVar sv)
 {
     if ((sv.Flags & ElaVariableFlags.External) == ElaVariableFlags.External)
         cw.Emit(Op.Pushext, sv.Address);
     else
         PushVar(sv.Address);
 }