public IOperand CompileAsOperand([NotNull] IRoutineBuilder rb, [NotNull] ZilObject expr, [NotNull] ISourceLine src, [CanBeNull] IVariable suggestion = null) { expr = expr.Unwrap(Context); var constant = CompileConstant(expr, AmbiguousConstantMode.Pessimistic); if (constant != null) { return(constant); } switch (expr.StdTypeAtom) { case StdAtom.FORM: return(CompileForm(rb, (ZilForm)expr, true, suggestion ?? rb.Stack)); case StdAtom.ATOM: var atom = (ZilAtom)expr; if (Globals.ContainsKey(atom)) { Context.HandleError(new CompilerError(src, CompilerMessages.Bare_Atom_0_Interpreted_As_Global_Variable_Index, atom)); return(Globals[atom].Indirect); } if (SoftGlobals.ContainsKey(atom)) { Context.HandleError(new CompilerError( expr.SourceLine ?? src, CompilerMessages.Soft_Variable_0_May_Not_Be_Used_Here, atom)); } else { Context.HandleError(new CompilerError( expr.SourceLine ?? src, CompilerMessages.Bare_Atom_0_Used_As_Operand_Is_Not_A_Global_Variable, atom)); } return(Game.Zero); default: Context.HandleError(new CompilerError( expr.SourceLine ?? src, CompilerMessages.Expressions_Of_This_Type_Cannot_Be_Compiled)); return(Game.Zero); } }
// TODO: replace CompileStmt with CompileForm and (in loops) CompileClauseBody void CompileStmt([NotNull] IRoutineBuilder rb, [NotNull] ZilObject stmt, bool wantResult) { stmt = stmt.Unwrap(Context); switch (stmt) { case ZilForm form: MarkSequencePoint(rb, form); var result = CompileForm(rb, form, wantResult, null); if (wantResult) { rb.Return(result); } break; case ZilList _: throw new CompilerError(stmt, CompilerMessages.Expressions_Of_This_Type_Cannot_Be_Compiled) .Combine(new CompilerError(CompilerMessages.Misplaced_Bracket_In_COND_Or_Loop)); default: if (wantResult) { var value = CompileConstant(stmt); if (value == null) { // TODO: show "expressions of this type cannot be compiled" warning even if wantResult is false? throw new CompilerError(stmt, CompilerMessages.Expressions_Of_This_Type_Cannot_Be_Compiled); } rb.Return(value); } break; } }
public IOperand CompileConstant([NotNull] ZilObject expr, AmbiguousConstantMode mode) { switch (expr.Unwrap(Context)) { case ZilFix fix: return(Game.MakeOperand(fix.Value)); case ZilHash hash when hash.StdTypeAtom == StdAtom.BYTE && hash.GetPrimitive(Context) is ZilFix fix: return(Game.MakeOperand(fix.Value)); case ZilWord word: return(CompileConstant(word.Value)); case ZilString str: return(Game.MakeOperand(TranslateString(str, Context))); case ZilChar ch: return(Game.MakeOperand((byte)ch.Char)); case ZilAtom atom: if (atom.StdAtom == StdAtom.T) { return(Game.One); } if (Routines.TryGetValue(atom, out var routine)) { return(routine); } if (Objects.TryGetValue(atom, out var obj)) { return(obj); } if (Constants.TryGetValue(atom, out var operand)) { return(operand); } if (mode == AmbiguousConstantMode.Optimistic && Globals.TryGetValue(atom, out var global)) { Context.HandleError(new CompilerError((ISourceLine)null, CompilerMessages.Bare_Atom_0_Interpreted_As_Global_Variable_Index, atom)); return(global); } return(null); case ZilFalse _: return(Game.Zero); case ZilTable table: if (Tables.TryGetValue(table, out var tb)) { return(tb); } tb = Game.DefineTable(table.Name, true); Tables.Add(table, tb); return(tb); case ZilConstant constant: return(CompileConstant(constant.Value)); case ZilForm form: return(form.IsGVAL(out var globalAtom) ? CompileConstant(globalAtom, AmbiguousConstantMode.Pessimistic) : null); case ZilHash hash when hash.StdTypeAtom == StdAtom.VOC && hash.GetPrimitive(Context) is ZilAtom primAtom: var wordAtom = ZilAtom.Parse("W?" + primAtom.Text, Context); if (Constants.TryGetValue(wordAtom, out operand)) { return(operand); } return(null); default: var primitive = expr.GetPrimitive(Context); if (primitive != expr && primitive.GetTypeAtom(Context) != expr.GetTypeAtom(Context)) { return(CompileConstant(primitive)); } return(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); } }