예제 #1
0
                protected override void EmitAfterBody(IRoutineBuilder rb, ILabel againLabel)
                {
                    // increment
                    bool down;

                    if (inc != null)
                    {
                        int incValue;

                        if (inc is ZilFix fix && (incValue = fix.Value) < 0)
                        {
                            rb.EmitBinary(BinaryOp.Sub, counter, cc.Game.MakeOperand(-incValue), counter);
                            down = true;
                        }
                        else if (inc.IsNonVariableForm())
                        {
                            var operand = cc.CompileAsOperand(rb, inc, src, counter);
                            if (operand != counter)
                            {
                                rb.EmitStore(counter, operand);
                            }
                            down = false;
                        }
                        else
                        {
                            var operand = cc.CompileAsOperand(rb, inc, src);
                            rb.EmitBinary(BinaryOp.Add, counter, operand, counter);
                            down = false;
                        }
                    }
예제 #2
0
 public VoidCall([NotNull] Compilation cc, [NotNull] IRoutineBuilder rb, [NotNull] ZilForm form)
     : this()
 {
     this.cc   = cc;
     this.rb   = rb;
     this.form = form;
 }
예제 #3
0
 public ValueCall([NotNull] Compilation cc, [NotNull] IRoutineBuilder rb, [NotNull] ZilForm form, [NotNull] IVariable resultStorage)
     : this()
 {
     this.cc            = cc;
     this.rb            = rb;
     this.form          = form;
     this.resultStorage = resultStorage;
 }
예제 #4
0
            public void BeforeBlock(IRoutineBuilder rb, Block block, ILabel exhaustedLabel)
            {
                this.rb             = rb;
                this.againLabel     = block.AgainLabel;
                this.exhaustedLabel = exhaustedLabel;

                EmitBeforeBlock(rb, exhaustedLabel);
            }
예제 #5
0
        Action ValidateAction([NotNull] Dictionary <ZilAtom, Action> actions, [NotNull] Syntax line)
        {
            try
            {
                using (DiagnosticContext.Push(line.SourceLine))
                {
                    if (actions.TryGetValue(line.ActionName, out var act) == false)
                    {
                        if (Routines.TryGetValue(line.Action, out var routine) == false)
                        {
                            throw new CompilerError(CompilerMessages.Undefined_0_1, "action routine", line.Action);
                        }

                        IRoutineBuilder preRoutine = null;
                        if (line.Preaction != null &&
                            Routines.TryGetValue(line.Preaction, out preRoutine) == false)
                        {
                            throw new CompilerError(CompilerMessages.Undefined_0_1, "preaction routine", line.Preaction);
                        }

                        var actionName = line.ActionName;
                        int index      = Context.ZEnvironment.NextAction++;

                        if (index >= Context.ZEnvironment.VocabFormat.MaxActionCount)
                        {
                            throw new InterpreterError(
                                      InterpreterMessages.Too_Many_0_Only_1_Allowed_In_This_Vocab_Format,
                                      "actions",
                                      Context.ZEnvironment.VocabFormat.MaxActionCount);
                        }

                        var number   = Game.MakeOperand(index);
                        var constant = Game.DefineConstant(actionName.Text, number);
                        Constants.Add(actionName, constant);
                        if (WantDebugInfo)
                        {
                            Debug.Assert(Game.DebugFile != null);
                            Game.DebugFile.MarkAction(constant, actionName.Text);
                        }

                        act = new Action(index, constant, routine, preRoutine, line.Action, line.Preaction);
                        actions.Add(actionName, act);
                    }
                    else
                    {
                        WarnIfActionRoutineDiffers(line, "action routine", line.Action, act.RoutineName);
                        WarnIfActionRoutineDiffers(line, "preaction routine", line.Preaction, act.PreRoutineName);
                    }

                    return(act);
                }
            }
            catch (ZilError ex)
            {
                Context.HandleError(ex);
                return(null);
            }
        }
예제 #6
0
 public PredCall([NotNull] Compilation cc, [NotNull] IRoutineBuilder rb, [NotNull] ZilForm form, [NotNull] ILabel label, bool polarity)
     : this()
 {
     this.cc       = cc;
     this.rb       = rb;
     this.form     = form;
     this.label    = label;
     this.polarity = polarity;
 }
예제 #7
0
 public Action(int index, IOperand constant, IRoutineBuilder routine, IRoutineBuilder preRoutine,
               ZilAtom routineName, ZilAtom preRoutineName)
 {
     Index          = index;
     Constant       = constant;
     Routine        = routine;
     RoutineName    = routineName;
     PreRoutine     = preRoutine;
     PreRoutineName = preRoutineName;
 }
예제 #8
0
        IOperand CompileClauseBody([NotNull] IRoutineBuilder rb, [NotNull] ZilListoidBase clause, bool wantResult,
                                   [CanBeNull] IVariable resultStorage)
        {
            if (clause.IsEmpty)
            {
                return(Game.One);
            }

            IOperand result = null;

            do
            {
                var(first, rest) = clause;

                // only want the result of the last statement (if any)
                bool wantThisResult = wantResult && rest.IsEmpty;

                var stmt = first.Unwrap(Context);

                switch (stmt)
                {
                case ZilForm form:
                    MarkSequencePoint(rb, form);

                    result = CompileForm(
                        rb,
                        form,
                        wantThisResult,
                        wantThisResult ? resultStorage : null);
                    break;

                case ZilList _:
                    throw new CompilerError(stmt, CompilerMessages.Expressions_Of_This_Type_Cannot_Be_Compiled)
                          .Combine(new CompilerError(CompilerMessages.Misplaced_Bracket_In_COND_Or_Loop));

                default:
                    if (wantThisResult)
                    {
                        result = CompileConstant(stmt);

                        if (result == null)
                        {
                            // TODO: show "expressions of this type cannot be compiled" warning even if wantResult is false?
                            throw new CompilerError(stmt,
                                                    CompilerMessages.Expressions_Of_This_Type_Cannot_Be_Compiled);
                        }
                    }
                    break;
                }

                clause = rest;
            } while (!clause.IsEmpty);

            return(result);
        }
예제 #9
0
                protected override void EmitBeforeBlock(IRoutineBuilder rb, ILabel exhaustedLabel)
                {
                    // initialize counter
                    this.counter = cc.PushInnerLocal(rb, atom, LocalBindingType.LoopState, src);
                    var operand = cc.CompileAsOperand(rb, start, src, counter);

                    if (operand != counter)
                    {
                        rb.EmitStore(counter, operand);
                    }
                }
예제 #10
0
        void MarkSequencePoint([NotNull] IRoutineBuilder rb, [NotNull] IProvideSourceLine node)
        {
            if (!WantDebugInfo || !(node.SourceLine is FileSourceLine fileSourceLine))
            {
                return;
            }

            Debug.Assert(Game.DebugFile != null);
            Game.DebugFile.MarkSequencePoint(rb,
                                             new DebugLineRef(fileSourceLine.FileName, fileSourceLine.Line, 1));
        }
예제 #11
0
 public ValuePredCall([NotNull] Compilation cc, [NotNull] IRoutineBuilder rb, [NotNull] ZilForm form,
                      [NotNull] IVariable resultStorage, [NotNull] ILabel label, bool polarity)
     : this()
 {
     this.cc            = cc;
     this.rb            = rb;
     this.form          = form;
     this.resultStorage = resultStorage;
     this.label         = label;
     this.polarity      = polarity;
 }
예제 #12
0
 protected override void EmitBeforeBody(IRoutineBuilder rb, ILabel againLabel, ILabel exhaustedLabel)
 {
     // test and branch before the body, if end is a (non-[GL]VAL) FORM
     if (end.IsNonVariableForm())
     {
         cc.CompileCondition(rb, end, end.SourceLine, exhaustedLabel, true);
         precheck = true;
     }
     else
     {
         precheck = false;
     }
 }
 public void Run(IRoutineBuilder routine, Action <IRoutineResult> callback)
 {
     this.routine  = routine;
     this.callback = callback;
     try
     {
         caller.StartCoroutine(Run());
     }
     catch (Exception e)
     {
         Debug.LogError($"{e.Message} {e.InnerException?.Message}");
     }
 }
        private IEnumerator Run()
        {
            Reset();
            caller.StartCoroutine(routine.RunCoroutine(this));
            do
            {
                yield return(null);
            } while (routine != null && routine.IsRunning);

            callback?.Invoke(routine.Result);
            this.routine  = null;
            this.callback = null;
        }
예제 #15
0
        public IOperand CompileAsOperand([NotNull] IRoutineBuilder rb, [NotNull] ZilObject expr, [NotNull] ISourceLine src,
                                         [CanBeNull] IVariable suggestion = null)
        {
            expr = expr.Unwrap(Context);

            var constant = CompileConstant(expr, AmbiguousConstantMode.Pessimistic);

            if (constant != null)
            {
                return(constant);
            }

            switch (expr.StdTypeAtom)
            {
            case StdAtom.FORM:
                return(CompileForm(rb, (ZilForm)expr, true, suggestion ?? rb.Stack));

            case StdAtom.ATOM:
                var atom = (ZilAtom)expr;
                if (Globals.ContainsKey(atom))
                {
                    Context.HandleError(new CompilerError(src,
                                                          CompilerMessages.Bare_Atom_0_Interpreted_As_Global_Variable_Index, atom));
                    return(Globals[atom].Indirect);
                }
                if (SoftGlobals.ContainsKey(atom))
                {
                    Context.HandleError(new CompilerError(
                                            expr.SourceLine ?? src,
                                            CompilerMessages.Soft_Variable_0_May_Not_Be_Used_Here,
                                            atom));
                }
                else
                {
                    Context.HandleError(new CompilerError(
                                            expr.SourceLine ?? src,
                                            CompilerMessages.Bare_Atom_0_Used_As_Operand_Is_Not_A_Global_Variable,
                                            atom));
                }
                return(Game.Zero);

            default:
                Context.HandleError(new CompilerError(
                                        expr.SourceLine ?? src,
                                        CompilerMessages.Expressions_Of_This_Type_Cannot_Be_Compiled));
                return(Game.Zero);
            }
        }
예제 #16
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);
        }
