예제 #1
0
        private void WhileStatement()
        {
            Loop l = new Loop();
            StartLoop(l);

            // Compile the condition.
            Consume(TokenType.LeftParen, "Expect '(' after 'while'.");
            Expression();
            Consume(TokenType.RightParen, "Expect ')' after while condition.");

            TestExitLoop();
            LoopBody();
            EndLoop();
        }
예제 #2
0
 // Marks the beginning of a loop. Keeps track of the current instruction so we
 // know what to loop back to at the end of the body.
 private void StartLoop(Loop l)
 {
     l.Enclosing = _loop;
     l.Start = _bytecode.Count - 1 - 1;
     l.ScopeDepth = _scopeDepth;
     _loop = l;
 }
예제 #3
0
        private void ForStatement()
        {
            // A for statement like:
            //
            //     for (i in sequence.expression) {
            //       System.write(i)
            //     }
            //
            // Is compiled to bytecode almost as if the source looked like this:
            //
            //     {
            //       var seq_ = sequence.expression
            //       var iter_
            //       while (iter_ = seq_.iterate(iter_)) {
            //         var i = seq_.iteratorValue(iter_)
            //         System.write(i)
            //       }
            //     }
            //
            // It's not exactly this, because the synthetic variables `seq_` and `iter_`
            // actually get names that aren't valid identfiers, but that's the basic
            // idea.
            //
            // The important parts are:
            // - The sequence expression is only evaluated once.
            // - The .iterate() method is used to advance the iterator and determine if
            //   it should exit the loop.
            // - The .iteratorValue() method is used to get the value at the current
            //   iterator position.

            // Create a scope for the hidden local variables used for the iterator.
            PushScope();

            Consume(TokenType.LeftParen, "Expect '(' after 'for'.");
            Consume(TokenType.Name, "Expect for loop variable name.");

            // Remember the name of the loop variable.
            string name = _parser.Source.Substring(_parser.Previous.Start, _parser.Previous.Length);
            int length = _parser.Previous.Length;

            Consume(TokenType.In, "Expect 'in' after loop variable.");
            IgnoreNewlines();

            // Evaluate the sequence expression and store it in a hidden local variable.
            // The space in the variable name ensures it won't collide with a user-defined
            // variable.
            Expression();
            int seqSlot = DefineLocal("seq ", 4);

            // Create another hidden local for the iterator object.
            null_(this, false);
            int iterSlot = DefineLocal("iter ", 5);

            Consume(TokenType.RightParen, "Expect ')' after loop expression.");

            Loop l = new Loop();
            StartLoop(l);

            // Advance the iterator by calling the ".iterate" method on the sequence.
            LoadLocal(seqSlot);
            LoadLocal(iterSlot);

            CallMethod(1, "iterate(_)");

            // Store the iterator back in its local for the next iteration.
            EmitByteArg(Instruction.StoreLocal, iterSlot);
            // TODO: We can probably get this working with a bit less stack juggling.

            TestExitLoop();

            // Get the current value in the sequence by calling ".iteratorValue".
            LoadLocal(seqSlot);
            LoadLocal(iterSlot);

            CallMethod(1, "iteratorValue(_)");

            // Bind the loop variable in its own scope. This ensures we get a fresh
            // variable each iteration so that closures for it don't all see the same one.
            PushScope();
            DefineLocal(name, length);

            LoopBody();

            // Loop variable.
            PopScope();

            EndLoop();

            // Hidden variables.
            PopScope();
        }
예제 #4
0
        // Initializes [compiler].
        public Compiler(Parser parser, Compiler parent, bool isFunction)
        {
            _parser = parser;
            _parent = parent;

            // Initialize this to null before allocating in case a GC gets triggered in
            // the middle of initializing the compiler.
            _constants = new List<Obj>();

            _numUpValues = 0;
            _numParams = 0;
            _loop = null;
            _enclosingClass = null;

            parser.Vm.Compiler = this;

            if (parent == null)
            {
                _numLocals = 0;

                // Compiling top-level code, so the initial scope is module-level.
                _scopeDepth = -1;
            }
            else
            {
                // Declare a fake local variable for the receiver so that it's slot in the
                // stack is taken. For methods, we call this "this", so that we can resolve
                // references to that like a normal variable. For functions, they have no
                // explicit "this". So we pick a bogus name. That way references to "this"
                // inside a function will try to walk up the parent chain to find a method
                // enclosing the function whose "this" we can close over.
                _numLocals = 1;
                if (isFunction)
                {
                    _locals[0].Name = null;
                    _locals[0].Length = 0;
                }
                else
                {
                    _locals[0].Name = "this";
                    _locals[0].Length = 4;
                }
                _locals[0].Depth = -1;
                _locals[0].IsUpvalue = false;

                // The initial scope for function or method is a local scope.
                _scopeDepth = 0;
            }

            _bytecode = new List<byte>();
        }
예제 #5
0
        // Ends the current innermost loop. Patches up all jumps and breaks now that
        // we know where the end of the loop is.
        private void EndLoop()
        {
            int loopOffset = _bytecode.Count - 1 - _loop.Start + 2;
            // TODO: Check for overflow.
            EmitShortArg(Instruction.Loop, loopOffset);

            PatchJump(_loop.ExitJump);

            // Find any break placeholder instructions (which will be Instruction.END in the
            // bytecode) and replace them with real jumps.
            int i = _loop.Body;
            while (i < _bytecode.Count - 1)
            {
                if (_bytecode[i] == (byte)Instruction.End)
                {
                    _bytecode[i] = (byte)Instruction.Jump;
                    PatchJump(i + 1);
                    i += 3;
                }
                else
                {
                    // Skip this instruction and its arguments.
                    i += 1 + GetNumArguments(_bytecode.ToArray(), _constants, i);
                }
            }

            _loop = _loop.Enclosing;
        }