internal IOperand CompileForm([NotNull] IRoutineBuilder rb, [NotNull] ZilForm form, bool wantResult, IVariable resultStorage) { using (DiagnosticContext.Push(form.SourceLine)) { var unwrapped = form.Unwrap(Context); if (!ReferenceEquals(unwrapped, form)) { switch (unwrapped) { case ZilForm newForm: form = newForm; break; default: return(wantResult ? CompileAsOperand(rb, unwrapped, form.SourceLine, resultStorage) : null); } } if (!(form.First is ZilAtom head)) { Context.HandleError(new CompilerError(form, CompilerMessages.FORM_Must_Start_With_An_Atom)); return(wantResult ? Game.Zero : null); } // built-in statements handled by ZBuiltins var zversion = Context.ZEnvironment.ZVersion; Debug.Assert(form.Rest != null); var argCount = form.Rest.Count(); if (wantResult) { // prefer the value version, then value+predicate, predicate, void if (ZBuiltins.IsBuiltinValueCall(head.Text, zversion, argCount)) { return(ZBuiltins.CompileValueCall(head.Text, this, rb, form, resultStorage)); } if (ZBuiltins.IsBuiltinValuePredCall(head.Text, zversion, argCount)) { var label1 = rb.DefineLabel(); resultStorage = resultStorage ?? rb.Stack; ZBuiltins.CompileValuePredCall(head.Text, this, rb, form, resultStorage, label1, true); rb.MarkLabel(label1); return(resultStorage); } if (ZBuiltins.IsBuiltinPredCall(head.Text, zversion, argCount)) { var label1 = rb.DefineLabel(); var label2 = rb.DefineLabel(); resultStorage = resultStorage ?? rb.Stack; ZBuiltins.CompilePredCall(head.Text, this, rb, form, label1, true); rb.EmitStore(resultStorage, Game.Zero); rb.Branch(label2); rb.MarkLabel(label1); rb.EmitStore(resultStorage, Game.One); rb.MarkLabel(label2); return(resultStorage); } if (ZBuiltins.IsBuiltinVoidCall(head.Text, zversion, argCount)) { ZBuiltins.CompileVoidCall(head.Text, this, rb, form); return(Game.One); } } else { // prefer the void version, then predicate, value, value+predicate // (predicate saves a cleanup instruction) if (ZBuiltins.IsBuiltinVoidCall(head.Text, zversion, argCount)) { ZBuiltins.CompileVoidCall(head.Text, this, rb, form); return(null); } if (ZBuiltins.IsBuiltinPredCall(head.Text, zversion, argCount)) { var dummy = rb.DefineLabel(); ZBuiltins.CompilePredCall(head.Text, this, rb, form, dummy, true); rb.MarkLabel(dummy); return(null); } if (ZBuiltins.IsBuiltinValueCall(head.Text, zversion, argCount)) { if (ZBuiltins.CompileValueCall(head.Text, this, rb, form, null) == rb.Stack) { rb.EmitPopStack(); } return(null); } if (ZBuiltins.IsBuiltinValuePredCall(head.Text, zversion, argCount)) { var label1 = rb.DefineLabel(); ZBuiltins.CompileValuePredCall(head.Text, this, rb, form, rb.Stack, label1, true); rb.MarkLabel(label1); rb.EmitPopStack(); return(null); } } // routine calls var obj = Context.GetZVal(Context.ZEnvironment.InternGlobalName(head)); while (obj is ZilConstant cnst) { obj = cnst.Value; } switch (obj) { case ZilRoutine rtn: // check argument count var args = form.Skip(1).ToArray(); if (args.Length < rtn.ArgSpec.MinArgCount || rtn.ArgSpec.MaxArgCount != null && args.Length > rtn.ArgSpec.MaxArgCount) { Context.HandleError(CompilerError.WrongArgCount( rtn.Name?.ToString() ?? "<unnamed routine>", new ArgCountRange(rtn.ArgSpec.MinArgCount, rtn.ArgSpec.MaxArgCount))); return(wantResult ? Game.Zero : null); } // compile routine call resultStorage = wantResult ? (resultStorage ?? rb.Stack) : null; using (var argOperands = CompileOperands(rb, form.SourceLine, args)) { rb.EmitCall(Routines[head], argOperands.AsArray(), resultStorage); } return(resultStorage); case ZilFalse _: // this always returns 0. we can eliminate the call if none of the arguments have side effects. var argsWithSideEffects = form.Skip(1).Where(HasSideEffects).ToArray(); if (argsWithSideEffects.Length <= 0) { return(Game.Zero); } resultStorage = wantResult ? (resultStorage ?? rb.Stack) : null; using (var argOperands = CompileOperands(rb, form.SourceLine, argsWithSideEffects)) { var operands = argOperands.AsArray(); if (operands.Any(o => o == rb.Stack)) { rb.EmitCall(Game.Zero, operands.Where(o => o == rb.Stack).ToArray(), resultStorage); } } return(resultStorage); default: // unrecognized if (!ZBuiltins.IsNearMatchBuiltin(head.Text, zversion, argCount, out var error)) { error = new CompilerError(CompilerMessages.Unrecognized_0_1, "routine or instruction", head); } Context.HandleError(error); return(wantResult ? Game.Zero : null); } } }
internal void CompileCondition([NotNull] IRoutineBuilder rb, [NotNull] ZilObject expr, [NotNull] ISourceLine src, [NotNull] ILabel label, bool polarity) { expr = expr.Unwrap(Context); var type = expr.StdTypeAtom; // ReSharper disable once SwitchStatementMissingSomeCases switch (type) { case StdAtom.FALSE: if (polarity == false) { rb.Branch(label); } return; case StdAtom.ATOM: var atom = (ZilAtom)expr; if (atom.StdAtom != StdAtom.T && atom.StdAtom != StdAtom.ELSE) { // could be a missing , or . before variable name var warning = new CompilerError(src, CompilerMessages.Bare_Atom_0_Treated_As_True_Here, expr); if (Locals.ContainsKey(atom) || Globals.ContainsKey(atom)) { warning = warning.Combine(new CompilerError(src, CompilerMessages.Did_You_Mean_The_Variable)); } Context.HandleError(warning); } if (polarity) { rb.Branch(label); } return; case StdAtom.FIX: bool nonzero = ((ZilFix)expr).Value != 0; if (polarity == nonzero) { rb.Branch(label); } return; case StdAtom.FORM: // handled below break; default: Context.HandleError(new CompilerError(expr.SourceLine ?? src, CompilerMessages.Expressions_Of_This_Type_Cannot_Be_Compiled)); return; } // it's a FORM var form = (ZilForm)expr; if (!(form.First is ZilAtom head)) { Context.HandleError(new CompilerError(form, CompilerMessages.FORM_Must_Start_With_An_Atom)); return; } // check for standard built-ins // prefer the predicate version, then value, value+predicate, void // (value+predicate is hard to clean up) var zversion = Context.ZEnvironment.ZVersion; var argCount = form.Count() - 1; if (ZBuiltins.IsBuiltinPredCall(head.Text, zversion, argCount)) { ZBuiltins.CompilePredCall(head.Text, this, rb, form, label, polarity); return; } if (ZBuiltins.IsBuiltinValueCall(head.Text, zversion, argCount)) { var result = ZBuiltins.CompileValueCall(head.Text, this, rb, form, rb.Stack); BranchIfNonZero(result); return; } if (ZBuiltins.IsBuiltinValuePredCall(head.Text, zversion, argCount)) { if (rb.CleanStack) { /* wasting the branch and checking the result with ZERO? is more efficient * than using the branch and having to clean the result off the stack */ var noBranch = rb.DefineLabel(); ZBuiltins.CompileValuePredCall(head.Text, this, rb, form, rb.Stack, noBranch, true); rb.MarkLabel(noBranch); rb.BranchIfZero(rb.Stack, label, !polarity); } else { ZBuiltins.CompileValuePredCall(head.Text, this, rb, form, rb.Stack, label, polarity); } return; } if (ZBuiltins.IsBuiltinVoidCall(head.Text, zversion, argCount)) { ZBuiltins.CompileVoidCall(head.Text, this, rb, form); // void calls return true if (polarity) { rb.Branch(label); } return; } // special cases var op1 = CompileAsOperand(rb, form, form.SourceLine); BranchIfNonZero(op1); void BranchIfNonZero(IOperand operand) { if (operand is INumericOperand numericResult) { if (numericResult.Value != 0 == polarity) { rb.Branch(label); } } else { rb.BranchIfZero(operand, label, !polarity); } } }
internal IOperand CompileAsOperandWithBranch([NotNull] IRoutineBuilder rb, [NotNull] ZilObject expr, [CanBeNull] IVariable resultStorage, [NotNull] ILabel label, bool polarity, [CanBeNull][InstantHandle] Func <IVariable> tempVarProvider = null) { expr = expr.Unwrap(Context); IOperand result = resultStorage; switch (expr) { case ZilFalse _: if (resultStorage == null) { result = Game.Zero; } else { rb.EmitStore(resultStorage, Game.Zero); } if (polarity == false) { rb.Branch(label); } return(result); case ZilFix fix: if (resultStorage == null) { result = Game.MakeOperand(fix.Value); } else { rb.EmitStore(resultStorage, Game.MakeOperand(fix.Value)); } bool nonzero = fix.Value != 0; if (polarity == nonzero) { rb.Branch(label); } return(result); case ZilForm form: if (!(form.First is ZilAtom head)) { Context.HandleError(new CompilerError(form, CompilerMessages.FORM_Must_Start_With_An_Atom)); return(Game.Zero); } // check for standard built-ins // prefer the value+predicate version, then value, predicate, void var zversion = Context.ZEnvironment.ZVersion; var argCount = form.Count() - 1; if (ZBuiltins.IsBuiltinValuePredCall(head.Text, zversion, argCount)) { if (resultStorage == null) { Debug.Assert(tempVarProvider != null); resultStorage = tempVarProvider(); } ZBuiltins.CompileValuePredCall(head.Text, this, rb, form, resultStorage, label, polarity); return(resultStorage); } if (ZBuiltins.IsBuiltinValueCall(head.Text, zversion, argCount)) { result = ZBuiltins.CompileValueCall(head.Text, this, rb, form, resultStorage); if (resultStorage != null && resultStorage != result) { rb.EmitStore(resultStorage, result); result = resultStorage; } else if (resultStorage == null && result == rb.Stack) { Debug.Assert(tempVarProvider != null); resultStorage = tempVarProvider(); rb.EmitStore(resultStorage, result); result = resultStorage; } rb.BranchIfZero(result, label, !polarity); return(result); } if (ZBuiltins.IsBuiltinPredCall(head.Text, zversion, argCount)) { if (resultStorage == null) { Debug.Assert(tempVarProvider != null); resultStorage = tempVarProvider(); } var label1 = rb.DefineLabel(); var label2 = rb.DefineLabel(); ZBuiltins.CompilePredCall(head.Text, this, rb, form, label1, true); rb.EmitStore(resultStorage, Game.Zero); rb.Branch(polarity ? label2 : label); rb.MarkLabel(label1); rb.EmitStore(resultStorage, Game.One); if (polarity) { rb.Branch(label); } rb.MarkLabel(label2); return(resultStorage); } if (ZBuiltins.IsBuiltinVoidCall(head.Text, zversion, argCount)) { ZBuiltins.CompileVoidCall(head.Text, this, rb, form); // void calls return true if (resultStorage == null) { result = Game.One; } else { rb.EmitStore(resultStorage, Game.One); } if (polarity) { rb.Branch(label); } return(result); } // for anything more complicated, treat it as a value result = CompileAsOperand(rb, form, form.SourceLine, resultStorage); if (resultStorage != null && resultStorage != result) { rb.EmitStore(resultStorage, result); result = resultStorage; } else if (resultStorage == null && result == rb.Stack) { Debug.Assert(tempVarProvider != null); resultStorage = tempVarProvider(); rb.EmitStore(resultStorage, result); result = resultStorage; } rb.BranchIfZero(result, label, !polarity); return(result); default: var constValue = CompileConstant(expr); if (constValue == null) { Context.HandleError(new CompilerError(expr, CompilerMessages.Expressions_Of_This_Type_Cannot_Be_Compiled)); return(Game.Zero); } else { if (resultStorage == null) { result = constValue; } else { rb.EmitStore(resultStorage, constValue); } if (polarity) { rb.Branch(label); } } return(result); } }