internal IOperand CompileIFFLAG([NotNull] IRoutineBuilder rb, [NotNull] ZilListoidBase clauses, [NotNull] ISourceLine src, bool wantResult, [CanBeNull] IVariable resultStorage) { resultStorage = resultStorage ?? rb.Stack; while (!clauses.IsEmpty) { ZilObject clause; (clause, clauses) = clauses; if (!(clause is ZilListoidBase list) || list.IsEmpty) { throw new CompilerError(CompilerMessages.All_Clauses_In_0_Must_Be_Lists, "IFFLAG"); } var(flag, body) = list; ZilObject value; bool match, isElse = false; ZilAtom shadyElseAtom = null; switch (flag) { case ZilAtom atom when(value = Context.GetCompilationFlagValue(atom)) != null: case ZilString str when(value = Context.GetCompilationFlagValue(str.Text)) != null: // name of a defined compilation flag match = value.IsTrue; break; case ZilForm form: form = Subrs.SubstituteIfflagForm(Context, form); match = ((ZilObject)form.Eval(Context)).IsTrue; break; case ZilAtom atom when atom.StdAtom != StdAtom.ELSE && atom.StdAtom != StdAtom.T: shadyElseAtom = atom; goto default; default: match = isElse = true; break; } // does this clause match? if (!match) { continue; } // emit code for clause var clauseResult = CompileClauseBody(rb, body, wantResult, resultStorage); // warn if this is an else clause and there are more clauses below if (!isElse || clauses.IsEmpty) { return(wantResult ? clauseResult : null); } var warning = new CompilerError(src, CompilerMessages._0_Clauses_After_Else_Part_Will_Never_Be_Evaluated, "IFFLAG"); if (shadyElseAtom != null) { // if the else clause wasn't introduced with ELSE or T, it might not have been meant as an else clause warning = warning.Combine(new CompilerError( flag.SourceLine, CompilerMessages.Undeclared_Compilation_Flag_0, shadyElseAtom)); } Context.HandleError(warning); return(wantResult ? clauseResult : null); } // no matching clauses if (wantResult) { rb.EmitStore(resultStorage, Game.Zero); } return(wantResult ? resultStorage : 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); } } }