Ejemplo n.º 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>();
            _debugSourceLines = new int[16000];
        }
Ejemplo n.º 2
0
        // Compiles a class definition. Assumes the "class" token has already been
        // consumed.
        private void ClassDefinition(bool isForeign)
        {
            // Create a variable to store the class in.
            int slot = DeclareNamedVariable();

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

            EmitShortArg(Instruction.CONSTANT, nameConstant);

            // Load the superclass (if there is one).
            if (Match(TokenType.Is))
            {
                ParsePrecedence(false, Precedence.Call);
            }
            else
            {
                // Implicitly inherit from Object.
                LoadCoreVariable("Object");
            }

            // 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 = -1;
            if (isForeign)
            {
                Emit(Instruction.FOREIGN_CLASS);
            }
            else
            {
                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;
            classCompiler.IsForeign = isForeign;

            _enclosingClass = classCompiler;

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

            while (!Match(TokenType.RightBrace))
            {
                if (!Method(classCompiler, slot)) break;

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

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

            if (!isForeign)
            {
                // Update the class with the number of fields.
                _bytecode[numFieldsInstruction] = (byte)fields.Count;
            }

            _enclosingClass = null;

            PopScope();
        }
Ejemplo n.º 3
0
        // Compiles a class definition. Assumes the "class" token has already been
        // consumed.
        private void ClassDefinition(bool isForeign)
        {
            // Create a variable to store the class in.
            int slot = DeclareNamedVariable();

            // Make a string constant for the name.
            int nameConstant = AddConstant(new Value(parser.source.Substring(parser.previous.start, parser.previous.length)));

            EmitShortArg(Instruction.CONSTANT, nameConstant);

            // Load the superclass (if there is one).
            if (Match(TokenType.TOKEN_IS))
            {
                ParsePrecedence(false, Precedence.PREC_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 = -1;
            if (isForeign)
            {
                Emit(Instruction.FOREIGN_CLASS);
            }
            else
            {
                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;
            classCompiler.isForeign = isForeign;

            enclosingClass = classCompiler;

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

            bool hasConstructor = false;

            while (!Match(TokenType.TOKEN_RIGHT_BRACE))
            {
                if (!Method(classCompiler, slot, out hasConstructor)) break;

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

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

            if (!hasConstructor)
            {
                CreateDefaultConstructor(slot);
            }

            if (!isForeign)
            {
                // Update the class with the number of fields.
                bytecode[numFieldsInstruction] = (byte)fields.Count;
            }

            enclosingClass = null;

            PopScope();
        }
Ejemplo n.º 4
0
        // Compiles a method definition inside a class body. Returns the symbol in the
        // method table for the new method.
        private bool Method(ClassCompiler classCompiler, int classSlot)
        {

            bool isForeign = Match(TokenType.Foreign);
            classCompiler.IsStaticMethod = Match(TokenType.Static);

            SignatureFn signatureFn = GetRule(_parser.Current.Type).Method;
            NextToken();

            if (signatureFn == null)
            {
                Error("Expect method definition.");
                return false;
            }

            // Build the method signature.
            Signature signature = SignatureFromToken(new Signature(), SignatureType.Getter);
            classCompiler.Signature = signature;

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

            if (classCompiler.IsStaticMethod && signature.Type == SignatureType.Initializer)
            {
                Error("A constructor cannot be static.");
            }

            String fullSignature = SignatureToString(signature);

            if (isForeign)
            {
                int constant = AddConstant(Obj.MakeString(fullSignature));

                EmitShortArg(Instruction.CONSTANT, constant);
            }
            else
            {
                Consume(TokenType.LeftBrace, "Expect '{' to begin method body.");
                methodCompiler.FinishBody(signature.Type == SignatureType.Initializer);
                methodCompiler.EndCompiler(fullSignature);
            }

            // Define the method. For a constructor, this defines the instance
            // initializer method.
            int methodSymbol = SignatureSymbol(signature);
            DefineMethod(classSlot, classCompiler.IsStaticMethod, methodSymbol);

            if (signature.Type == SignatureType.Initializer)
            {
                signature.Type = SignatureType.Method;
                int constructorSymbol = SignatureSymbol(signature);
                CreateConstructor(signature, methodSymbol);
                DefineMethod(classSlot, true, constructorSymbol);
            }

            return true;
        }
Ejemplo n.º 5
0
        // Compiles a method definition inside a class body. Returns the symbol in the
        // method table for the new method.
        private bool Method(ClassCompiler classCompiler, int classSlot, out bool hasConstructor)
        {
            hasConstructor = false;

            bool isForeign = Match(TokenType.TOKEN_FOREIGN);
            classCompiler.isStaticMethod = Match(TokenType.TOKEN_STATIC);

            SignatureFn signatureFn = GetRule(parser.current.type).method;
            NextToken();

            if (signatureFn == null)
            {
                Error("Expect method definition.");
                return false;
            }

            // Build the method signature.
            Signature signature = SignatureFromToken(SignatureType.SIG_GETTER);
            classCompiler.signature = signature;

            Compiler methodCompiler = new Compiler(parser, this, false);
            signatureFn(methodCompiler, signature);

            if (classCompiler.isStaticMethod && signature.Type == SignatureType.SIG_INITIALIZER)
            {
                Error("A constructor cannot be static.");
            }

            String fullSignature = SignatureToString(signature);

            if (isForeign)
            {
                int constant = AddConstant(new Value(fullSignature));

                EmitShortArg(Instruction.CONSTANT, constant);
            }
            else
            {
                Consume(TokenType.TOKEN_LEFT_BRACE, "Expect '{' to begin method body.");
                methodCompiler.FinishBody(signature.Type == SignatureType.SIG_INITIALIZER);
                methodCompiler.EndCompiler(fullSignature);
            }

            // Define the method. For a constructor, this defines the instance
            // initializer method.
            int methodSymbol = SignatureSymbol(signature);
            DefineMethod(classSlot, classCompiler.isStaticMethod, methodSymbol);

            if (signature.Type == SignatureType.SIG_INITIALIZER)
            {
                signature.Type = SignatureType.SIG_METHOD;
                int constructorSymbol = SignatureSymbol(signature);
                CreateConstructor(signature, methodSymbol);
                DefineMethod(classSlot, true, constructorSymbol);

                hasConstructor = true;
            }

            return true;
        }