// 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]; }
// 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(); }
// 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(); }
// 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; }
// 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; }