private ExprData CompileLazy(ElaLazyLiteral exp, LabelMap map, Hints hints) { var ed = default(ExprData); //Try to optimize lazy section for a case //when a function application is marked as lazy if (!TryOptimizeLazy(exp, map, hints)) { //Regular lazy section compilation //Create a closure around section ed = CompileLazyExpression(exp.Expression, map, hints); } if ((hints & Hints.Left) == Hints.Left) AddValueNotUsed(exp); return ed; }
//This methods tries to optimize lazy section. It would only work when a lazy //section if a function application that result in saturation (no partial applications) //allowed. In such a case this method eliminates "double" function call (which would be //the result of a regular compilation logic). If this method fails than regular compilation //logic is used. private bool TryOptimizeLazy(ElaLazyLiteral lazy, LabelMap map, Hints hints) { var body = default(ElaExpression); //Only function application is accepted if ((body = lazy.Expression).Type != ElaNodeType.Juxtaposition) return false; var funCall = (ElaJuxtaposition)body; //If a target is not a variable we can't check what is actually called if (funCall.Target.Type != ElaNodeType.NameReference) return false; var varRef = (ElaNameReference)funCall.Target; var scopeVar = GetVariable(varRef.Name, varRef.Line, varRef.Column); var len = funCall.Parameters.Count; //Only one parameter is allowed if (len > 1) return false; //If a target is not function we can't optimize it if ((scopeVar.VariableFlags & ElaVariableFlags.Function) != ElaVariableFlags.Function) return false; //Only saturation case is optimized if (scopeVar.Data != funCall.Parameters.Count) return false; //We can only optimize a thunk if a last parameter (that will be executed in a strict manner) //is either a primitive value or an already initialized variable. for (var i = 0; i < len; i++) { var p = funCall.Parameters[i]; //Need to check if variable is already initialized. if (p.Type == ElaNodeType.NameReference) { var ssv = GetVariable(p.GetName(), CurrentScope, GetFlags.NoError, 0, 0); if ((ssv.Flags & ElaVariableFlags.NoInit) == ElaVariableFlags.NoInit) return false; } else if (p.Type != ElaNodeType.Primitive) return false; } for (var i = 0; i < len; i++) CompileExpression(funCall.Parameters[len - i - 1], map, Hints.None, funCall); var sl = len - 1; AddLinePragma(varRef); PushVar(scopeVar); //We partially apply function and create a new function for (var i = 0; i < sl; i++) cw.Emit(Op.Call); AddLinePragma(lazy); //LazyCall uses a function provided to create a thunk //and remembers last function arguments as ElaFunction.LastParameter cw.Emit(Op.LazyCall, len); return true; }
void LazyExpr(out ElaExpression exp) { Expect(63); var lazy = new ElaLazyLiteral(t); Expr(out exp); lazy.Expression = exp; exp = lazy; }
private ElaExpression ValidateDoBlock(ElaExpression exp) { if (exp.Type == ElaNodeType.Juxtaposition && ((ElaJuxtaposition)exp).Parameters[0] == null) { var ext = ((ElaJuxtaposition)exp).Parameters[1]; var ctx = default(ElaContext); if (ext.Type == ElaNodeType.Context) { ctx = (ElaContext)ext; ext = ctx.Expression; } var eqt = new ElaJuxtaposition { Spec = true }; eqt.SetLinePragma(exp.Line, exp.Column); eqt.Target = new ElaNameReference(t) { Name = ">>=" }; eqt.Parameters.Add(ext); var jux = new ElaJuxtaposition(); jux.SetLinePragma(exp.Line, exp.Column); jux.Target = new ElaNameReference { Name = "point" }; jux.Parameters.Add(new ElaUnitLiteral()); eqt.Parameters.Add(new ElaLambda { Left = new ElaPlaceholder(), Right = jux }); if (ctx != null) { ctx.Expression = eqt; exp = ctx; } else exp = eqt; } var root = exp; while (true) { if (exp.Type == ElaNodeType.Juxtaposition) { var juxta = (ElaJuxtaposition)exp; if (juxta.Parameters.Count == 2) { juxta.Parameters[0] = Reduce(juxta.Parameters[0], juxta); juxta.Parameters[1] = Reduce(juxta.Parameters[1], juxta); exp = juxta.Parameters[1]; } else break; } else if (exp.Type == ElaNodeType.LetBinding) { var lb = (ElaLetBinding)exp; lb.Expression = Reduce(lb.Expression, lb); exp = lb.Expression; } else if (exp.Type == ElaNodeType.Lambda) { var lb = (ElaLambda)exp; lb.Right = Reduce(lb.Right, lb); if (lb.Left.Type != ElaNodeType.NameReference && lb.Left.Type != ElaNodeType.Placeholder) { var em = new ElaMatch(); em.SetLinePragma(lb.Left.Line, lb.Left.Column); em.Expression = new ElaNameReference { Name = "$x01" }; em.Entries = new ElaEquationSet(); var eq1 = new ElaEquation(); eq1.SetLinePragma(lb.Left.Line, lb.Left.Column); eq1.Left = lb.Left; eq1.Right = lb.Right; em.Entries.Equations.Add(eq1); var eq2 = new ElaEquation(); eq2.SetLinePragma(lb.Left.Line, lb.Left.Column); eq2.Left = new ElaNameReference { Name = "$x02" }; var errExp = new ElaJuxtaposition(); errExp.SetLinePragma(lb.Left.Line, lb.Left.Column); errExp.Target = new ElaNameReference { Name = "failure" }; errExp.Parameters.Add(new ElaNameReference { Name = "$x02" }); eq2.Right = errExp; em.Entries.Equations.Add(eq2); lb.Left = new ElaNameReference { Name = "$x01" }; lb.Right = em; exp = lb; } else exp = lb.Right; } else break; } var ret = new ElaLazyLiteral { Expression = root }; ret.SetLinePragma(root.Line, root.Column); return ret; }