Esempio n. 1
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;
        }
Esempio n. 2
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;
        }
Esempio n. 3
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);
     }
 }
Esempio n. 4
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);
        }
Esempio n. 5
0
        //Checks if a field reference is actually an exported name prefixed by a module
        //and if so emits a direct Pushexit op typeId. If this is not the case returns false
        //and the typeId gets compiled as a regular field reference.
        private bool TryOptimizeFieldReference(ElaFieldReference p, out ScopeVar fieldSv)
        {
            fieldSv = default(ScopeVar);

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

            var mod = default(ModuleReference);
            var sv  = GetVariable(p.TargetObject.GetName(), p.TargetObject.Line, p.TargetObject.Column);

            //Looks like a target object is not a module
            if ((sv.Flags & ElaVariableFlags.Module) != ElaVariableFlags.Module ||
                !frame.References.TryGetValue(p.TargetObject.GetName(), out mod))
            {
                return(false);
            }

            //We have such a reference but looks like it couldn't be obtained
            //We don't need to handle this situation here, it is already reported by a linker
            if (refs[mod.LogicalHandle] == null)
            {
                return(false);
            }

            //No such name, now captured statically
            if (!refs[mod.LogicalHandle].GlobalScope.Locals.TryGetValue(p.FieldName, out fieldSv) &&
                !options.IgnoreUndefined)
            {
                AddError(ElaCompilerError.UndefinedNameInModule, p, p.TargetObject.GetName(), p.FieldName);
                return(false);
            }

            //Name is private, now captured statically
            if ((fieldSv.VariableFlags & ElaVariableFlags.Private) == ElaVariableFlags.Private)
            {
                AddError(ElaCompilerError.PrivateNameInModule, p, p.FieldName, p.TargetObject.GetName());
                return(false);
            }

            AddLinePragma(p);
            cw.Emit(Op.Pushext, mod.LogicalHandle | fieldSv.Address << 8);
            return(true);
        }
Esempio n. 6
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);
        }
Esempio n. 7
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);
            }
        }
Esempio n. 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);
            }
        }
Esempio n. 9
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);
            }
        }
Esempio n. 10
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.CompilerGenerated, -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.CompilerGenerated, -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 | ElaVariableFlags.CompilerGenerated, len);
            var typeVar = AddVariable("$--" + name, juxta, flags | ElaVariableFlags.CompilerGenerated, len);

            cw.Emit(Op.Ctorid, ctid);
            PopVar(consVar);
            PushVar(sca);
            PopVar(typeVar);
        }