예제 #1
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>();
        }
예제 #2
0
        // Compiles a method definition inside a class body. Returns the symbol in the
        // method table for the new method.
        private int Method(ClassCompiler classCompiler, bool isConstructor, SignatureFn signatureFn)
        {
            // Build the method signature.
            Signature signature = new Signature();
            SignatureFromToken(signature);

            classCompiler.MethodName = signature.Name;
            classCompiler.MethodLength = signature.Length;

            Compiler methodCompiler = new Compiler(_parser, this, false);

            // Compile the method signature.
            signatureFn(methodCompiler, signature);

            // Include the full signature in debug messages in stack traces.

            Consume(TokenType.LeftBrace, "Expect '{' to begin method body.");
            methodCompiler.FinishBody(isConstructor);

            methodCompiler.EndCompiler();

            return SignatureSymbol(signature);
        }
예제 #3
0
        // Compiles a class definition. Assumes the "class" token has already been
        // consumed.
        private void ClassDefinition()
        {
            // Create a variable to store the class in.
            int slot = DeclareNamedVariable();
            bool isModule = _scopeDepth == -1;

            // Make a string constant for the name.
            int nameConstant = AddConstant(Obj.MakeString(_parser.Source.Substring(_parser.Previous.Start, _parser.Previous.Length)));

            EmitConstant(nameConstant);

            // Load the superclass (if there is one).
            if (Match(TokenType.Is))
            {
                ParsePrecedence(false, Precedence.Call);
            }
            else
            {
                // Create the empty class.
                Emit(Instruction.Null);
            }

            // Store a placeholder for the number of fields argument. We don't know
            // the value until we've compiled all the methods to see which fields are
            // used.
            int numFieldsInstruction = EmitByteArg(Instruction.Class, 255);

            // Store it in its name.
            DefineVariable(slot);

            // Push a local variable scope. Static fields in a class body are hoisted out
            // into local variables declared in this scope. Methods that use them will
            // have upvalues referencing them.
            PushScope();

            ClassCompiler classCompiler = new ClassCompiler();

            // Set up a symbol table for the class's fields. We'll initially compile
            // them to slots starting at zero. When the method is bound to the class, the
            // bytecode will be adjusted by [BindMethod] to take inherited fields
            // into account.
            List<string> fields = new List<string>();

            classCompiler.Fields = fields;

            _enclosingClass = classCompiler;

            // Compile the method definitions.
            Consume(TokenType.LeftBrace, "Expect '{' after class declaration.");
            MatchLine();

            while (!Match(TokenType.RightBrace))
            {
                Instruction instruction = Instruction.MethodInstance;
                bool isConstructor = false;

                classCompiler.IsStaticMethod = false;

                if (Match(TokenType.Static))
                {
                    instruction = Instruction.MethodStatic;
                    classCompiler.IsStaticMethod = true;
                }
                else if (Peek() == TokenType.New)
                {
                    // If the method name is "new", it's a constructor.
                    isConstructor = true;
                }

                SignatureFn signature = _rules[(int)_parser.Current.Type].Method;
                NextToken();

                if (signature == null)
                {
                    Error("Expect method definition.");
                    break;
                }

                int methodSymbol = Method(classCompiler, isConstructor, signature);

                // Load the class. We have to do this for each method because we can't
                // keep the class on top of the stack. If there are static fields, they
                // will be locals above the initial variable slot for the class on the
                // stack. To skip past those, we just load the class each time right before
                // defining a method.
                if (isModule)
                {
                    EmitShortArg(Instruction.LoadModuleVar, slot);
                }
                else
                {
                    LoadLocal(slot);
                }

                // Define the method.
                EmitShortArg(instruction, methodSymbol);

                // Don't require a newline after the last definition.
                if (Match(TokenType.RightBrace)) break;

                ConsumeLine("Expect newline after definition in class.");
            }

            // Update the class with the number of fields.
            _bytecode[numFieldsInstruction] = (byte)fields.Count;

            _enclosingClass = null;

            PopScope();
        }