Esempio n. 1
0
        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);
                }
            }
        }
Esempio n. 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);
                }
            }
        }
Esempio n. 3
0
        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);
            }
        }