예제 #17
0
        // TODO: replace CompileStmt with CompileForm and (in loops) CompileClauseBody
        void CompileStmt([NotNull] IRoutineBuilder rb, [NotNull] ZilObject stmt, bool wantResult)
        {
            stmt = stmt.Unwrap(Context);

            switch (stmt)
            {
            case ZilForm form:
                MarkSequencePoint(rb, form);

                var result = CompileForm(rb, form, wantResult, null);

                if (wantResult)
                {
                    rb.Return(result);
                }
                break;

            case ZilList _:
                throw new CompilerError(stmt, CompilerMessages.Expressions_Of_This_Type_Cannot_Be_Compiled)
                      .Combine(new CompilerError(CompilerMessages.Misplaced_Bracket_In_COND_Or_Loop));

            default:
                if (wantResult)
                {
                    var value = CompileConstant(stmt);

                    if (value == null)
                    {
                        // TODO: show "expressions of this type cannot be compiled" warning even if wantResult is false?
                        throw new CompilerError(stmt, CompilerMessages.Expressions_Of_This_Type_Cannot_Be_Compiled);
                    }

                    rb.Return(value);
                }
                break;
            }
        }
예제 #18
0
        /// <exception cref="ArgumentException">A symbol called <paramref name="name"/> is already defined; or <paramref name="entryPoint"/> is <see langword="true"/> and an entry point routine is alrady defined.</exception>
        public IRoutineBuilder DefineRoutine(string name, bool entryPoint, bool cleanStack)
        {
            name = SanitizeSymbol(name);
            if (symbols.ContainsKey(name))
            {
                throw new ArgumentException("Global symbol already defined: " + name, nameof(name));
            }

            if (entryPoint && entryRoutine != null)
            {
                throw new ArgumentException("Entry routine already defined");
            }

            var result = new RoutineBuilder(this, name, entryPoint, cleanStack);

            symbols.Add(name, "routine");

            if (entryPoint)
            {
                entryRoutine = result;
            }

            return(result);
        }
