コード例 #1
0
        static ZilRoutine MaybeRewriteRoutine([NotNull] Context ctx, [NotNull] ZilRoutine origRoutine)
        {
            const string SExpectedResultType = "a list (with an arg spec and body) or FALSE";

            Debug.Assert(origRoutine.Name != null);

            var result = ctx.RunHook(
                "REWRITE-ROUTINE",
                origRoutine.Name,
                origRoutine.ArgSpec.ToZilList(),
                new ZilList(origRoutine.Body));

            switch (result)
            {
            case ZilList list when list.GetLength(1) == null && list.First is ZilList args:
                Debug.Assert(list.Rest != null);

                return(new ZilRoutine(origRoutine.Name, null, args, list.Rest, origRoutine.Flags));

            case ZilFalse _:
            case null:
                return(origRoutine);

            default:
                throw new InterpreterError(InterpreterMessages._0_1_Must_Return_2, "routine rewriter", SExpectedResultType);
            }
        }
コード例 #2
0
        public static void WalkRoutineForms([NotNull] this ZilRoutine routine, [NotNull] Action <ZilForm> action)
        {
            var children =
                routine.ArgSpec.Select(ai => ai.DefaultValue)
                .Concat(routine.Body);

            foreach (var form in children.OfType <ZilForm>())
            {
                action(form);
                form.WalkChildren(action);
            }
        }
コード例 #3
0
        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);
                }
            }
        }