Ejemplo n.º 1
0
        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);
        }
Ejemplo n.º 2
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);
                }
            }
        }
Ejemplo n.º 3
0
 public void MarkVariableAsReadAndWritten([NotNull] LocalBindingRecord lbr) =>
 lbr.IsEverRead = lbr.IsEverWritten = true;
Ejemplo n.º 4
0
 public void MarkVariableAsRead([NotNull] LocalBindingRecord lbr) =>
 lbr.IsEverRead = true;