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; }
//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); }
//Compiles built-in as function in place. It is compiled in such a manner each time //it is referenced. But normally its body is just one or two op codes, so this is not a problem. private void CompileBuiltin(ElaBuiltinKind kind, ElaExpression exp, LabelMap map, string name) { StartSection(); //Here we determine the number of parameters based on the function kind. var pars = BuiltinParams(kind); cw.StartFrame(pars); var funSkipLabel = cw.DefineLabel(); cw.Emit(Op.Br, funSkipLabel); var address = cw.Offset; pdb.StartFunction(name, address, pars); AddLinePragma(exp); //Gets the actual typeId for built-in CompileBuiltinInline(kind, exp, map, Hints.None); cw.Emit(Op.Ret); frame.Layouts.Add(new MemoryLayout(currentCounter, cw.FinishFrame(), address)); EndSection(); pdb.EndFunction(frame.Layouts.Count - 1, cw.Offset); cw.MarkLabel(funSkipLabel); cw.Emit(Op.PushI4, pars); cw.Emit(Op.Newfun, frame.Layouts.Count - 1); }
internal override bool CanFollow(ElaExpression exp) { if (exp.IsIrrefutable()) return false; if (exp.Type == ElaNodeType.NameReference) return Name != exp.GetName(); return true; }
//Compiles any given expression as lazy, can be used to automatically generate thunks //as well as to compile an explicit lazy literal. private ExprData CompileLazyExpression(ElaExpression exp, LabelMap map, Hints hints) { Label funSkipLabel; int address; LabelMap newMap; CompileFunctionProlog(null, 1, exp.Line, exp.Column, out funSkipLabel, out address, out newMap); var ed = CompileExpression(exp, newMap, Hints.Scope | Hints.FunBody, null); CompileFunctionEpilog(null, 1, address, funSkipLabel); cw.Emit(Op.Newlazy); return ed; }
public static void PutInBraces(ElaExpression e, StringBuilder sb) { var simple = IsSimpleExpression(e); if (!simple) { sb.Append('('); e.ToString(sb, 0); sb.Append(')'); } else e.ToString(sb, 0); }
internal override bool CanFollow(ElaExpression exp) { if (exp.IsIrrefutable()) return false; if (exp.Type == ElaNodeType.Primitive) { var lit = (ElaPrimitive)exp; return !Value.Equals(lit.Value); } return true; }
public static bool IsSimpleExpression(ElaExpression p) { return p == null || p.Type == ElaNodeType.NameReference || p.Type == ElaNodeType.Primitive || p.Type == ElaNodeType.ListLiteral || p.Type == ElaNodeType.RecordLiteral || p.Type == ElaNodeType.TupleLiteral || p.Type == ElaNodeType.Placeholder || p.Type == ElaNodeType.UnitLiteral || p.Type == ElaNodeType.FieldReference; }
//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; } }
//Generic compilation logic for a constructor, compiles both functions and constants. //Parameter sca is an address of variable that contains real type ID. private void CompileConstructor(string typeName, int sca, ElaExpression exp, ElaVariableFlags flags, int typeModuleId) { //Constructor name var name = String.Empty; switch (exp.Type) { case ElaNodeType.NameReference: { var n = (ElaNameReference)exp; name = n.Name; if (!n.Uppercase) AddError(ElaCompilerError.InvalidConstructor, n, FormatNode(n)); else CompileConstructorConstant(typeName, n, sca, flags, typeModuleId); } break; case ElaNodeType.Juxtaposition: { var n = (ElaJuxtaposition)exp; if (n.Target.Type == ElaNodeType.NameReference) { var m = (ElaNameReference)n.Target; name = m.Name; //If a name is uppercase or if this is an infix/postfix/prefix constructor //we assume that this is a correct case and is a constructor function if (m.Uppercase || (Format.IsSymbolic(m.Name) && n.Parameters.Count <= 2)) { CompileConstructorFunction(typeName, m.Name, n, sca, flags, typeModuleId); break; } } AddError(ElaCompilerError.InvalidConstructor, exp, FormatNode(exp)); } break; default: AddError(ElaCompilerError.InvalidConstructor, exp, FormatNode(exp)); break; } //To prevent redundant errors CurrentScope.Locals.Remove("$$$$" + name); var ab = AddVariable("$$$$" + name, exp, flags, -1); cw.Emit(Op.Ctorid, frame.InternalConstructors.Count - 1); PopVar(ab); }
//An entry method for range generation. Implementation of ranges are not provided by a compiler, //instead a particular data type implement ranges by providing an instance of Enum class. Functions //succ, enumFrom and enumFromTo are explicitly invoked by this method. private void CompileRange(ElaExpression parent, ElaRange range, LabelMap map, Hints hints) { AddLinePragma(range); var snd = AddVariable(); var fst = AddVariable(); //Compile the first element which should always be present. CompileExpression(range.First, map, Hints.None, range); PopVar(fst); //If the second element is missing we will calculate it using 'succ'. if (range.Second == null) { var sv = GetFunction("succ", range); PushVar(fst); PushVar(sv); cw.Emit(Op.Call); PopVar(snd); } else { CompileExpression(range.Second, map, Hints.None, range); PopVar(snd); } //If a last element is missing we need to generate an infinite range //using 'enumFrom' function. if (range.Last == null) { var sv = GetFunction("enumFrom", range); PushVar(snd); PushVar(fst); PushVar(sv); cw.Emit(Op.Call); cw.Emit(Op.Call); } else { //An ordinary strict range. var sv = GetFunction("enumFromTo", range); PushVar(snd); PushVar(fst); CompileExpression(range.Last, map, Hints.None, range); PushVar(sv); cw.Emit(Op.Call); cw.Emit(Op.Call); cw.Emit(Op.Call); } }
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; }
//Adds a provided variable to the current scope or modifies an existing //variable with the same name (this is required because match variables unlike //regular variables in the same scope can shadow each other). private int AddMatchVariable(string varName, ElaExpression exp, out bool newV) { var sv = ScopeVar.Empty; newV = false; //Here we first check if such a name is already added and if this is the case simply fetch an //existing name. if (CurrentScope.Parent != null && CurrentScope.Parent.Locals.TryGetValue(varName, out sv) && (sv.Flags & ElaVariableFlags.Parameter) == ElaVariableFlags.Parameter) return 0 | sv.Address << 8; else { var res = CurrentScope.TryChangeVariable(varName); newV = true; if (res != -1) return 0 | res << 8; return AddVariable(varName, exp, ElaVariableFlags.None, -1); } }
internal override bool CanFollow(ElaExpression exp) { if (exp.IsIrrefutable()) return false; if (exp.Type == ElaNodeType.Juxtaposition) { var jx = (ElaJuxtaposition)exp; if (jx.Parameters.Count != Parameters.Count || jx.Target.GetName() != Target.GetName()) return true; for (var i = 0; i < Parameters.Count; i++) if (Parameters[i].CanFollow(jx.Parameters[i])) return true; return false; } return true; }
public static void PutInParens(ElaExpression e, StringBuilder sb) { if (e == null) { sb.Append("<null>"); return; } var simple = IsSimpleExpression(e); if (!simple) { sb.Append('('); e.ToString(sb, 0); sb.Append(')'); } else { e.ToString(sb, 0); } }
internal override bool CanFollow(ElaExpression pat) { if (pat.IsIrrefutable()) return false; if (pat.Type == ElaNodeType.ListLiteral) { var xs = (ElaListLiteral)pat; if (xs.Values.Count != Values.Count) return true; for (var i = 0; i < Values.Count; i++) if (Values[i].CanFollow(xs.Values[i])) return true; return false; } return true; }
internal override bool CanFollow(ElaExpression pat) { if (pat.IsIrrefutable()) return false; if (pat.Type == ElaNodeType.RecordLiteral) { var rec = (ElaRecordLiteral)pat; if (rec.Fields.Count != Fields.Count) return true; for (var i = 0; i < rec.Fields.Count; i++) if (Fields[i].CanFollow(rec.Fields[i])) return true; return false; } return true; }
internal override bool CanFollow(ElaExpression pat) { if (pat.IsIrrefutable()) return false; if (pat.Type == ElaNodeType.TupleLiteral) { var tuple = (ElaTupleLiteral)pat; if (tuple.Parameters.Count != Parameters.Count) return true; for (var i = 0; i < Parameters.Count; i++) if (Parameters[i].CanFollow(tuple.Parameters[i])) return true; return false; } return true; }
//Compiles xs parameters, can be used in all cases where xs creation is needed. private void CompileTupleParameters(ElaExpression v, List<ElaExpression> pars, LabelMap map) { //Optimize xs creates for a case of pair (a single op typeId is used). if (pars.Count == 2) { CompileExpression(pars[0], map, Hints.None, v); CompileExpression(pars[1], map, Hints.None, v); AddLinePragma(v); cw.Emit(Op.Newtup_2); } else { AddLinePragma(v); cw.Emit(Op.Newtup, pars.Count); for (var i = 0; i < pars.Count; i++) { CompileExpression(pars[i], map, Hints.None, v); cw.Emit(Op.Tupcons, i); } } }
//A main method to add named variables. private int AddVariable(string name, ElaExpression exp, ElaVariableFlags flags, int data) { //Hiding is allowed in parameter list if ((flags & ElaVariableFlags.Parameter) == ElaVariableFlags.Parameter) CurrentScope.Locals.Remove(name); else if (CurrentScope.Locals.ContainsKey(name)) { //But for other cases hiding in the same scope is not allowed CurrentScope.Locals.Remove(name); AddError(ElaCompilerError.NoHindingSameScope, exp, name.TrimStart('$')); } CurrentScope.Locals.Add(name, new ScopeVar(flags, currentCounter, data)); //Additional debug info is only generated when a debug flag is set. if (debug && exp != null) { cw.Emit(Op.Nop); AddVarPragma(name, currentCounter, cw.Offset, flags, data); AddLinePragma(exp); } return AddVariable(); }
//Compiles logical OR operator in a lazy manner. private void CompileLogicalOr(ElaExpression parent, ElaExpression left, ElaExpression right, LabelMap map, Hints hints) { //Logical OR is executed in a lazy manner var exitLab = default(Label); var termLab = default(Label); var ut = hints; if ((ut & Hints.Left) == Hints.Left) ut ^= Hints.Left; if ((ut & Hints.Tail) == Hints.Tail) ut ^= Hints.Tail; CompileExpression(left, map, ut, parent); termLab = cw.DefineLabel(); exitLab = cw.DefineLabel(); cw.Emit(Op.Brtrue, termLab); CompileExpression(right, map, ut, parent); cw.Emit(Op.Br, exitLab); cw.MarkLabel(termLab); cw.Emit(Op.PushI1_1); cw.MarkLabel(exitLab); cw.Emit(Op.Nop); }
internal virtual bool CanFollow(ElaExpression exp) { return(!exp.IsIrrefutable()); }
public static bool IsHiddenVar(ElaExpression p) { return(p != null && p.Type == ElaNodeType.NameReference && ((ElaNameReference)p).Name[0] == '$'); }
//Generates a line pragma and remembers a provided location //via lastLine and lastColumn class fields. private void AddLinePragma(ElaExpression exp) { lastLine = exp.Line; lastColumn = exp.Column; pdb.AddLineSym(cw.Offset, exp.Line, exp.Column); }
//Compiles a sequencing operator private void CompileSeq(ElaExpression parent, ElaExpression left, ElaExpression right, LabelMap map, Hints hints) { var ut = hints; if ((ut & Hints.Left) == Hints.Left) ut ^= Hints.Left; //Sequence operators forces left expression, pops it and yields a value //of a right expression. Evaliation is done in a strict order. CompileExpression(left, map, Hints.None, parent); cw.Emit(Op.Force); cw.Emit(Op.Pop); CompileExpression(right, map, ut, parent); }
internal override bool CanFollow(ElaExpression _) { return(true); }
//Tests if a given expression is a name reference of a placeholder (_). private bool IsSimplePattern(ElaExpression exp) { return exp.Type == ElaNodeType.NameReference || exp.Type == ElaNodeType.Placeholder; }
//Performs validation of overlapping for simple case of pattern matching //(such as 'match' expression with a single pattern per entry). private void ValidateOverlapSimple(IEnumerable<ElaEquation> seq, ElaExpression mexp) { var lst = new List<ElaExpression>(); foreach (var e in seq) { foreach (var o in lst) if (!e.Left.CanFollow(o)) AddWarning(ElaCompilerWarning.MatchEntryNotReachable, e.Left, FormatNode(e.Left), FormatNode(o)); lst.Add(e.Left); } }
//Builds a lists of all variables that are declared in the given pattern. private void ExtractPatternNames(ElaExpression pat, List<String> names) { switch (pat.Type) { case ElaNodeType.LazyLiteral: { //This whole method is used to extract names before making a //pattern lazy, so we need to extract all names here as well, //because this lazy literal will be compiled strict anyway (because //the whole pattern is lazy already). var vp = (ElaLazyLiteral)pat; ExtractPatternNames(vp.Expression, names); } break; case ElaNodeType.UnitLiteral: //Idle break; case ElaNodeType.As: { var asPat = (ElaAs)pat; names.Add(asPat.Name); ExtractPatternNames(asPat.Expression, names); } break; case ElaNodeType.Primitive: //Idle break; case ElaNodeType.NameReference: { var vexp = (ElaNameReference)pat; if (!vexp.Uppercase) //Uppercase is constructor names.Add(vexp.Name); } break; case ElaNodeType.RecordLiteral: { var rexp = (ElaRecordLiteral)pat; foreach (var e in rexp.Fields) if (e.FieldValue != null) ExtractPatternNames(e.FieldValue, names); } break; case ElaNodeType.TupleLiteral: { var texp = (ElaTupleLiteral)pat; foreach (var e in texp.Parameters) ExtractPatternNames(e, names); } break; case ElaNodeType.Placeholder: //Idle break; case ElaNodeType.Juxtaposition: { var hexp = (ElaJuxtaposition)pat; foreach (var e in hexp.Parameters) ExtractPatternNames(e, names); } break; case ElaNodeType.ListLiteral: { var l = (ElaListLiteral)pat; if (l.HasValues()) { foreach (var e in l.Values) ExtractPatternNames(e, names); } } break; } }
//This method contains main compilation logic for 'match' expressions and for 'try' expressions. //This method only supports a single pattern per entry. //A 'mexp' (matched expression) parameter can be null and is used for additional validation only. private void CompileSimpleMatch(IEnumerable<ElaEquation> bindings, LabelMap map, Hints hints, ElaExpression mexp) { ValidateOverlapSimple(bindings, mexp); var failLab = cw.DefineLabel(); var endLab = cw.DefineLabel(); //We need to remembers the first set of system addresses because we will have //to push them manually for the entries other than first. var firstSys = -1; var first = true; //Loops through all bindings //For the first iteration we assume that all values are already on stack. //For the next iteration we manually push all values. foreach (var b in bindings) { //Each match entry starts a lexical scope StartScope(false, b.Line, b.Column); //For second+ entries we put a label where we would jump if a previous //pattern fails if (!first) { cw.MarkLabel(failLab); failLab = cw.DefineLabel(); } var p = b.Left; var sysVar = -1; //We apply an optimization if a we have a name reference (only for the first iteration). if (p.Type == ElaNodeType.NameReference && first) sysVar = AddVariable(p.GetName(), p, ElaVariableFlags.Parameter, -1); else sysVar = AddVariable(); //Create an unnamed system variable //This is not the first entry, so we have to push values first if (!first) PushVar(firstSys); PopVar(sysVar); //We have to remember addresses calculated in a first iteration //to be able to push them once again in a second iteration. if (first) firstSys = sysVar; CompilePattern(sysVar, p, failLab, false /*allowBang*/, false /*forceStrict*/); first = false; //Compile entry expression if (b.Right == null) AddError(ElaCompilerError.InvalidMatchEntry, b.Left, FormatNode(b)); else CompileExpression(b.Right, map, Hints.None, b); //Close current scope EndScope(); //If everything is OK skip through 'fail' clause cw.Emit(Op.Br, endLab); } //We fall here if all patterns have failed cw.MarkLabel(failLab); //If this hints is set than we generate a match for a 'try' //(exception handling) block. Instead of MatchFailed we need //to rethrow an original exception. Block 'try' always have //just a single expression to match, therefore firstSys[0] is //pretty safe here. if ((hints & Hints.Throw) == Hints.Throw) { PushVar(firstSys); cw.Emit(Op.Rethrow); } else cw.Emit(Op.Failwith, (Int32)ElaRuntimeError.MatchFailed); //Happy path for match cw.MarkLabel(endLab); cw.Emit(Op.Nop); }
//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; } }
//Compiles a pattern as lazy by creating a thunk an initializing all pattern names with these thunks. private void CompileLazyPattern(int sys, ElaExpression pat, bool allowBang) { //First we need to obtain a list of all names, that declared in this pattern. var names = new List<String>(); ExtractPatternNames(pat, names); //Walk through all names and create thunks for (var i = 0; i < names.Count; i++) { var n = names[i]; Label funSkipLabel; int address; LabelMap newMap; CompileFunctionProlog(null, 1, pat.Line, pat.Column, out funSkipLabel, out address, out newMap); var next = cw.DefineLabel(); var exit = cw.DefineLabel(); //As soon as already compiling pattern as lazy, we should enforce strictness even if //pattern is declared as lazy. Otherwise everything would crash, because here assume //that names are bound in this bound in the same scope. CompilePattern(1 | ((sys >> 8) << 8), pat, next, allowBang, true /*forceStrict*/); cw.Emit(Op.Br, exit); cw.MarkLabel(next); cw.Emit(Op.Failwith, (Int32)ElaRuntimeError.MatchFailed); cw.MarkLabel(exit); cw.Emit(Op.Nop); //Here we expect that our target variable is already declared by a pattern in the //same physical scope - so we can obtain and push it as a return value. var sv = GetVariable(n, pat.Line, pat.Column); PushVar(sv); CompileFunctionEpilog(null, 1, address, funSkipLabel); cw.Emit(Op.Newlazy); ScopeVar var; if (CurrentScope.Locals.TryGetValue(n, out var)) { //If it doesn't have a NoInit flag we are not good if ((var.Flags & ElaVariableFlags.NoInit) == ElaVariableFlags.NoInit) { PopVar(var.Address = 0 | var.Address << 8); //Aligning it to local scope CurrentScope.RemoveFlags(n, ElaVariableFlags.NoInit); } } } }
//Performs a type check and throws and error if the type is not as expected. //This method assumes that a value to be checked is on the top of the stack. private ScopeVar TypeCheckConstructor(string cons, string prefix, string name, ElaExpression par, bool force) { var sv = EmitSpecName(prefix, "$$" + name, par, ElaCompilerError.UndefinedType); TypeCheckConstructorAllStack(cons, force); return sv; }
//Perform an eta expansion for a given expression private void EtaExpand(string name, ElaExpression exp, LabelMap map, int args) { //Here we generate a function which has a provided number of //arguments if (name != null) StartFun(name, args); StartSection(); StartScope(true, exp.Line, exp.Column); cw.StartFrame(args); var funSkipLabel = cw.DefineLabel(); cw.Emit(Op.Br, funSkipLabel); var address = cw.Offset; if (exp.Type != ElaNodeType.Equation) CompileExpression(exp, map, Hints.None, null); else CompileFunction((ElaEquation)exp); //Functions are curried so generate a call for each argument for (var i = 0; i < args; i++) cw.Emit(Op.Call); cw.Emit(Op.Ret); frame.Layouts.Add(new MemoryLayout(currentCounter, cw.FinishFrame(), address)); EndSection(); EndScope(); if (name != null) EndFun(frame.Layouts.Count - 1); cw.MarkLabel(funSkipLabel); cw.Emit(Op.PushI4, args); cw.Emit(Op.Newfun, frame.Layouts.Count - 1); }
internal override bool CanFollow(ElaExpression exp) { return(Expression.CanFollow(exp)); }