protected override void EmitAfterBody(IRoutineBuilder rb, ILabel againLabel) { // increment bool down; if (inc != null) { int incValue; if (inc is ZilFix fix && (incValue = fix.Value) < 0) { rb.EmitBinary(BinaryOp.Sub, counter, cc.Game.MakeOperand(-incValue), counter); down = true; } else if (inc.IsNonVariableForm()) { var operand = cc.CompileAsOperand(rb, inc, src, counter); if (operand != counter) { rb.EmitStore(counter, operand); } down = false; } else { var operand = cc.CompileAsOperand(rb, inc, src); rb.EmitBinary(BinaryOp.Add, counter, operand, counter); down = false; } }
public VoidCall([NotNull] Compilation cc, [NotNull] IRoutineBuilder rb, [NotNull] ZilForm form) : this() { this.cc = cc; this.rb = rb; this.form = form; }
public ValueCall([NotNull] Compilation cc, [NotNull] IRoutineBuilder rb, [NotNull] ZilForm form, [NotNull] IVariable resultStorage) : this() { this.cc = cc; this.rb = rb; this.form = form; this.resultStorage = resultStorage; }
public void BeforeBlock(IRoutineBuilder rb, Block block, ILabel exhaustedLabel) { this.rb = rb; this.againLabel = block.AgainLabel; this.exhaustedLabel = exhaustedLabel; EmitBeforeBlock(rb, exhaustedLabel); }
Action ValidateAction([NotNull] Dictionary <ZilAtom, Action> actions, [NotNull] Syntax line) { try { using (DiagnosticContext.Push(line.SourceLine)) { if (actions.TryGetValue(line.ActionName, out var act) == false) { if (Routines.TryGetValue(line.Action, out var routine) == false) { throw new CompilerError(CompilerMessages.Undefined_0_1, "action routine", line.Action); } IRoutineBuilder preRoutine = null; if (line.Preaction != null && Routines.TryGetValue(line.Preaction, out preRoutine) == false) { throw new CompilerError(CompilerMessages.Undefined_0_1, "preaction routine", line.Preaction); } var actionName = line.ActionName; int index = Context.ZEnvironment.NextAction++; if (index >= Context.ZEnvironment.VocabFormat.MaxActionCount) { throw new InterpreterError( InterpreterMessages.Too_Many_0_Only_1_Allowed_In_This_Vocab_Format, "actions", Context.ZEnvironment.VocabFormat.MaxActionCount); } var number = Game.MakeOperand(index); var constant = Game.DefineConstant(actionName.Text, number); Constants.Add(actionName, constant); if (WantDebugInfo) { Debug.Assert(Game.DebugFile != null); Game.DebugFile.MarkAction(constant, actionName.Text); } act = new Action(index, constant, routine, preRoutine, line.Action, line.Preaction); actions.Add(actionName, act); } else { WarnIfActionRoutineDiffers(line, "action routine", line.Action, act.RoutineName); WarnIfActionRoutineDiffers(line, "preaction routine", line.Preaction, act.PreRoutineName); } return(act); } } catch (ZilError ex) { Context.HandleError(ex); return(null); } }
public PredCall([NotNull] Compilation cc, [NotNull] IRoutineBuilder rb, [NotNull] ZilForm form, [NotNull] ILabel label, bool polarity) : this() { this.cc = cc; this.rb = rb; this.form = form; this.label = label; this.polarity = polarity; }
public Action(int index, IOperand constant, IRoutineBuilder routine, IRoutineBuilder preRoutine, ZilAtom routineName, ZilAtom preRoutineName) { Index = index; Constant = constant; Routine = routine; RoutineName = routineName; PreRoutine = preRoutine; PreRoutineName = preRoutineName; }
IOperand CompileClauseBody([NotNull] IRoutineBuilder rb, [NotNull] ZilListoidBase clause, bool wantResult, [CanBeNull] IVariable resultStorage) { if (clause.IsEmpty) { return(Game.One); } IOperand result = null; do { var(first, rest) = clause; // only want the result of the last statement (if any) bool wantThisResult = wantResult && rest.IsEmpty; var stmt = first.Unwrap(Context); switch (stmt) { case ZilForm form: MarkSequencePoint(rb, form); result = CompileForm( rb, form, wantThisResult, wantThisResult ? resultStorage : null); 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 (wantThisResult) { result = CompileConstant(stmt); if (result == 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); } } break; } clause = rest; } while (!clause.IsEmpty); return(result); }
protected override void EmitBeforeBlock(IRoutineBuilder rb, ILabel exhaustedLabel) { // initialize counter this.counter = cc.PushInnerLocal(rb, atom, LocalBindingType.LoopState, src); var operand = cc.CompileAsOperand(rb, start, src, counter); if (operand != counter) { rb.EmitStore(counter, operand); } }
void MarkSequencePoint([NotNull] IRoutineBuilder rb, [NotNull] IProvideSourceLine node) { if (!WantDebugInfo || !(node.SourceLine is FileSourceLine fileSourceLine)) { return; } Debug.Assert(Game.DebugFile != null); Game.DebugFile.MarkSequencePoint(rb, new DebugLineRef(fileSourceLine.FileName, fileSourceLine.Line, 1)); }
public ValuePredCall([NotNull] Compilation cc, [NotNull] IRoutineBuilder rb, [NotNull] ZilForm form, [NotNull] IVariable resultStorage, [NotNull] ILabel label, bool polarity) : this() { this.cc = cc; this.rb = rb; this.form = form; this.resultStorage = resultStorage; this.label = label; this.polarity = polarity; }
protected override void EmitBeforeBody(IRoutineBuilder rb, ILabel againLabel, ILabel exhaustedLabel) { // test and branch before the body, if end is a (non-[GL]VAL) FORM if (end.IsNonVariableForm()) { cc.CompileCondition(rb, end, end.SourceLine, exhaustedLabel, true); precheck = true; } else { precheck = false; } }
public void Run(IRoutineBuilder routine, Action <IRoutineResult> callback) { this.routine = routine; this.callback = callback; try { caller.StartCoroutine(Run()); } catch (Exception e) { Debug.LogError($"{e.Message} {e.InnerException?.Message}"); } }
private IEnumerator Run() { Reset(); caller.StartCoroutine(routine.RunCoroutine(this)); do { yield return(null); } while (routine != null && routine.IsRunning); callback?.Invoke(routine.Result); this.routine = null; this.callback = null; }
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); } }
public ILocalBuilder PushInnerLocal([NotNull] IRoutineBuilder rb, [NotNull] ZilAtom atom, LocalBindingType reason, ISourceLine src) { if (Locals.TryGetValue(atom, out var prev)) { // save the old binding if (OuterLocals.TryGetValue(atom, out var stk) == false) { stk = new Stack <LocalBindingRecord>(); OuterLocals.Add(atom, stk); } stk.Push(prev); } ILocalBuilder result; if (SpareLocals.Count > 0) { // reuse a spare variable result = SpareLocals.Pop(); } else { // allocate a new variable with a unique name var tempName = MakeUniqueVariableName(atom); try { result = rb.DefineLocal(tempName.Text); } catch (InvalidOperationException) { throw new CompilerError(CompilerMessages.Expression_Needs_Temporary_Variables_Not_Allowed_Here); } TempLocalNames.Add(tempName); } var lbr = new LocalBindingRecord(reason, src, atom.Text, result); Locals[atom] = lbr; AllLocalBindingRecords.Add(lbr); return(result); }
// 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; } }
/// <exception cref="ArgumentException">A symbol called <paramref name="name"/> is already defined; or <paramref name="entryPoint"/> is <see langword="true"/> and an entry point routine is alrady defined.</exception> public IRoutineBuilder DefineRoutine(string name, bool entryPoint, bool cleanStack) { name = SanitizeSymbol(name); if (symbols.ContainsKey(name)) { throw new ArgumentException("Global symbol already defined: " + name, nameof(name)); } if (entryPoint && entryRoutine != null) { throw new ArgumentException("Entry routine already defined"); } var result = new RoutineBuilder(this, name, entryPoint, cleanStack); symbols.Add(name, "routine"); if (entryPoint) { entryRoutine = result; } return(result); }
void GenerateRoutineCode() { // compile routines IRoutineBuilder mainRoutine = null; foreach (var routine in Context.ZEnvironment.Routines) { var entryPoint = routine.Name == Context.ZEnvironment.EntryRoutineName; Debug.Assert(routine.Name != null); Debug.Assert(Routines.ContainsKey(routine.Name)); var rb = Routines[routine.Name]; try { using (DiagnosticContext.Push(routine.SourceLine)) { BuildRoutine(routine, rb, entryPoint, Context.TraceRoutines); } } catch (ZilError ex) { // could be a compiler error, or an interpreter error thrown by macro evaluation Context.HandleError(ex); } rb.Finish(); if (entryPoint) { mainRoutine = rb; } } if (mainRoutine == null) { throw new CompilerError(CompilerMessages.Missing_GO_Routine); } }
protected abstract void EmitBeforeBlock([NotNull] IRoutineBuilder rb, [NotNull] ILabel exhaustedLabel);
void BuildRoutine([NotNull] ZilRoutine routine, [NotNull] IRoutineBuilder rb, bool entryPoint, bool traceRoutines) { // give the user a chance to rewrite the routine routine = MaybeRewriteRoutine(Context, routine); // set up arguments and locals ClearLocalsAndBlocks(); if (Context.TraceRoutines) { rb.EmitPrint("[" + routine.Name, false); } DefineLocalsFromArgSpec(); if (Context.TraceRoutines) { rb.EmitPrint("]\n", false); } // define a block for the routine Blocks.Clear(); Blocks.Push(new Block { Name = routine.ActivationAtom, AgainLabel = rb.RoutineStart, ReturnLabel = null, Flags = BlockFlags.None }); // generate code for routine body int i = 1; foreach (var stmt in routine.Body) { // only want the result of the last statement // and we never want results in the entry routine, since it can't return CompileStmt(rb, stmt, !entryPoint && i == routine.BodyLength); i++; } // the entry point has to quit instead of returning if (entryPoint) { rb.EmitQuit(); } // clean up WarnAboutUnusedLocals(); ClearLocalsAndBlocks(); // helpers void DefineLocalsFromArgSpec() { foreach (var arg in routine.ArgSpec) { var originalArgName = arg.Atom; var uniqueArgName = MakeUniqueVariableName(originalArgName); if (uniqueArgName != originalArgName) { /* When a parameter has to be renamed because of a conflict, use TempLocalNames * to reserve the new name so we don't collide with it later. For example: * * <GLOBAL FOO <>> * <ROUTINE BLAH (FOO) * <PROG ((FOO)) ...>> * * We rename the local variable to FOO?1 to avoid shadowing the global. * Now the temporary variable bound by the PROG has to be FOO?2. * ZIL code only sees the name FOO: the local is shadowed inside the PROG, * and the global can always be accessed with SETG and GVAL. */ TempLocalNames.Add(uniqueArgName); } var lb = MakeLocalBuilder(arg, uniqueArgName.Text); if (traceRoutines && arg.Type == ArgItem.ArgType.Required) { // TODO: print OPT parameters when tracing routine execution too rb.EmitPrint(" " + originalArgName + "=", false); rb.EmitPrint(PrintOp.Number, lb); } var lbr = new LocalBindingRecord(arg.Type.ToLocalBindingType(), routine.SourceLine, originalArgName.Text, lb); Locals.Add(originalArgName, lbr); AllLocalBindingRecords.Add(lbr); SetOrEmitDefaultValue(lb, arg); } } ILocalBuilder MakeLocalBuilder(ArgItem arg, string uniqueArgName) { ILocalBuilder lb; switch (arg.Type) { case ArgItem.ArgType.Required: try { lb = rb.DefineRequiredParameter(uniqueArgName); } catch (InvalidOperationException) { throw new CompilerError( CompilerMessages.Expression_Needs_Temporary_Variables_Not_Allowed_Here); } break; case ArgItem.ArgType.Optional: lb = rb.DefineOptionalParameter(uniqueArgName); break; case ArgItem.ArgType.Auxiliary: lb = rb.DefineLocal(uniqueArgName); break; default: throw UnhandledCaseException.FromEnum(arg.Type); } return(lb); } void SetOrEmitDefaultValue(ILocalBuilder lb, ArgItem arg) { if (arg.DefaultValue == null) { return; } Debug.Assert(arg.Type == ArgItem.ArgType.Optional || arg.Type == ArgItem.ArgType.Auxiliary); // setting any default value counts as a write MarkVariableAsWritten(lb); lb.DefaultValue = CompileConstant(arg.DefaultValue); if (lb.DefaultValue != null) { return; } ILabel nextLabel = null; // ReSharper disable once SwitchStatementMissingSomeCases switch (arg.Type) { case ArgItem.ArgType.Optional when !rb.HasArgCount: // not a constant throw new CompilerError(routine.SourceLine, CompilerMessages.Optional_Args_With_Nonconstant_Defaults_Not_Supported_For_This_Target); case ArgItem.ArgType.Optional: nextLabel = rb.DefineLabel(); rb.Branch(Condition.ArgProvided, lb, null, nextLabel, true); goto default; default: var val = CompileAsOperand(rb, arg.DefaultValue, routine.SourceLine, lb); if (val != lb) { rb.EmitStore(lb, val); } break; } if (nextLabel != null) { rb.MarkLabel(nextLabel); } } void WarnAboutUnusedLocals() { foreach (var lbr in AllLocalBindingRecords) { if (lbr.IsEverRead || lbr.IsEverWritten) { continue; } if (lbr.Type == LocalBindingType.CompilerTemporary) { continue; } //XXX not sure about this if (lbr.Type == LocalBindingType.RoutineRequired) { continue; } var warning = new CompilerError( lbr.Definition, CompilerMessages.Local_Variable_0_Is_Never_Used, lbr.BoundName); Context.HandleError(warning); } } }
protected abstract void EmitAfterBody([NotNull] IRoutineBuilder rb, [NotNull] ILabel againLabel);
internal IOperand CompilePROG([NotNull] IRoutineBuilder rb, [NotNull] ZilListoidBase args, [NotNull] ISourceLine src, bool wantResult, [CanBeNull] IVariable resultStorage, [NotNull] string name, bool repeat, bool catchy) { if (!args.IsCons(out var first, out var rest)) { throw new CompilerError(CompilerMessages._0_Argument_1_2, name, 1, "argument must be an activation atom or binding list"); } if (first is ZilAtom activationAtom) { args = rest; } else { activationAtom = null; } if (!args.IsCons(out var bindings, out var body) || !(bindings is ZilList bindingList)) { throw new CompilerError(CompilerMessages._0_Missing_Binding_List, name); } if (!wantResult) { /* Recognize the "deferred return" pattern: * * <PROG (... var ...) * ... * <SET var expr> * ... * <LVAL var>> * * In this pattern, the PROG (or BIND) binds an atom and references it exactly twice: * with SET anywhere in the body, and with LVAL as the last expression in the PROG. * * In void context, since the variable value is discarded anyway, we can eliminate the * store and the binding, transforming it to this: * * <PROG (...) * ... * expr * ...> */ TransformProgArgsIfImplementingDeferredReturn(ref bindingList, ref body); } // add new locals, if any var innerLocals = new Queue <ZilAtom>(); void AddLocal(ZilAtom atom) { innerLocals.Enqueue(atom); PushInnerLocal(rb, atom, LocalBindingType.ProgAuxiliary, src); } void AddLocalWithDefault(ZilAtom atom, ZilObject value) { innerLocals.Enqueue(atom); var lb = PushInnerLocal(rb, atom, LocalBindingType.ProgAuxiliary, src); var loc = CompileAsOperand(rb, value, src, lb); if (loc != lb) { rb.EmitStore(lb, loc); } // setting any default value counts as a write MarkVariableAsWritten(lb); } foreach (var obj in bindingList) { switch (obj) { case ZilAtom atom: AddLocal(atom); break; case ZilAdecl adecl when adecl.First is ZilAtom atom: AddLocal(atom); break; case ZilList list when !list.HasLength(2): throw new CompilerError(CompilerMessages._0_Expected_1_Element1s_In_Binding_List, name, 2); case ZilList list when list.Matches(out ZilAtom atom, out ZilObject value): AddLocalWithDefault(atom, value); break; case ZilList list when list.Matches(out ZilAdecl adecl, out ZilObject value) && adecl.First is ZilAtom atom: AddLocalWithDefault(atom, value); break; case ZilAdecl _: case ZilList _: throw new CompilerError(CompilerMessages.Invalid_Atom_Binding); default: throw new CompilerError(CompilerMessages.Elements_Of_Binding_List_Must_Be_Atoms_Or_Lists); } } if (wantResult && resultStorage == null) { resultStorage = rb.Stack; } var block = new Block { Name = activationAtom, AgainLabel = rb.DefineLabel(), ReturnLabel = rb.DefineLabel(), ResultStorage = resultStorage }; if (wantResult) { block.Flags |= BlockFlags.WantResult; } if (!catchy) { block.Flags |= BlockFlags.ExplicitOnly; } rb.MarkLabel(block.AgainLabel); Blocks.Push(block); try { // generate code for prog body IOperand result; if (wantResult) { var clauseResult = CompileClauseBody(rb, body, !repeat, resultStorage); /* The resultStorage we pass to CompileClauseBody (like any other method) is just * a hint, so clauseResult might be different if the result is easily accessible. * For example: * * <SET R <PROG () <SET X 123>>> ;"clauseResult is X" * * SET 'X,123 ;inner SET * SET 'R,X ;outer SET * * But we can't always use it as-is, because there might be RETURNs inside that store * a value and branch to the end of the PROG. * * RETURN always stores the value in our desired resultStorage, so we need to move * the clause body result there too: * * <SET R <PROG () <COND (.F <RETURN 123>)> 456>> * * ZERO? F /L1 * SET 'R,123 ;RETURN * JUMP L2 * L1: SET 'R,456 ;move clauseResult to resultStorage * L2: ... */ if (repeat) { result = resultStorage; } else if (clauseResult != resultStorage && (block.Flags & BlockFlags.Returned) != 0) { rb.EmitStore(resultStorage, clauseResult); result = resultStorage; } else { result = clauseResult; } } else { CompileClauseBody(rb, body, false, null); result = null; } if (repeat) { rb.Branch(block.AgainLabel); } if ((block.Flags & BlockFlags.Returned) != 0) { rb.MarkLabel(block.ReturnLabel); } return(result); } finally { while (innerLocals.Count > 0) { PopInnerLocal(innerLocals.Dequeue()); } Blocks.Pop(); } }
private IOperand CompileBoundedLoop( [NotNull] IRoutineBuilder rb, [NotNull] IBoundedLoopBuilder builder, [NotNull] ZilListoidBase args, [NotNull] ISourceLine src, bool wantResult, [CanBeNull] IVariable resultStorage) { // extract loop spec ("binding list", although we don't care about the bindings here) // TODO: allow activation atoms in bounded loops? if (!args.IsCons(out var first, out var rest) || !(first is ZilList spec)) { throw new CompilerError(CompilerMessages.Expected_Binding_List_At_Start_Of_0, builder.Name); } // instantiate the loop and let it check binding syntax var blc = new BoundedLoopContext(this, spec, src); using (var loop = builder.MakeLoop(blc)) { // look for the optional end statements ZilListoidBase body; if (rest.StartsWith(out ZilList endStmts)) { (_, body) = rest; } else { body = rest; } // create block resultStorage = wantResult ? (resultStorage ?? rb.Stack) : null; var block = new Block { AgainLabel = rb.DefineLabel(), ResultStorage = resultStorage, ReturnLabel = rb.DefineLabel(), Flags = wantResult ? BlockFlags.WantResult : 0 }; Blocks.Push(block); try { var exhaustedLabel = rb.DefineLabel(); // let the loop initialize counters, etc. loop.BeforeBlock(rb, block, exhaustedLabel); // mark the top of the block ("again" label) and let the loop add prechecks, etc. rb.MarkLabel(block.AgainLabel); loop.BeforeBody(); // compile the body CompileClauseBody(rb, body, false, null); // let the loop add postchecks, etc., and mark the end of the block ("exhausted" label) loop.AfterBody(); rb.MarkLabel(exhaustedLabel); // compile the end statements if present, and provide a return value if requested if (endStmts != null) { CompileClauseBody(rb, endStmts, false, null); } if (wantResult) { rb.EmitStore(resultStorage, Game.One); } // if <RETURN> was used inside the loop, mark the return label if ((block.Flags & BlockFlags.Returned) != 0) { rb.MarkLabel(block.ReturnLabel); } } finally { Blocks.Pop(); } return(wantResult ? resultStorage : null); } }
public void MarkSequencePoint(IRoutineBuilder routine, DebugLineRef point) { ((RoutineBuilder)routine).MarkSequencePoint(point); }
public void MarkRoutine(IRoutineBuilder routine, DebugLineRef start, DebugLineRef end) { ((RoutineBuilder)routine).defnStart = start; ((RoutineBuilder)routine).defnEnd = end; }
internal IOperand CompileVERSION_P([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, "VERSION?"); } var(condition, body) = list; // check version condition int condVersion; switch (condition) { case ZilAtom atom: // ReSharper disable once SwitchStatementMissingSomeCases switch (atom.StdAtom) { case StdAtom.ZIP: condVersion = 3; break; case StdAtom.EZIP: condVersion = 4; break; case StdAtom.XZIP: condVersion = 5; break; case StdAtom.YZIP: condVersion = 6; break; case StdAtom.ELSE: case StdAtom.T: condVersion = 0; break; default: throw new CompilerError(CompilerMessages.Unrecognized_Atom_In_VERSION_Must_Be_ZIP_EZIP_XZIP_YZIP_ELSET); } break; case ZilFix fix: condVersion = fix.Value; if (condVersion < 3 || condVersion > 8) { throw new CompilerError(CompilerMessages.Version_Number_Out_Of_Range_Must_Be_38); } break; default: throw new CompilerError(CompilerMessages.Conditions_In_In_VERSION_Clauses_Must_Be_Atoms); } // does this clause match? if (condVersion != Context.ZEnvironment.ZVersion && condVersion != 0) { continue; } // emit code for clause var clauseResult = CompileClauseBody(rb, body, wantResult, resultStorage); if (condVersion == 0 && !clauses.IsEmpty) { Context.HandleError(new CompilerError(src, CompilerMessages._0_Clauses_After_Else_Part_Will_Never_Be_Evaluated, "VERSION?")); } return(wantResult ? clauseResult : null); } // no matching clauses if (wantResult) { rb.EmitStore(resultStorage, Game.Zero); } return(wantResult ? resultStorage : null); }
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); }
protected abstract void EmitBeforeBody([NotNull] IRoutineBuilder rb, [NotNull] ILabel againLabel, [NotNull] ILabel exhaustedLabel);
internal IOperand CompileDO([NotNull] IRoutineBuilder rb, [NotNull] ZilListoidBase args, [NotNull] ISourceLine src, bool wantResult, [CanBeNull] IVariable resultStorage) { return(CompileBoundedLoop(rb, DoLoop.Builder, args, src, wantResult, resultStorage)); }