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); } } }