//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; } }
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); }
//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*/); } }
//Used to compile an anonymous function (lambda). This function returns a number of parameters //in compiled lambda. private int CompileLambda(ElaEquation bid) { var fc = new ElaJuxtaposition(); //Lambda is parsed as a an application of a lambda operator, e.g. //expr -> expr, therefore it needs to be transformed in order to be //able to use existing match compilation logic. if (bid.Left.Type == ElaNodeType.Juxtaposition && !bid.Left.Parens) //Parens flag is added if an expression is in parens and //therefore should be qualified as a pattern. { var f = (ElaJuxtaposition)bid.Left; fc.Parameters.Add(f.Target); fc.Parameters.AddRange(f.Parameters); } else { fc.Parameters.Add(bid.Left); } bid.Left = fc; var parLen = fc.Parameters.Count; StartFun(null, parLen); var funSkipLabel = cw.DefineLabel(); var map = new LabelMap(); cw.Emit(Op.Br, funSkipLabel); //We start a real (VM based) lexical scope for a function. StartScope(true, bid.Right.Line, bid.Right.Column); StartSection(); var address = cw.Offset; CompileFunctionMatch(parLen, bid, map, Hints.Scope | Hints.Tail); var funHandle = frame.Layouts.Count; var ss = EndFun(funHandle); frame.Layouts.Add(new MemoryLayout(currentCounter, ss, address)); EndScope(); EndSection(); cw.Emit(Op.Ret); cw.MarkLabel(funSkipLabel); AddLinePragma(bid); //Function is constructed cw.Emit(Op.PushI4, parLen); cw.Emit(Op.Newfun, funHandle); return(parLen); }
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); }
//Compiles constructor parameters. This method is called from CompileContructorCall, it is used //when inlining constructor and when constructor has >2 arguments. private void CompileConstructorParameters(string prefix, string name, ElaJuxtaposition juxta, LabelMap map) { var pars = juxta.Parameters; cw.Emit(Op.Newtup, pars.Count); for (var i = 0; i < pars.Count; i++) { CompileExpression(pars[i], map, Hints.None, juxta); TypeCheckIf(prefix, name, i); cw.Emit(Op.Tupcons, i); } }
private ElaJuxtaposition MakeBind(ElaExpression exp) { var ret = new ElaJuxtaposition { Target = new ElaNameReference { Name = ">>=" } }; ret.Parameters.Add(exp); ret.Parameters.Add(null); ret.SetLinePragma(exp.Line, exp.Column); ret.Target.SetLinePragma(exp.Line, exp.Column); return(ret); }
private ElaLambda MakeFake() { var juxta = new ElaJuxtaposition { Target = new ElaNameReference { Name = "point" } }; juxta.Parameters.Add(new ElaUnitLiteral()); return(new ElaLambda { Left = new ElaPlaceholder(), Right = juxta }); }
//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*/); }
//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); } }
//Here we check if a function application is actually a constructor application. //The latter case can be inlined. We support both direct reference and a qualified reference. //Only constructors without type constraints can be inlined. private bool TryOptimizeConstructor(ElaJuxtaposition juxta, LabelMap map) { if (juxta.Target.Type == ElaNodeType.NameReference) { var nr = (ElaNameReference)juxta.Target; if (nr.Uppercase || Format.IsSymbolic(nr.Name)) { var sv = GetGlobalVariable("$-" + nr.Name, GetFlags.NoError, 0, 0); if (!sv.IsEmpty() && sv.Data == juxta.Parameters.Count) { var sv2 = GetGlobalVariable("$--" + nr.Name, GetFlags.None, juxta.Line, juxta.Column); CompileConstructorCall(null, nr.Name, juxta, map, sv, sv2); return(true); } } } else if (juxta.Target.Type == ElaNodeType.FieldReference) { var fr = (ElaFieldReference)juxta.Target; if (fr.TargetObject.Type == ElaNodeType.NameReference) { var prefix = fr.TargetObject.GetName(); CodeFrame _; var sv = FindByPrefix(prefix, "$-" + fr.FieldName, out _); if (!sv.IsEmpty() && sv.Data == juxta.Parameters.Count) { var sv2 = FindByPrefix(prefix, "$--" + fr.FieldName, out _); CompileConstructorCall(prefix, fr.FieldName, juxta, map, sv, sv2); return(true); } } } return(false); }
//Checks if a constructor parameter type constraint is invalid private bool IsInvalidConstructorParameterConstaint(ElaJuxtaposition n) { if (n.Parameters.Count != 1) { return(true); } if (IsInvalidConstructorParameter(n.Parameters[0])) { return(true); } if (n.Target.Type == ElaNodeType.NameReference) { return(!((ElaNameReference)n.Target).Uppercase); } else if (n.Target.Type == ElaNodeType.FieldReference) { var f = (ElaFieldReference)n.Target; return(f.TargetObject.Type != ElaNodeType.NameReference || !Char.IsUpper(f.FieldName[0])); } return(true); }
//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, 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; } }
//Compiling a regular function call. private ExprData CompileFunctionCall(ElaJuxtaposition v, LabelMap map, Hints hints) { var ed = ExprData.Empty; var bf = default(ElaNameReference); var sv = default(ScopeVar); if (!map.HasContext && TryOptimizeConstructor(v, map)) { return(ed); } if (!map.HasContext && v.Target.Type == ElaNodeType.NameReference) { bf = (ElaNameReference)v.Target; sv = GetVariable(bf.Name, bf.Line, bf.Column); //If the target is one of the built-in application function we need to transform this //to a regular function call, e.g. 'x |> f' is translated into 'f x' by manually creating //an appropriates AST node. This is done to simplify compilation - so that all optimization //of a regular function call would be applied to pipes as well. if ((sv.Flags & ElaVariableFlags.Builtin) == ElaVariableFlags.Builtin) { var k = (ElaBuiltinKind)sv.Data; if (v.Parameters.Count == 2) { if (k == ElaBuiltinKind.BackwardPipe) { var fc = new ElaJuxtaposition { Target = v.Parameters[0] }; fc.SetLinePragma(v.Line, v.Column); fc.Parameters.Add(v.Parameters[1]); return(CompileFunctionCall(fc, map, hints)); } else if (k == ElaBuiltinKind.ForwardPipe) { var fc = new ElaJuxtaposition { Target = v.Parameters[1] }; fc.SetLinePragma(v.Line, v.Column); fc.Parameters.Add(v.Parameters[0]); return(CompileFunctionCall(fc, map, hints)); } else if (k == ElaBuiltinKind.LogicalOr) { CompileLogicalOr(v, v.Parameters[0], v.Parameters[1], map, hints); return(ed); } else if (k == ElaBuiltinKind.LogicalAnd) { CompileLogicalAnd(v, v.Parameters[0], v.Parameters[1], map, hints); return(ed); } else if (k == ElaBuiltinKind.Seq) { CompileSeq(v, v.Parameters[0], v.Parameters[1], map, hints); return(ed); } } } } //We can't apply tail call optimization for the context bound call var tail = (hints & Hints.Tail) == Hints.Tail && !map.HasContext; var len = v.Parameters.Count; //Compile arguments to which a function is applied for (var i = 0; i < len; i++) { CompileExpression(v.Parameters[len - i - 1], map, Hints.None, v); } //If this a tail call and we effectively call the same function we are currently in, //than do not emit an actual function call, just do a goto. (Tail recursion optimization). if (tail && map.FunctionName != null && map.FunctionName == v.GetName() && map.FunctionParameters == len && map.FunctionScope == GetScope(map.FunctionName) && (sv.Flags & ElaVariableFlags.ClassFun) != ElaVariableFlags.ClassFun) { AddLinePragma(v); cw.Emit(Op.Br, map.FunStart); return(ed); } if (bf != null) { if (v.Parameters[0].Type == ElaNodeType.Primitive && bf.Line == v.Parameters[0].Line && bf.Column + bf.Name.Length == v.Parameters[0].Column && ((ElaPrimitive)v.Parameters[0]).Value.IsNegative()) { var par = ((ElaPrimitive)v.Parameters[0]).Value.ToString(); AddWarning(map, ElaCompilerWarning.NegationAmbiguity, v, bf.Name, par); AddHint(ElaCompilerHint.AddSpaceApplication, v, bf.Name, par, par.TrimStart('-')); } //The target is one of built-in functions and therefore can be inlined for optimization. if ((sv.Flags & ElaVariableFlags.Builtin) == ElaVariableFlags.Builtin) { var kind = (ElaBuiltinKind)sv.Data; var pars = BuiltinParams(kind); //We inline built-ins only when all arguments are provided //If this is not the case a built-in is compiled into a function in-place //and than called. if (len != pars) { AddLinePragma(bf); CompileBuiltin(kind, v.Target, map, bf.Name); if (v.FlipParameters) { cw.Emit(Op.Flip); } for (var i = 0; i < len; i++) { cw.Emit(Op.Call); } } else { CompileBuiltinInline(kind, v.Target, map, hints); } ed = new ExprData(DataKind.BuiltinError, sv.Data); return(ed); } else { //Regular situation, just push a target name AddLinePragma(v.Target); PushVar(sv); if ((sv.VariableFlags & ElaVariableFlags.Function) == ElaVariableFlags.Function) { ed = new ExprData(DataKind.FunParams, sv.Data); } else if ((sv.VariableFlags & ElaVariableFlags.ObjectLiteral) == ElaVariableFlags.ObjectLiteral) { ed = new ExprData(DataKind.VarType, (Int32)ElaVariableFlags.ObjectLiteral); } } } else { ed = CompileExpression(v.Target, map, Hints.None, v); } //Why it comes from AST? Because parser do not save the difference between pre-, post- and infix applications. //However Ela does support left and right sections for operators - and for such cases an additional flag is used //to signal about a section. if (v.FlipParameters) { cw.Emit(Op.Flip); } //It means that we are trying to call "not a function". Ela is a dynamic language, still it's worth to generate //a warning in such a case. if (ed.Type == DataKind.VarType) { AddWarning(map, ElaCompilerWarning.FunctionInvalidType, v.Target, FormatNode(v.Target)); } AddLinePragma(v); for (var i = 0; i < len; i++) { var last = i == v.Parameters.Count - 1; //Use a tail call if this function call is a tail expression and optimizations are enabled. if (last && tail && opt) { cw.Emit(Op.Callt); } else { cw.Emit(Op.Call); } } return(ed); }
//This method rewrites a do-notation into a chain of bind (>>=) function calls. private void CompileDoNotation(ElaDoNotation exp, LabelMap map, Hints hints) { var root = default(ElaExpression); var bind = default(ElaLetBinding); var current = default(ElaLambda); for (var i = 0; i < exp.Statements.Count; i++) { var ce = exp.Statements[i]; var last = i == exp.Statements.Count - 1; if (ce.Type != ElaNodeType.LetBinding) { var ass = ce.Type == ElaNodeType.DoAssignment ? (ElaDoAssignment)ce : null; var juxta = MakeBind(ass != null ? ass.Right : ce); if (root == null) { root = juxta; if (bind != null) { bind.Expression = root; root = bind; bind = null; } } else { current.Right = juxta; if (bind != null) { bind.Expression = juxta; current.Right = bind; bind = null; } } if (!last) { var lam = new ElaLambda(); lam.SetLinePragma(ce.Line, ce.Column); lam.Left = ass != null ? ass.Left : new ElaPlaceholder(); juxta.Parameters[1] = lam; current = lam; if (lam.Left.Type != ElaNodeType.NameReference && lam.Left.Type != ElaNodeType.Placeholder) { var jux = new ElaJuxtaposition { Target = new ElaNameReference { Name = "failure" } }; jux.Parameters.Add(new ElaNameReference { Name = "$v" }); var jux2 = new ElaJuxtaposition(); jux2.Parameters.Add(new ElaNameReference { Name = "$v" }); lam.Next = new ElaEquation { Left = jux2, Right = jux }; } } else { if (current != null) { if (current.Right.Type == ElaNodeType.LetBinding) { var lb = (ElaLetBinding)current.Right; lb.Expression = juxta.Parameters[0]; current.Right = lb; } else { current.Right = juxta.Parameters[0]; } } else if (root.Type == ElaNodeType.Juxtaposition) { var jroot = (ElaJuxtaposition)root; //root = jroot.Parameters[0]; jroot.Parameters[1] = MakeFake(); } else if (root.Type == ElaNodeType.LetBinding) { var lroot = (ElaLetBinding)root; if (lroot.Expression.Type == ElaNodeType.Juxtaposition) { var jroot = (ElaJuxtaposition)lroot.Expression; jroot.Parameters[1] = MakeFake(); //lroot.Expression = jroot.Parameters[0]; } } } } else { var letb = (ElaLetBinding)ce; if (bind != null) { bind.Equations.Equations.AddRange(letb.Equations.Equations); } else { bind = letb; } } } CompileExpression(root, map, hints, null); }
//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); }