예제 #19
0
        void GenerateRoutineCode()
        {
            // compile routines
            IRoutineBuilder mainRoutine = null;

            foreach (var routine in Context.ZEnvironment.Routines)
            {
                var entryPoint = routine.Name == Context.ZEnvironment.EntryRoutineName;
                Debug.Assert(routine.Name != null);
                Debug.Assert(Routines.ContainsKey(routine.Name));
                var rb = Routines[routine.Name];
                try
                {
                    using (DiagnosticContext.Push(routine.SourceLine))
                    {
                        BuildRoutine(routine, rb, entryPoint, Context.TraceRoutines);
                    }
                }
                catch (ZilError ex)
                {
                    // could be a compiler error, or an interpreter error thrown by macro evaluation
                    Context.HandleError(ex);
                }
                rb.Finish();

                if (entryPoint)
                {
                    mainRoutine = rb;
                }
            }

            if (mainRoutine == null)
            {
                throw new CompilerError(CompilerMessages.Missing_GO_Routine);
            }
        }
예제 #20
0
 protected abstract void EmitBeforeBlock([NotNull] IRoutineBuilder rb, [NotNull] ILabel exhaustedLabel);
예제 #21
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);
                }
            }
        }
예제 #22
0
 protected abstract void EmitAfterBody([NotNull] IRoutineBuilder rb, [NotNull] ILabel againLabel);
