// Compiles a method signature for a subscript operator. private static void SubscriptSignature(Compiler c, Signature signature) { signature.Type = SignatureType.Subscript; // The signature currently has "[" as its name since that was the token that // matched it. Clear that out. signature.Length = 0; signature.Name = ""; // Parse the parameters inside the subscript. c.FinishParameterList(signature); c.Consume(TokenType.RightBracket, "Expect ']' after parameters."); c.MaybeSetter(signature); }
// Compiles a method signature for an infix operator. static void InfixSignature(Compiler c, Signature signature) { // Add the RHS parameter. signature.Type = SignatureType.Method; signature.Arity = 1; // Parse the parameter name. c.Consume(TokenType.LeftParen, "Expect '(' after operator name."); c.DeclareNamedVariable(); c.Consume(TokenType.RightParen, "Expect ')' after parameter name."); }
// Compiles a method signature for an operator that can either be unary or // infix (i.e. "-"). private static void MixedSignature(Compiler c, Signature signature) { signature.Type = SignatureType.Getter; // If there is a parameter, it's an infix operator, otherwise it's unary. if (c.Match(TokenType.LeftParen)) { // Add the RHS parameter. signature.Type = SignatureType.Method; signature.Arity = 1; // Parse the parameter name. c.DeclareNamedVariable(); c.Consume(TokenType.RightParen, "Expect ')' after parameter name."); } }
private static void Call(Compiler c, bool allowAssignment) { c.IgnoreNewlines(); c.Consume(TokenType.Name, "Expect method name after '.'."); c.NamedCall(allowAssignment, Instruction.CALL_0); }
private static void Conditional(Compiler c, bool allowAssignment) { // Ignore newline after '?'. c.IgnoreNewlines(); // Jump to the else branch if the condition is false. int ifJump = c.EmitJump(Instruction.JUMP_IF); // Compile the then branch. c.ParsePrecedence(allowAssignment, Precedence.Ternary); c.Consume(TokenType.Colon, "Expect ':' after then branch of conditional operator."); c.IgnoreNewlines(); // Jump over the else branch when the if branch is taken. int elseJump = c.EmitJump(Instruction.JUMP); // Compile the else branch. c.PatchJump(ifJump); c.ParsePrecedence(allowAssignment, Precedence.Assignment); // Patch the jump over the else. c.PatchJump(elseJump); }
// A list literal. private static void List(Compiler c, bool allowAssignment) { // Load the List class. int listClassSymbol = c.parser.module.Variables.FindIndex(v => v.Name == "List"); //ASSERT(listClassSymbol != -1, "Should have already defined 'List' variable."); c.EmitShortArg(Instruction.LOAD_MODULE_VAR, listClassSymbol); // Instantiate a new list. c.CallMethod(0, "<instantiate>"); // Compile the list elements. Each one compiles to a ".add()" call. if (c.Peek() != TokenType.TOKEN_RIGHT_BRACKET) { do { c.IgnoreNewlines(); // Push a copy of the list since the add() call will consume it. c.Emit(Instruction.DUP); // The element. c.Expression(); c.CallMethod(1, "add(_)"); // Discard the result of the add() call. c.Emit(Instruction.POP); } while (c.Match(TokenType.TOKEN_COMMA)); } // Allow newlines before the closing ']'. c.IgnoreNewlines(); c.Consume(TokenType.TOKEN_RIGHT_BRACKET, "Expect ']' after list elements."); }
// Subscript or "array indexing" operator like `foo[bar]`. private static void Subscript(Compiler c, bool allowAssignment) { Signature signature = new Signature { Name = "", Length = 0, Type = SignatureType.Subscript, Arity = 0 }; // Parse the argument list. c.FinishArgumentList(signature); c.Consume(TokenType.RightBracket, "Expect ']' after arguments."); if (c.Match(TokenType.Eq)) { if (!allowAssignment) c.Error("Invalid assignment."); signature.Type = SignatureType.SubscriptSetter; // Compile the assigned value. c.ValidateNumParameters(++signature.Arity); c.Expression(); } c.CallSignature(Instruction.CALL_0, signature); }
// A list literal. private static void List(Compiler c, bool allowAssignment) { // Load the List class. c.LoadCoreVariable("List"); // Instantiate a new list. c.CallMethod(0, "new()"); // Compile the list elements. Each one compiles to a ".add()" call. if (c.Peek() != TokenType.RightBracket) { do { c.IgnoreNewlines(); if (c.Peek() == TokenType.RightBracket) break; // Push a copy of the list since the add() call will consume it. c.Emit(Instruction.DUP); // The element. c.Expression(); c.CallMethod(1, "add(_)"); // Discard the result of the add() call. c.Emit(Instruction.POP); } while (c.Match(TokenType.Comma)); } // Allow newlines before the closing ']'. c.IgnoreNewlines(); c.Consume(TokenType.RightBracket, "Expect ']' after list elements."); }
// A map literal. private static void Map(Compiler c, bool allowAssignment) { // Load the Map class. c.LoadCoreVariable("Map"); // Instantiate a new map. c.CallMethod(0, "new()"); // Compile the map elements. Each one is compiled to just invoke the // subscript setter on the map. if (c.Peek() != TokenType.RightBrace) { do { c.IgnoreNewlines(); if (c.Peek() == TokenType.RightBrace) break; // Push a copy of the map since the subscript call will consume it. c.Emit(Instruction.DUP); // The key. c.ParsePrecedence(false, Precedence.Primary); c.Consume(TokenType.Colon, "Expect ':' after map key."); c.IgnoreNewlines(); // The value. c.Expression(); c.CallMethod(2, "[_]=(_)"); // Discard the result of the setter call. c.Emit(Instruction.POP); } while (c.Match(TokenType.Comma)); } // Allow newlines before the closing '}'. c.IgnoreNewlines(); c.Consume(TokenType.RightBrace, "Expect '}' after map entries."); }
// Compiles a method signature for a constructor. private static void ConstructorSignature(Compiler c, Signature signature) { c.Consume(TokenType.TOKEN_NAME, "Expect constructor name after 'construct'."); // Capture the name. signature = c.SignatureFromToken(SignatureType.SIG_INITIALIZER); if (c.Match(TokenType.TOKEN_EQ)) { c.Error("A constructor cannot be a setter."); } if (!c.Match(TokenType.TOKEN_LEFT_PAREN)) { c.Error("A constructor cannot be a getter."); return; } if (c.Match(TokenType.TOKEN_RIGHT_PAREN)) return; c.FinishParameterList(signature); c.Consume(TokenType.TOKEN_RIGHT_PAREN, "Expect ')' after parameters."); }
// A parenthesized expression. private static void Grouping(Compiler c, bool allowAssignment) { c.Expression(); c.Consume(TokenType.RightParen, "Expect ')' after expression."); }
// Compiles a method signature for an operator that can either be unary or // infix (i.e. "-"). private static void MixedSignature(Compiler c, Signature signature) { signature.Type = SignatureType.SIG_GETTER; // If there is a parameter, it's an infix operator, otherwise it's unary. if (c.Match(TokenType.TOKEN_LEFT_PAREN)) { // Add the RHS parameter. signature.Type = SignatureType.SIG_METHOD; signature.Arity = 1; // Parse the parameter name. c.DeclareNamedVariable(); c.Consume(TokenType.TOKEN_RIGHT_PAREN, "Expect ')' after parameter name."); } }
// Compiles a method signature for an infix operator. static void InfixSignature(Compiler c, Signature signature) { // Add the RHS parameter. signature.Type = SignatureType.SIG_METHOD; signature.Arity = 1; // Parse the parameter name. c.Consume(TokenType.TOKEN_LEFT_PAREN, "Expect '(' after operator name."); c.DeclareNamedVariable(); c.Consume(TokenType.TOKEN_RIGHT_PAREN, "Expect ')' after parameter name."); }
// A map literal. private static void Map(Compiler c, bool allowAssignment) { // Load the Map class. int mapClassSymbol = c.parser.module.Variables.FindIndex(v => v.Name == "Map"); c.EmitShortArg(Instruction.LOAD_MODULE_VAR, mapClassSymbol); // Instantiate a new map. c.CallMethod(0, "<instantiate>"); // Compile the map elements. Each one is compiled to just invoke the // subscript setter on the map. if (c.Peek() != TokenType.TOKEN_RIGHT_BRACE) { do { c.IgnoreNewlines(); // Push a copy of the map since the subscript call will consume it. c.Emit(Instruction.DUP); // The key. c.ParsePrecedence(false, Precedence.PREC_PRIMARY); c.Consume(TokenType.TOKEN_COLON, "Expect ':' after map key."); // The value. c.Expression(); c.CallMethod(2, "[_]=(_)"); // Discard the result of the setter call. c.Emit(Instruction.POP); } while (c.Match(TokenType.TOKEN_COMMA)); } // Allow newlines before the closing '}'. c.IgnoreNewlines(); c.Consume(TokenType.TOKEN_RIGHT_BRACE, "Expect '}' after map entries."); }
// Compiles a method signature for a constructor. private static void ConstructorSignature(Compiler c, Signature signature) { c.Consume(TokenType.Name, "Expect constructor name after 'construct'."); // Capture the name. signature = c.SignatureFromToken(signature, SignatureType.Initializer); if (c.Match(TokenType.Eq)) { c.Error("A constructor cannot be a setter."); } if (!c.Match(TokenType.LeftParen)) { c.Error("A constructor cannot be a getter."); return; } if (c.Match(TokenType.RightParen)) return; c.FinishParameterList(signature); c.Consume(TokenType.RightParen, "Expect ')' after parameters."); }
private static void super_(Compiler c, bool allowAssignment) { ClassCompiler enclosingClass = c.GetEnclosingClass(); if (enclosingClass == null) { c.Error("Cannot use 'super' outside of a method."); } c.LoadThis(); // TODO: Super operator calls. // See if it's a named super call, or an unnamed one. if (c.Match(TokenType.Dot)) { // Compile the superclass call. c.Consume(TokenType.Name, "Expect method name after 'super.'."); c.NamedCall(allowAssignment, Instruction.SUPER_0); } else if (enclosingClass != null) { // No explicit name, so use the name of the enclosing method. Make sure we // check that enclosingClass isn't null first. We've already reported the // error, but we don't want to crash here. c.MethodCall(Instruction.SUPER_0, enclosingClass.Signature); } }
public static ObjFn Compile(WrenVM vm, ObjModule module, string sourcePath, string source, bool printErrors) { Parser parser = new Parser { vm = vm, Module = module, SourcePath = sourcePath, Source = source, TokenStart = 0, CurrentChar = 0, CurrentLine = 1, Current = { Type = TokenType.Error, Start = 0, Length = 0, Line = 0 }, SkipNewlines = true, PrintErrors = printErrors, HasError = false, Raw = "" }; Compiler compiler = new Compiler(parser, null, true); // Read the first token. compiler.NextToken(); compiler.IgnoreNewlines(); while (!compiler.Match(TokenType.Eof)) { compiler.Definition(); // If there is no newline, it must be the end of the block on the same line. if (!compiler.MatchLine()) { compiler.Consume(TokenType.Eof, "Expect end of file."); break; } } compiler.Emit(Instruction.NULL); compiler.Emit(Instruction.RETURN); // See if there are any implicitly declared module-level variables that never // got an explicit definition. // TODO: It would be nice if the error was on the line where it was used. for (int i = 0; i < parser.Module.Variables.Count; i++) { ModuleVariable t = parser.Module.Variables[i]; if (t.Container == Obj.Undefined) { compiler.Error(string.Format("Variable '{0}' is used but not defined.", t.Name)); } } return compiler.EndCompiler("(script)"); }
// A parenthesized expression. private static void Grouping(Compiler c, bool allowAssignment) { c.Expression(); c.Consume(TokenType.TOKEN_RIGHT_PAREN, "Expect ')' after expression."); }