// Skips the rest of a block comment. static void SkipBlockComment(Parser parser) { NextChar(parser); // The opening "*". int nesting = 1; while (nesting > 0) { char c = PeekChar(parser); if (c == '\0') { LexError(parser, "Unterminated block comment."); return; } if (c == '/' && PeekNextChar(parser) == '*') { NextChar(parser); NextChar(parser); nesting++; continue; } if (c == '*' && PeekNextChar(parser) == '/') { NextChar(parser); NextChar(parser); nesting--; continue; } // Regular comment character. NextChar(parser); } }
static string GetTokenString(Parser parser) { return parser.Source.Substring(parser.TokenStart, parser.CurrentChar - parser.TokenStart); }
// If the current character is [c], then consumes it and makes a token of type // [two]. Otherwise makes a token of type [one]. static void TwoCharToken(Parser parser, char c, TokenType two, TokenType one) { if (PeekChar(parser) == c) { NextChar(parser); MakeToken(parser, two); return; } MakeToken(parser, one); }
// Skips the rest of the current line. static void SkipLineComment(Parser parser) { while (PeekChar(parser) != '\n' && PeekChar(parser) != '\0') { NextChar(parser); } }
// Finishes lexing an identifier. Handles reserved words. static void ReadName(Parser parser, TokenType type) { while (IsName(PeekChar(parser)) || IsDigit(PeekChar(parser))) { NextChar(parser); } string tokenName = GetTokenString(parser); switch (tokenName) { case "break": type = TokenType.Break; break; case "class": type = TokenType.Class; break; case "construct": type = TokenType.Construct; break; case "else": type = TokenType.Else; break; case "false": type = TokenType.False; break; case "for": type = TokenType.For; break; case "foreign": type = TokenType.Foreign; break; case "if": type = TokenType.If; break; case "import": type = TokenType.Import; break; case "in": type = TokenType.In; break; case "is": type = TokenType.Is; break; case "null": type = TokenType.Null; break; case "return": type = TokenType.Return; break; case "static": type = TokenType.Static; break; case "super": type = TokenType.Super; break; case "this": type = TokenType.This; break; case "true": type = TokenType.True; break; case "var": type = TokenType.Var; break; case "while": type = TokenType.While; break; } MakeToken(parser, type); }
// Parses the numeric value of the current token. static void MakeNumber(Parser parser, bool isHex) { string s = GetTokenString(parser); try { parser.number = isHex ? Convert.ToInt32(s, 16) : Convert.ToDouble(s, CultureInfo.InvariantCulture); } catch (OverflowException) { LexError(parser, "Number too big"); } MakeToken(parser, TokenType.TOKEN_NUMBER); }
// Returns the current character the parser is sitting on. static char PeekChar(Parser parser) { return parser.CurrentChar < parser.Source.Length ? parser.Source[parser.CurrentChar] : '\0'; }
// Finishes lexing a hexadecimal number literal. static void ReadHexNumber(Parser parser) { // Skip past the `x` used to denote a hexadecimal literal. NextChar(parser); // Iterate over all the valid hexadecimal digits found. while (ReadHexDigit(parser) != -1) { } MakeNumber(parser, true); }
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)"); }
// 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]; }
private static void LexError(Parser parser, string format) { parser.HasError = true; if (!parser.PrintErrors) return; Console.Error.Write("[{0} line {1}] Error: ", parser.SourcePath, parser.CurrentLine); Console.Error.WriteLine(format); }
// Finishes lexing an identifier. Handles reserved words. static void ReadName(Parser parser, TokenType type) { while (IsName(PeekChar(parser)) || IsDigit(PeekChar(parser))) { NextChar(parser); } string tokenName = GetTokenString(parser); switch (tokenName) { case "break": type = TokenType.TOKEN_BREAK; break; case "class": type = TokenType.TOKEN_CLASS; break; case "construct": type = TokenType.TOKEN_CONSTRUCT; break; case "else": type = TokenType.TOKEN_ELSE; break; case "false": type = TokenType.TOKEN_FALSE; break; case "for": type = TokenType.TOKEN_FOR; break; case "foreign": type = TokenType.TOKEN_FOREIGN; break; case "if": type = TokenType.TOKEN_IF; break; case "import": type = TokenType.TOKEN_IMPORT; break; case "in": type = TokenType.TOKEN_IN; break; case "is": type = TokenType.TOKEN_IS; break; case "null": type = TokenType.TOKEN_NULL; break; case "return": type = TokenType.TOKEN_RETURN; break; case "static": type = TokenType.TOKEN_STATIC; break; case "super": type = TokenType.TOKEN_SUPER; break; case "this": type = TokenType.TOKEN_THIS; break; case "true": type = TokenType.TOKEN_TRUE; break; case "var": type = TokenType.TOKEN_VAR; break; case "while": type = TokenType.TOKEN_WHILE; break; } MakeToken(parser, type); }
private static char peekchar(Parser parser) { throw new NotImplementedException(); }
static int ReadHexDigit(Parser parser) { char c = NextChar(parser); if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') return c - 'a' + 10; if (c >= 'A' && c <= 'F') return c - 'A' + 10; // Don't consume it if it isn't expected. Keeps us from reading past the end // of an unterminated string. parser.CurrentChar--; return -1; }
// Returns the character after the current character. static char PeekNextChar(Parser parser) { // If we're at the end of the source, don't read past it. return parser.CurrentChar >= parser.Source.Length - 1 ? '\0' : parser.Source[parser.CurrentChar + 1]; }
// Parses the numeric value of the current token. static void MakeNumber(Parser parser, bool isHex) { string s = GetTokenString(parser); try { parser.Number = isHex ? Convert.ToInt32(s, 16) : Convert.ToDouble(s, CultureInfo.InvariantCulture); } catch (OverflowException) { LexError(parser, "Number too big"); } catch (FormatException) { LexError(parser, "Number was not in correct format"); } MakeToken(parser, TokenType.Number); }
// Advances the parser forward one character. static char NextChar(Parser parser) { char c = PeekChar(parser); parser.CurrentChar++; if (c == '\n') parser.CurrentLine++; return c; }
// Finishes lexing a number literal. static void ReadNumber(Parser parser) { while (IsDigit(PeekChar(parser))) NextChar(parser); // See if it has a floating point. Make sure there is a digit after the "." // so we don't get confused by method calls on number literals. if (PeekChar(parser) == '.' && IsDigit(PeekNextChar(parser))) { NextChar(parser); while (IsDigit(PeekChar(parser))) NextChar(parser); } // See if the number is in scientific notation if (PeekChar(parser) == 'e' || PeekChar(parser) == 'E') { NextChar(parser); // if the exponant is negative if (PeekChar(parser) == '-') NextChar(parser); if (!IsDigit(PeekChar(parser))) { LexError(parser, "Unterminated scientific notation."); } while (IsDigit(PeekChar(parser))) NextChar(parser); } MakeNumber(parser, false); }
// Sets the parser's current token to the given [type] and current character // range. static void MakeToken(Parser parser, TokenType type) { parser.Current.Type = type; parser.Current.Start = parser.TokenStart; parser.Current.Length = parser.CurrentChar - parser.TokenStart; parser.Current.Line = parser.CurrentLine; // Make line tokens appear on the line containing the "\n". if (type == TokenType.Line) parser.Current.Line--; }
// Adds [c] to the current string literal being tokenized. static void AddStringChar(Parser parser, char c) { parser.Raw += c; }
// Sets the parser's current token to the given [type] and current character // range. static void MakeToken(Parser parser, TokenType type) { parser.current.type = type; parser.current.start = parser.tokenStart; parser.current.length = parser.currentChar - parser.tokenStart; parser.current.line = parser.currentLine; // Make line tokens appear on the line containing the "\n". if (type == TokenType.TOKEN_LINE) parser.current.line--; }