예제 #23
0
        internal IOperand CompilePROG([NotNull] IRoutineBuilder rb, [NotNull] ZilListoidBase args,
                                      [NotNull] ISourceLine src,
                                      bool wantResult, [CanBeNull] IVariable resultStorage, [NotNull] string name, bool repeat, bool catchy)
        {
            if (!args.IsCons(out var first, out var rest))
            {
                throw new CompilerError(CompilerMessages._0_Argument_1_2, name, 1, "argument must be an activation atom or binding list");
            }

            if (first is ZilAtom activationAtom)
            {
                args = rest;
            }
            else
            {
                activationAtom = null;
            }

            if (!args.IsCons(out var bindings, out var body) || !(bindings is ZilList bindingList))
            {
                throw new CompilerError(CompilerMessages._0_Missing_Binding_List, name);
            }

            if (!wantResult)
            {
                /* Recognize the "deferred return" pattern:
                 *
                 *   <PROG (... var ...)
                 *       ...
                 *       <SET var expr>
                 *       ...
                 *       <LVAL var>>
                 *
                 * In this pattern, the PROG (or BIND) binds an atom and references it exactly twice:
                 * with SET anywhere in the body, and with LVAL as the last expression in the PROG.
                 *
                 * In void context, since the variable value is discarded anyway, we can eliminate the
                 * store and the binding, transforming it to this:
                 *
                 *   <PROG (...)
                 *       ...
                 *       expr
                 *       ...>
                 */

                TransformProgArgsIfImplementingDeferredReturn(ref bindingList, ref body);
            }

            // add new locals, if any
            var innerLocals = new Queue <ZilAtom>();

            void AddLocal(ZilAtom atom)
            {
                innerLocals.Enqueue(atom);
                PushInnerLocal(rb, atom, LocalBindingType.ProgAuxiliary, src);
            }

            void AddLocalWithDefault(ZilAtom atom, ZilObject value)
            {
                innerLocals.Enqueue(atom);
                var lb  = PushInnerLocal(rb, atom, LocalBindingType.ProgAuxiliary, src);
                var loc = CompileAsOperand(rb, value, src, lb);

                if (loc != lb)
                {
                    rb.EmitStore(lb, loc);
                }

                // setting any default value counts as a write
                MarkVariableAsWritten(lb);
            }

            foreach (var obj in bindingList)
            {
                switch (obj)
                {
                case ZilAtom atom:
                    AddLocal(atom);
                    break;

                case ZilAdecl adecl when adecl.First is ZilAtom atom:
                    AddLocal(atom);
                    break;

                case ZilList list when !list.HasLength(2):
                    throw new CompilerError(CompilerMessages._0_Expected_1_Element1s_In_Binding_List, name, 2);

                case ZilList list when list.Matches(out ZilAtom atom, out ZilObject value):
                    AddLocalWithDefault(atom, value);

                    break;

                case ZilList list when list.Matches(out ZilAdecl adecl, out ZilObject value) && adecl.First is ZilAtom atom:
                    AddLocalWithDefault(atom, value);

                    break;

                case ZilAdecl _:
                case ZilList _:
                    throw new CompilerError(CompilerMessages.Invalid_Atom_Binding);

                default:
                    throw new CompilerError(CompilerMessages.Elements_Of_Binding_List_Must_Be_Atoms_Or_Lists);
                }
            }

            if (wantResult && resultStorage == null)
            {
                resultStorage = rb.Stack;
            }

            var block = new Block
            {
                Name          = activationAtom,
                AgainLabel    = rb.DefineLabel(),
                ReturnLabel   = rb.DefineLabel(),
                ResultStorage = resultStorage
            };

            if (wantResult)
            {
                block.Flags |= BlockFlags.WantResult;
            }
            if (!catchy)
            {
                block.Flags |= BlockFlags.ExplicitOnly;
            }

            rb.MarkLabel(block.AgainLabel);
            Blocks.Push(block);

            try
            {
                // generate code for prog body
                IOperand result;

                if (wantResult)
                {
                    var clauseResult = CompileClauseBody(rb, body, !repeat, resultStorage);

                    /* The resultStorage we pass to CompileClauseBody (like any other method) is just
                     * a hint, so clauseResult might be different if the result is easily accessible.
                     * For example:
                     *
                     *     <SET R <PROG () <SET X 123>>>  ;"clauseResult is X"
                     *
                     *     SET 'X,123       ;inner SET
                     *     SET 'R,X         ;outer SET
                     *
                     * But we can't always use it as-is, because there might be RETURNs inside that store
                     * a value and branch to the end of the PROG.
                     *
                     * RETURN always stores the value in our desired resultStorage, so we need to move
                     * the clause body result there too:
                     *
                     *     <SET R <PROG () <COND (.F <RETURN 123>)> 456>>
                     *
                     *     ZERO? F /L1
                     *     SET 'R,123       ;RETURN
                     *     JUMP L2
                     * L1: SET 'R,456       ;move clauseResult to resultStorage
                     * L2: ...
                     */

                    if (repeat)
                    {
                        result = resultStorage;
                    }
                    else if (clauseResult != resultStorage && (block.Flags & BlockFlags.Returned) != 0)
                    {
                        rb.EmitStore(resultStorage, clauseResult);
                        result = resultStorage;
                    }
                    else
                    {
                        result = clauseResult;
                    }
                }
                else
                {
                    CompileClauseBody(rb, body, false, null);
                    result = null;
                }

                if (repeat)
                {
                    rb.Branch(block.AgainLabel);
                }

                if ((block.Flags & BlockFlags.Returned) != 0)
                {
                    rb.MarkLabel(block.ReturnLabel);
                }

                return(result);
            }
            finally
            {
                while (innerLocals.Count > 0)
                {
                    PopInnerLocal(innerLocals.Dequeue());
                }

                Blocks.Pop();
            }
        }
