Beispiel #1
0
        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);
        }
Beispiel #2
0
        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);
                }
            }
        }