//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); }
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(); }
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(); }
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); }
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; }
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; }
internal void RemoveFlags(string name, ElaVariableFlags flags) { var sv = Locals[name]; sv = new ScopeVar(sv.Flags ^ flags, sv.Address, sv.Data); Locals[name] = sv; }
//**********************************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; }
//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); }
//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; } }
//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; }
//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; } }
//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); }