예제 #24
0
        private IOperand CompileBoundedLoop(
            [NotNull] IRoutineBuilder rb, [NotNull] IBoundedLoopBuilder builder,
            [NotNull] ZilListoidBase args, [NotNull] ISourceLine src,
            bool wantResult, [CanBeNull] IVariable resultStorage)
        {
            // extract loop spec ("binding list", although we don't care about the bindings here)
            // TODO: allow activation atoms in bounded loops?
            if (!args.IsCons(out var first, out var rest) || !(first is ZilList spec))
            {
                throw new CompilerError(CompilerMessages.Expected_Binding_List_At_Start_Of_0, builder.Name);
            }

            // instantiate the loop and let it check binding syntax
            var blc = new BoundedLoopContext(this, spec, src);

            using (var loop = builder.MakeLoop(blc))
            {
                // look for the optional end statements
                ZilListoidBase body;
                if (rest.StartsWith(out ZilList endStmts))
                {
                    (_, body) = rest;
                }
                else
                {
                    body = rest;
                }

                // create block
                resultStorage = wantResult ? (resultStorage ?? rb.Stack) : null;

                var block = new Block
                {
                    AgainLabel    = rb.DefineLabel(),
                    ResultStorage = resultStorage,
                    ReturnLabel   = rb.DefineLabel(),
                    Flags         = wantResult ? BlockFlags.WantResult : 0
                };

                Blocks.Push(block);
                try
                {
                    var exhaustedLabel = rb.DefineLabel();

                    // let the loop initialize counters, etc.
                    loop.BeforeBlock(rb, block, exhaustedLabel);

                    // mark the top of the block ("again" label) and let the loop add prechecks, etc.
                    rb.MarkLabel(block.AgainLabel);
                    loop.BeforeBody();

                    // compile the body
                    CompileClauseBody(rb, body, false, null);

                    // let the loop add postchecks, etc., and mark the end of the block ("exhausted" label)
                    loop.AfterBody();

                    rb.MarkLabel(exhaustedLabel);

                    // compile the end statements if present, and provide a return value if requested
                    if (endStmts != null)
                    {
                        CompileClauseBody(rb, endStmts, false, null);
                    }

                    if (wantResult)
                    {
                        rb.EmitStore(resultStorage, Game.One);
                    }

                    // if <RETURN> was used inside the loop, mark the return label
                    if ((block.Flags & BlockFlags.Returned) != 0)
                    {
                        rb.MarkLabel(block.ReturnLabel);
                    }
                }
                finally
                {
                    Blocks.Pop();
                }

                return(wantResult ? resultStorage : null);
            }
        }
