diff options
Diffstat (limited to 'src/z/js/parser/lexical_grammar.zig')
| -rw-r--r-- | src/z/js/parser/lexical_grammar.zig | 1137 |
1 files changed, 0 insertions, 1137 deletions
diff --git a/src/z/js/parser/lexical_grammar.zig b/src/z/js/parser/lexical_grammar.zig deleted file mode 100644 index 2211fbe..0000000 --- a/src/z/js/parser/lexical_grammar.zig +++ /dev/null @@ -1,1137 +0,0 @@ -const std = @import("std"); - -const ast = @import("ast.zig"); -pub const Loc = ast.Loc; - -pub const TokenType = enum { - // Keywords - await, - @"break", - case, - @"catch", - class, - @"const", - @"continue", - debugger, - default, - delete, - do, - @"else", - @"enum", - @"export", - extends, - false, - finally, - @"for", - function, - @"if", - import, - in, - instanceof, - let, - new, - null, - @"return", - super, - @"switch", - this, - throw, - true, - @"try", - typeof, - @"var", - void, - @"while", - with, - yield, - using, - async, - static, - get, - set, - of, - - // Identifiers and literals - identifier, - private_identifier, - number, - string, - bigint, - regex, - template_head, - template_middle, - template_tail, - no_sub_template, - - // Punctuators - lbrace, - rbrace, - lparen, - rparen, - lbracket, - rbracket, - dot, - semicolon, - comma, - @"...", - @"<", - @">", - @"<=", - @">=", - @"==", - @"!=", - @"===", - @"!==", - @"+", - @"-", - @"*", - @"%", - @"**", - @"++", - @"--", - @"<<", - @">>", - @">>>", - @"&", - @"|", - @"^", - @"!", - @"~", - @"&&", - @"||", - @"??", - @"?", - @":", - @"=", - @"+=", - @"-=", - @"*=", - @"%=", - @"**=", - @"<<=", - @">>=", - @">>>=", - @"&=", - @"|=", - @"^=", - @"&&=", - @"||=", - @"??=", - @"=>", - @"/", - @"/=", - @"?.", - @".", - @"#", - - // Special - from, - eof, - unknown, - - pub fn isKeyword(self: TokenType) bool { - return switch (self) { - .await, - .@"break", - .case, - .@"catch", - .class, - .@"const", - .@"continue", - .debugger, - .default, - .delete, - .do, - .@"else", - .@"enum", - .@"export", - .extends, - .false, - .finally, - .@"for", - .function, - .@"if", - .import, - .in, - .instanceof, - .let, - .new, - .null, - .@"return", - .super, - .@"switch", - .this, - .throw, - .true, - .@"try", - .typeof, - .@"var", - .void, - .@"while", - .with, - .yield, - .using, - .async, - .static, - .get, - .set, - .of, - .from, - => true, - else => false, - }; - } - - pub fn isIdentifier(self: TokenType) bool { - return self == .identifier or self.isKeyword(); - } - - pub fn isKeywordButNotLet(self: TokenType) bool { - return self.isKeyword() and self != .let; - } -}; - -pub const Token = struct { - kind: TokenType, - loc: Loc, - slice: []const u8, -}; - -pub const Lexer = struct { - source: []const u8, - pos: usize, - - const Self = @This(); - - pub fn init(source: []const u8) Self { - return .{ .source = source, .pos = 0 }; - } - - pub fn tokenize(source: []const u8) []const Token { - var buf: [4096]Token = undefined; - return tokenizeBuf(source, &buf); - } - - pub fn tokenizeBuf(source: []const u8, tokens: *[4096]Token) []const Token { - @setEvalBranchQuota(100000); - var lexer = Self.init(source); - var count: usize = 0; - - while (true) { - const tok = lexer.next(); - tokens[count] = tok; - count += 1; - if (tok.kind == .eof) break; - if (count >= tokens.len - 1) { - tokens[count] = .{ .kind = .eof, .loc = .{ .start = lexer.pos, .end = lexer.pos }, .slice = "" }; - break; - } - } - - return tokens[0..count]; - } - - fn ch(self: Self) ?u8 { - if (self.pos >= self.source.len) return null; - return self.source[self.pos]; - } - - fn chAt(self: Self, offset: usize) ?u8 { - const idx = self.pos + offset; - if (idx >= self.source.len) return null; - return self.source[idx]; - } - - fn advance(self: *Self) void { - if (self.pos < self.source.len) self.pos += 1; - } - - fn isWhiteSpace(c: u8) bool { - return switch (c) { - ' ', '\t', 0x0B, 0x0C => true, - else => false, - }; - } - - fn isLineTerminator(c: u8) bool { - return switch (c) { - '\n', '\r' => true, - else => false, - }; - } - - fn isDigit(c: u8) bool { - return c >= '0' and c <= '9'; - } - - fn isHexDigit(c: u8) bool { - return switch (c) { - '0'...'9', 'a'...'f', 'A'...'F' => true, - else => false, - }; - } - - fn isOctalDigit(c: u8) bool { - return c >= '0' and c <= '7'; - } - - fn isIdStart(c: u8) bool { - return switch (c) { - 'a'...'z', 'A'...'Z', '_', '$' => true, - else => c > 0x7F, - }; - } - - fn isIdContinue(c: u8) bool { - return isIdStart(c) or isDigit(c) or c == 0x200C or c == 0x200D; - } - - fn skipWhiteSpace(self: *Self) void { - while (self.ch()) |c| { - if (isWhiteSpace(c) or isLineTerminator(c)) { - self.advance(); - } else { - break; - } - } - } - - fn skipSingleLineComment(self: *Self) void { - self.advance(); - self.advance(); - while (self.ch()) |c| { - if (isLineTerminator(c)) break; - self.advance(); - } - } - - fn skipMultiLineComment(self: *Self) void { - self.advance(); - self.advance(); - while (self.ch()) |c| { - if (c == '*') { - if (self.chAt(1)) |n| { - if (n == '/') { - self.advance(); - self.advance(); - return; - } - } - } - self.advance(); - } - } - - fn skipHashbang(self: *Self) void { - if (self.ch()) |c| { - if (c == '#') { - self.advance(); - if (self.ch()) |n| { - if (n == '!') { - self.advance(); - while (self.ch()) |c2| { - if (isLineTerminator(c2)) break; - self.advance(); - } - } - } - } - } - } - - fn scanString(self: *Self, quote: u8) Token { - const start = self.pos; - self.advance(); - - while (self.ch()) |c| { - if (c == quote) { - self.advance(); - return .{ - .kind = .string, - .loc = .{ .start = start, .end = self.pos }, - .slice = self.source[start + 1 .. self.pos - 1], - }; - } - if (c == '\\') { - self.advance(); - if (self.ch()) |_| self.advance(); - } else if (isLineTerminator(c) and c != 0x2028 and c != 0x2029) { - break; - } else { - self.advance(); - } - } - - return .{ - .kind = .string, - .loc = .{ .start = start, .end = self.pos }, - .slice = self.source[start + 1 .. self.pos], - }; - } - - fn scanTemplate(self: *Self, start_kind: TokenType) Token { - const start = self.pos; - if (start_kind == .template_middle or start_kind == .template_head) { - self.advance(); - } - return self.scanTemplateRest(start); - } - - fn scanTemplateRest(self: *Self, start: usize) Token { - while (self.ch()) |c| { - if (c == '`') { - self.advance(); - return .{ - .kind = .no_sub_template, - .loc = .{ .start = start, .end = self.pos }, - .slice = self.source[start .. self.pos - 1], - }; - } - if (c == '$') { - if (self.chAt(1)) |n| { - if (n == '{') { - self.advance(); - self.advance(); - return .{ - .kind = .template_head, - .loc = .{ .start = start, .end = self.pos }, - .slice = self.source[start .. self.pos - 2], - }; - } - } - } - if (c == '\\') { - self.advance(); - if (self.ch()) |_| self.advance(); - } else { - self.advance(); - } - } - - return .{ - .kind = .no_sub_template, - .loc = .{ .start = start, .end = self.pos }, - .slice = self.source[start..self.pos], - }; - } - - fn scanTemplateMiddleOrTail(self: *Self) Token { - const start = self.pos; - self.advance(); - - while (self.ch()) |c| { - if (c == '`') { - self.advance(); - return .{ - .kind = .template_tail, - .loc = .{ .start = start, .end = self.pos }, - .slice = self.source[start .. self.pos - 1], - }; - } - if (c == '$') { - if (self.chAt(1)) |n| { - if (n == '{') { - self.advance(); - self.advance(); - return .{ - .kind = .template_middle, - .loc = .{ .start = start, .end = self.pos }, - .slice = self.source[start .. self.pos - 2], - }; - } - } - } - if (c == '\\') { - self.advance(); - if (self.ch()) |_| self.advance(); - } else { - self.advance(); - } - } - - return .{ - .kind = .template_tail, - .loc = .{ .start = start, .end = self.pos }, - .slice = self.source[start..self.pos], - }; - } - - fn scanNumber(self: *Self) Token { - const start = self.pos; - - if (self.ch()) |c| { - if (c == '0') { - self.advance(); - if (self.ch()) |n| { - switch (n) { - 'x', 'X' => return self.scanHex(start), - 'b', 'B' => return self.scanBinary(start), - 'o', 'O' => return self.scanOctal(start), - 'n' => { - self.advance(); - return .{ - .kind = .bigint, - .loc = .{ .start = start, .end = self.pos }, - .slice = self.source[start .. self.pos - 1], - }; - }, - 'e', 'E' => return self.scanDecimalAfterInt(start), - '.', '0'...'9' => return self.scanDecimalAfterInt(start), - else => {}, - } - } - } - } - - return self.scanDecimal(start); - } - - fn scanDecimal(self: *Self, start: usize) Token { - while (self.ch()) |c| { - if (!isDigit(c) and c != '_') break; - self.advance(); - } - - if (self.ch()) |c| { - if (c == '.') { - self.advance(); - while (self.ch()) |c2| { - if (!isDigit(c2) and c2 != '_') break; - self.advance(); - } - } - if (self.ch()) |c2| { - if (c2 == 'e' or c2 == 'E') { - return self.scanExponent(start); - } - } - if (self.ch()) |c2| { - if (c2 == 'n') { - self.advance(); - return .{ - .kind = .bigint, - .loc = .{ .start = start, .end = self.pos }, - .slice = self.source[start .. self.pos - 1], - }; - } - } - } - - return .{ - .kind = .number, - .loc = .{ .start = start, .end = self.pos }, - .slice = self.source[start..self.pos], - }; - } - - fn scanDecimalAfterInt(self: *Self, start: usize) Token { - while (self.ch()) |c| { - if (!isDigit(c) and c != '_') break; - self.advance(); - } - if (self.ch()) |c| { - if (c == '.') { - self.advance(); - while (self.ch()) |c2| { - if (!isDigit(c2) and c2 != '_') break; - self.advance(); - } - } - if (self.ch()) |c2| { - if (c2 == 'e' or c2 == 'E') { - return self.scanExponent(start); - } - } - if (self.ch()) |c2| { - if (c2 == 'n') { - self.advance(); - return .{ - .kind = .bigint, - .loc = .{ .start = start, .end = self.pos }, - .slice = self.source[start .. self.pos - 1], - }; - } - } - } - return .{ - .kind = .number, - .loc = .{ .start = start, .end = self.pos }, - .slice = self.source[start..self.pos], - }; - } - - fn scanExponent(self: *Self, start: usize) Token { - self.advance(); - if (self.ch()) |c| { - if (c == '+' or c == '-') self.advance(); - } - while (self.ch()) |c| { - if (!isDigit(c) and c != '_') break; - self.advance(); - } - return .{ - .kind = .number, - .loc = .{ .start = start, .end = self.pos }, - .slice = self.source[start..self.pos], - }; - } - - fn scanHex(self: *Self, start: usize) Token { - self.advance(); - while (self.ch()) |c| { - if (!isHexDigit(c) and c != '_') break; - self.advance(); - } - if (self.ch()) |c| { - if (c == 'n') { - self.advance(); - return .{ - .kind = .bigint, - .loc = .{ .start = start, .end = self.pos }, - .slice = self.source[start .. self.pos - 1], - }; - } - } - return .{ - .kind = .number, - .loc = .{ .start = start, .end = self.pos }, - .slice = self.source[start..self.pos], - }; - } - - fn scanBinary(self: *Self, start: usize) Token { - self.advance(); - while (self.ch()) |c| { - if (c != '0' and c != '1' and c != '_') break; - self.advance(); - } - if (self.ch()) |c| { - if (c == 'n') { - self.advance(); - return .{ - .kind = .bigint, - .loc = .{ .start = start, .end = self.pos }, - .slice = self.source[start .. self.pos - 1], - }; - } - } - return .{ - .kind = .number, - .loc = .{ .start = start, .end = self.pos }, - .slice = self.source[start..self.pos], - }; - } - - fn scanOctal(self: *Self, start: usize) Token { - self.advance(); - while (self.ch()) |c| { - if (!isOctalDigit(c) and c != '_') break; - self.advance(); - } - if (self.ch()) |c| { - if (c == 'n') { - self.advance(); - return .{ - .kind = .bigint, - .loc = .{ .start = start, .end = self.pos }, - .slice = self.source[start .. self.pos - 1], - }; - } - } - return .{ - .kind = .number, - .loc = .{ .start = start, .end = self.pos }, - .slice = self.source[start..self.pos], - }; - } - - fn scanIdentifierOrKeyword(self: *Self) Token { - const start = self.pos; - while (self.ch()) |c| { - if (!isIdContinue(c)) break; - self.advance(); - } - - const slice = self.source[start..self.pos]; - const kind = keywordFromString(slice); - - return .{ - .kind = kind, - .loc = .{ .start = start, .end = self.pos }, - .slice = slice, - }; - } - - fn scanPrivateIdentifier(self: *Self) Token { - const start = self.pos; - self.advance(); - while (self.ch()) |c| { - if (!isIdContinue(c)) break; - self.advance(); - } - return .{ - .kind = .private_identifier, - .loc = .{ .start = start, .end = self.pos }, - .slice = self.source[start..self.pos], - }; - } - - fn scanRegex(self: *Self) Token { - const start = self.pos; - self.advance(); - - var in_class = false; - while (self.ch()) |c| { - if (c == '\\') { - self.advance(); - if (self.ch()) |_| self.advance(); - } else if (c == '[') { - in_class = true; - self.advance(); - } else if (c == ']') { - in_class = false; - self.advance(); - } else if (c == '/' and !in_class) { - self.advance(); - break; - } else if (isLineTerminator(c)) { - break; - } else { - self.advance(); - } - } - - while (self.ch()) |c| { - if (!isIdContinue(c)) break; - self.advance(); - } - - return .{ - .kind = .regex, - .loc = .{ .start = start, .end = self.pos }, - .slice = self.source[start..self.pos], - }; - } - - fn scanPunctuator(self: *Self) Token { - const start = self.pos; - const c = self.ch().?; - - switch (c) { - '{' => { - self.advance(); - return simple(.lbrace, start); - }, - '}' => { - self.advance(); - return simple(.rbrace, start); - }, - '(' => { - self.advance(); - return simple(.lparen, start); - }, - ')' => { - self.advance(); - return simple(.rparen, start); - }, - '[' => { - self.advance(); - return simple(.lbracket, start); - }, - ']' => { - self.advance(); - return simple(.rbracket, start); - }, - ';' => { - self.advance(); - return simple(.semicolon, start); - }, - ',' => { - self.advance(); - return simple(.comma, start); - }, - ':' => { - self.advance(); - return simple(.@":", start); - }, - '?' => { - self.advance(); - if (self.ch()) |n| { - if (n == '?') { - self.advance(); - if (self.ch()) |n2| { - if (n2 == '=') { - self.advance(); - return simple(.@"??=", start); - } - } - return simple(.@"??", start); - } - if (n == '.') { - if (self.chAt(1)) |n2| { - if (!isDigit(n2)) { - self.advance(); - return simple(.@"?.", start); - } - } else { - self.advance(); - return simple(.@"?.", start); - } - } - } - return simple(.@"?", start); - }, - '~' => { - self.advance(); - return simple(.@"~", start); - }, - '.' => { - self.advance(); - if (self.ch()) |n| { - if (n == '.') { - if (self.chAt(1)) |n2| { - if (n2 == '.') { - self.advance(); - self.advance(); - return simple(.@"...", start); - } - } - } - } - return simple(.@".", start); - }, - '#' => { - self.advance(); - if (self.ch()) |n| { - if (isIdStart(n)) { - return self.scanPrivateIdentifier(); - } - } - return simple(.@"#", start); - }, - '+' => { - self.advance(); - if (self.ch()) |n| { - if (n == '+') { - self.advance(); - return simple(.@"++", start); - } - if (n == '=') { - self.advance(); - return simple(.@"+=", start); - } - } - return simple(.@"+", start); - }, - '-' => { - self.advance(); - if (self.ch()) |n| { - if (n == '-') { - self.advance(); - return simple(.@"--", start); - } - if (n == '=') { - self.advance(); - return simple(.@"-=", start); - } - } - return simple(.@"-", start); - }, - '*' => { - self.advance(); - if (self.ch()) |n| { - if (n == '*') { - self.advance(); - if (self.ch()) |n2| { - if (n2 == '=') { - self.advance(); - return simple(.@"**=", start); - } - } - return simple(.@"**", start); - } - if (n == '=') { - self.advance(); - return simple(.@"*=", start); - } - } - return simple(.@"*", start); - }, - '%' => { - self.advance(); - if (self.ch()) |n| { - if (n == '=') { - self.advance(); - return simple(.@"%=", start); - } - } - return simple(.@"%", start); - }, - '=' => { - self.advance(); - if (self.ch()) |n| { - if (n == '=') { - self.advance(); - if (self.ch()) |n2| { - if (n2 == '=') { - self.advance(); - return simple(.@"===", start); - } - } - return simple(.@"==", start); - } - if (n == '>') { - self.advance(); - return simple(.@"=>", start); - } - } - return simple(.@"=", start); - }, - '!' => { - self.advance(); - if (self.ch()) |n| { - if (n == '=') { - self.advance(); - if (self.ch()) |n2| { - if (n2 == '=') { - self.advance(); - return simple(.@"!==", start); - } - } - return simple(.@"!=", start); - } - } - return simple(.@"!", start); - }, - '<' => { - self.advance(); - if (self.ch()) |n| { - if (n == '<') { - self.advance(); - if (self.ch()) |n2| { - if (n2 == '=') { - self.advance(); - return simple(.@"<<=", start); - } - } - return simple(.@"<<", start); - } - if (n == '=') { - self.advance(); - return simple(.@"<=", start); - } - } - return simple(.@"<", start); - }, - '>' => { - self.advance(); - if (self.ch()) |n| { - if (n == '>') { - self.advance(); - if (self.ch()) |n2| { - if (n2 == '>') { - self.advance(); - if (self.ch()) |n3| { - if (n3 == '=') { - self.advance(); - return simple(.@">>>=", start); - } - } - return simple(.@">>>", start); - } - if (n2 == '=') { - self.advance(); - return simple(.@">>=", start); - } - } - return simple(.@">>", start); - } - if (n == '=') { - self.advance(); - return simple(.@">=", start); - } - } - return simple(.@">", start); - }, - '&' => { - self.advance(); - if (self.ch()) |n| { - if (n == '&') { - self.advance(); - if (self.ch()) |n2| { - if (n2 == '=') { - self.advance(); - return simple(.@"&&=", start); - } - } - return simple(.@"&&", start); - } - if (n == '=') { - self.advance(); - return simple(.@"&=", start); - } - } - return simple(.@"&", start); - }, - '|' => { - self.advance(); - if (self.ch()) |n| { - if (n == '|') { - self.advance(); - if (self.ch()) |n2| { - if (n2 == '=') { - self.advance(); - return simple(.@"||=", start); - } - } - return simple(.@"||", start); - } - if (n == '=') { - self.advance(); - return simple(.@"|=", start); - } - } - return simple(.@"|", start); - }, - '^' => { - self.advance(); - if (self.ch()) |n| { - if (n == '=') { - self.advance(); - return simple(.@"^=", start); - } - } - return simple(.@"^", start); - }, - '`' => { - self.advance(); - return self.scanTemplateRest(start); - }, - '\'' => return self.scanString('\''), - '"' => return self.scanString('"'), - '/' => { - self.advance(); - if (self.ch()) |n| { - if (n == '/') { - self.skipSingleLineComment(); - return self.next(); - } - if (n == '*') { - self.skipMultiLineComment(); - return self.next(); - } - if (n == '=') { - self.advance(); - return simple(.@"/=", start); - } - } - return simple(.@"/", start); - }, - else => { - if (isLineTerminator(c) or isWhiteSpace(c)) { - self.skipWhiteSpace(); - return self.next(); - } - if (isDigit(c)) return self.scanNumber(); - if (isIdStart(c)) return self.scanIdentifierOrKeyword(); - if (c == '\\') { - self.advance(); - return self.scanIdentifierOrKeyword(); - } - self.advance(); - return simple(.unknown, start); - }, - } - } - - fn simple(kind: TokenType, start: usize) Token { - return .{ .kind = kind, .loc = .{ .start = start, .end = start }, .slice = "" }; - } - - fn simpleWithSlice(comptime kind: TokenType, start: usize, slice: []const u8) Token { - return .{ .kind = kind, .loc = .{ .start = start, .end = start }, .slice = slice }; - } - - pub fn next(self: *Self) Token { - if (self.ch()) |c| { - _ = c; - // Handle hashbang at beginning - if (self.pos == 0) { - if (self.ch()) |c2| { - if (c2 == '#') { - self.skipHashbang(); - return self.next(); - } - } - } - return self.scanPunctuator(); - } - return simple(.eof, self.pos); - } - - fn keywordFromString(s: []const u8) TokenType { - const keywords = comptime keywords: { - const arr = struct { - const data = [_]struct { key: []const u8, val: TokenType }{ - .{ .key = "await", .val = .await }, - .{ .key = "break", .val = .@"break" }, - .{ .key = "case", .val = .case }, - .{ .key = "catch", .val = .@"catch" }, - .{ .key = "class", .val = .class }, - .{ .key = "const", .val = .@"const" }, - .{ .key = "continue", .val = .@"continue" }, - .{ .key = "debugger", .val = .debugger }, - .{ .key = "default", .val = .default }, - .{ .key = "delete", .val = .delete }, - .{ .key = "do", .val = .do }, - .{ .key = "else", .val = .@"else" }, - .{ .key = "enum", .val = .@"enum" }, - .{ .key = "export", .val = .@"export" }, - .{ .key = "extends", .val = .extends }, - .{ .key = "false", .val = .false }, - .{ .key = "finally", .val = .finally }, - .{ .key = "for", .val = .@"for" }, - .{ .key = "function", .val = .function }, - .{ .key = "if", .val = .@"if" }, - .{ .key = "import", .val = .import }, - .{ .key = "in", .val = .in }, - .{ .key = "instanceof", .val = .instanceof }, - .{ .key = "let", .val = .let }, - .{ .key = "new", .val = .new }, - .{ .key = "null", .val = .null }, - .{ .key = "return", .val = .@"return" }, - .{ .key = "super", .val = .super }, - .{ .key = "switch", .val = .@"switch" }, - .{ .key = "this", .val = .this }, - .{ .key = "throw", .val = .throw }, - .{ .key = "true", .val = .true }, - .{ .key = "try", .val = .@"try" }, - .{ .key = "typeof", .val = .typeof }, - .{ .key = "var", .val = .@"var" }, - .{ .key = "void", .val = .void }, - .{ .key = "while", .val = .@"while" }, - .{ .key = "with", .val = .with }, - .{ .key = "yield", .val = .yield }, - .{ .key = "using", .val = .using }, - .{ .key = "async", .val = .async }, - .{ .key = "static", .val = .static }, - .{ .key = "get", .val = .get }, - .{ .key = "set", .val = .set }, - .{ .key = "of", .val = .of }, - .{ .key = "from", .val = .from }, - }; - }; - break :keywords arr.data; - }; - inline for (keywords) |entry| { - if (std.mem.eql(u8, s, entry.key)) return entry.val; - } - return .identifier; - } - - pub fn tokenizeComptime(comptime source: []const u8) []const Token { - comptime { - return tokenize(source); - } - } -}; |