//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); } } } }
//Compile all declarations including function bindings, name bindings and bindings //defined by pattern matching. private void CompileDeclaration(ElaEquation s, LabelMap map, Hints hints) { //Check for some errors ValidateBinding(s); var partial = (s.VariableFlags & ElaVariableFlags.PartiallyApplied) == ElaVariableFlags.PartiallyApplied; var fun = partial || s.IsFunction(); if ((s.Left.Type != ElaNodeType.NameReference || ((ElaNameReference)s.Left).Uppercase) && !fun) { if ((hints & Hints.Lazy) == Hints.Lazy) { CompileLazyBindingPattern(s, map); } else { CompileBindingPattern(s, map); } } else { var nm = default(String); var sv = GetNoInitVariable(s, out nm); var addr = sv.Address; if (sv.IsEmpty()) { addr = AddVariable(s.Left.GetName(), s, s.VariableFlags, -1); } else { CurrentScope.AddFlags(nm, ElaVariableFlags.Self); } //Compile expression and write it to a variable if (fun) { //*****CURRENTLY NOT USED! //Here we automatically eta expand functions defined through partial application if (partial) { EtaExpand(s.Left.GetName(), s.Right, map, s.Arguments); } else if (CompileFunction(s, map)) { CurrentScope.AddFlags(nm, ElaVariableFlags.Clean); //A function is clean } } else { map.BindingName = s.Left.GetName(); if ((hints & Hints.Lazy) == Hints.Lazy) { CompileLazyExpression(s.Right, map, Hints.None); } else { var ed = CompileExpression(s.Right, map, Hints.None, s); if (ed.Type == DataKind.Builtin) { CurrentScope.AddFlagsAndData(nm, ElaVariableFlags.Builtin, ed.Data); } } } //Now, when done initialization, when can remove NoInit flags. if (!sv.IsEmpty()) { CurrentScope.RemoveFlags(nm, ElaVariableFlags.NoInit | ElaVariableFlags.Self); } AddLinePragma(s); PopVar(addr); } }