예제 #25
0
 public void MarkSequencePoint(IRoutineBuilder routine, DebugLineRef point)
 {
     ((RoutineBuilder)routine).MarkSequencePoint(point);
 }
예제 #26
0
 public void MarkRoutine(IRoutineBuilder routine, DebugLineRef start, DebugLineRef end)
 {
     ((RoutineBuilder)routine).defnStart = start;
     ((RoutineBuilder)routine).defnEnd   = end;
 }
예제 #27
0
        internal IOperand CompileVERSION_P([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, "VERSION?");
                }

                var(condition, body) = list;

                // check version condition
                int condVersion;
                switch (condition)
                {
                case ZilAtom atom:
                    // ReSharper disable once SwitchStatementMissingSomeCases
                    switch (atom.StdAtom)
                    {
                    case StdAtom.ZIP:
                        condVersion = 3;
                        break;

                    case StdAtom.EZIP:
                        condVersion = 4;
                        break;

                    case StdAtom.XZIP:
                        condVersion = 5;
                        break;

                    case StdAtom.YZIP:
                        condVersion = 6;
                        break;

                    case StdAtom.ELSE:
                    case StdAtom.T:
                        condVersion = 0;
                        break;

                    default:
                        throw new CompilerError(CompilerMessages.Unrecognized_Atom_In_VERSION_Must_Be_ZIP_EZIP_XZIP_YZIP_ELSET);
                    }
                    break;

                case ZilFix fix:
                    condVersion = fix.Value;
                    if (condVersion < 3 || condVersion > 8)
                    {
                        throw new CompilerError(CompilerMessages.Version_Number_Out_Of_Range_Must_Be_38);
                    }
                    break;

                default:
                    throw new CompilerError(CompilerMessages.Conditions_In_In_VERSION_Clauses_Must_Be_Atoms);
                }

                // does this clause match?
                if (condVersion != Context.ZEnvironment.ZVersion && condVersion != 0)
                {
                    continue;
                }

                // emit code for clause
                var clauseResult = CompileClauseBody(rb, body, wantResult, resultStorage);

                if (condVersion == 0 && !clauses.IsEmpty)
                {
                    Context.HandleError(new CompilerError(src, CompilerMessages._0_Clauses_After_Else_Part_Will_Never_Be_Evaluated, "VERSION?"));
                }

                return(wantResult ? clauseResult : null);
            }

            // no matching clauses
            if (wantResult)
            {
                rb.EmitStore(resultStorage, Game.Zero);
            }

            return(wantResult ? resultStorage : null);
        }
예제 #28
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);
        }
예제 #29
0
 protected abstract void EmitBeforeBody([NotNull] IRoutineBuilder rb, [NotNull] ILabel againLabel, [NotNull] ILabel exhaustedLabel);
예제 #30
0
 internal IOperand CompileDO([NotNull] IRoutineBuilder rb, [NotNull] ZilListoidBase args, [NotNull] ISourceLine src, bool wantResult,
                             [CanBeNull] IVariable resultStorage)
 {
     return(CompileBoundedLoop(rb, DoLoop.Builder, args, src, wantResult, resultStorage));
 }