From 006674d7e08146a9bd980e2138ce9444d2e40e6f Mon Sep 17 00:00:00 2001 From: Nathan Reiner Date: Thu, 30 Jul 2026 09:19:48 +0200 Subject: add formatting --- src/z/js/parser/syntactic_grammar.zig | 1907 --------------------------------- 1 file changed, 1907 deletions(-) delete mode 100644 src/z/js/parser/syntactic_grammar.zig (limited to 'src/z/js/parser/syntactic_grammar.zig') diff --git a/src/z/js/parser/syntactic_grammar.zig b/src/z/js/parser/syntactic_grammar.zig deleted file mode 100644 index 065e827..0000000 --- a/src/z/js/parser/syntactic_grammar.zig +++ /dev/null @@ -1,1907 +0,0 @@ -const std = @import("std"); - -const lex = @import("lexical_grammar.zig"); -const Token = lex.Token; -const TokenType = lex.TokenType; -const Loc = lex.Loc; - -const ast = @import("ast.zig"); -const Expr = ast.Expr; -const ExprType = ast.ExprType; -const Stmt = ast.Stmt; -const StmtType = ast.StmtType; -const Pat = ast.Pat; -const PatType = ast.PatType; -const Decl = ast.Decl; -const DeclType = ast.DeclType; -const Lit = ast.Lit; -const LitType = ast.LitType; - -pub const ParseError = error{ - UnexpectedToken, - ExpectedToken, - ExpectedExpression, - ExpectedStatement, - ExpectedIdentifier, - UnclosedString, - InvalidNumber, - InvalidEscape, - UnterminatedComment, - ExpectedParameterName, - ExpectedPropertyName, - UnterminatedTemplate, - InvalidAssignmentTarget, - TooManyTokens, -}; - -const BP = struct { - const comma = 1; - const assign = 2; - const yield = 3; - const conditional = 4; - const coalesce = 5; - const log_or = 6; - const log_and = 7; - const bit_or = 8; - const bit_xor = 9; - const bit_and = 10; - const equality = 11; - const relational = 12; - const shift = 13; - const additive = 14; - const multiplicative = 15; - const exponentiation = 16; - const unary = 17; - const update = 18; - const call = 19; - const member = 20; -}; - -pub const NodeArena = struct { - exprs: [8192]Expr = undefined, - expr_count: usize = 0, - stmts: [8192]Stmt = undefined, - stmt_count: usize = 0, - decls: [1024]Decl = undefined, - decl_count: usize = 0, - pats: [1024]Pat = undefined, - pat_count: usize = 0, - lits: [1024]Lit = undefined, - lit_count: usize = 0, - string_buf: [65536]u8 = undefined, - string_pos: usize = 0, - - fn exprPtr(self: *NodeArena, data: ExprType, loc: Loc) *const Expr { - const idx = self.expr_count; - self.exprs[idx] = .{ .loc = loc, .data = data }; - self.expr_count += 1; - return &self.exprs[idx]; - } - - fn stmtPtr(self: *NodeArena, data: StmtType, loc: Loc) *const Stmt { - const idx = self.stmt_count; - self.stmts[idx] = .{ .loc = loc, .data = data }; - self.stmt_count += 1; - return &self.stmts[idx]; - } - - fn declPtr(self: *NodeArena, data: DeclType, loc: Loc) *const Decl { - const idx = self.decl_count; - self.decls[idx] = .{ .loc = loc, .data = data }; - self.decl_count += 1; - return &self.decls[idx]; - } - - fn patPtr(self: *NodeArena, data: PatType, loc: Loc) *const Pat { - const idx = self.pat_count; - self.pats[idx] = .{ .loc = loc, .data = data }; - self.pat_count += 1; - return &self.pats[idx]; - } - - fn litPtr(self: *NodeArena, data: LitType, loc: Loc) *const Lit { - const idx = self.lit_count; - self.lits[idx] = .{ .loc = loc, .data = data }; - self.lit_count += 1; - return &self.lits[idx]; - } -}; - -pub const Parser = struct { - tokens: []const Token, - pos: usize, - source: []const u8, - arena: *NodeArena, - - pub fn init(tokens: []const Token, source: []const u8, arena: *NodeArena) @This() { - return .{ .tokens = tokens, .pos = 0, .source = source, .arena = arena }; - } - - fn tok(self: *@This()) Token { - return self.tokens[self.pos]; - } - - fn peek(self: *@This()) TokenType { - return self.tokens[self.pos].kind; - } - - fn peekAt(self: *@This(), offset: usize) TokenType { - const idx = self.pos + offset; - if (idx >= self.tokens.len) return .eof; - return self.tokens[idx].kind; - } - - fn advance(self: *@This()) Token { - const t = self.tokens[self.pos]; - self.pos += 1; - return t; - } - - fn expect(self: *@This(), kind: TokenType) ParseError!Token { - if (self.peek() != kind) return ParseError.ExpectedToken; - return self.advance(); - } - - fn match(self: *@This(), kind: TokenType) bool { - if (self.peek() == kind) { - self.advance(); - return true; - } - return false; - } - - fn matchKeyword(self: *@This(), kw: TokenType) bool { - if (self.peek() == kw) { - self.advance(); - return true; - } - return false; - } - - fn isLineTerminatorBeforeNext(self: *@This()) bool { - if (self.pos == 0) return false; - const prev_token = self.tokens[self.pos - 1]; - const next_token = self.tokens[self.pos]; - // Check if there's a line terminator between prev and next tokens - var i = prev_token.loc.end; - while (i < next_token.loc.start) : (i += 1) { - if (i >= self.source.len) break; - const c = self.source[i]; - switch (c) { - '\n', '\r', 0x2028, 0x2029 => return true, - else => {}, - } - } - return false; - } - - fn as_identifier(self: *@This(), tok: Token) []const u8 { - return self.source[tok.loc.start..tok.loc.end]; - } - - fn tokenSlice(self: *@This(), tok: Token) []const u8 { - return self.source[tok.loc.start..tok.loc.end]; - } - - pub fn parseProgram(self: *@This()) ParseError!ast.Program { - var stmts: [2048]Stmt = undefined; - var count: usize = 0; - - while (self.peek() != .eof) { - const item = try self.parseStatementListItem(); - stmts[count] = item; - count += 1; - if (count >= stmts.len) return ParseError.TooManyTokens; - } - - const body = stmts[0..count]; - const loc: Loc = if (body.len > 0) .{ .start = body[0].loc.start, .end = body[body.len - 1].loc.end } else .{ .start = 0, .end = 0 }; - - return .{ .body = body, .loc = loc }; - } - - fn parseStatementListItem(self: *@This()) ParseError!Stmt { - switch (self.peek()) { - .function => return (try self.parseFunctionDecl(.normal, .stmt)).?, - .async => { - if (self.peekAt(1) == .function) { - return (try self.parseFunctionDecl(.async, .stmt)).?; - } - if (self.peekAt(1) == .@"*" and self.peekAt(2) == .function) { - return (try self.parseFunctionDecl(.async_generator, .stmt)).?; - } - return self.parseStatement(); - }, - .class => return (try self.parseClassDecl(.stmt)).?, - .@"const", .let => return self.parseLexicalDecl(), - .@"var" => return self.parseVarStmt(), - .using => return self.parseUsingStmt(), - .import => return try self.parseImport(), - .@"export" => return try self.parseExport(), - else => return self.parseStatement(), - } - } - - fn parseStatement(self: *@This()) ParseError!Stmt { - switch (self.peek()) { - .lbrace => return self.parseBlock(), - .semicolon => { - const t = self.advance(); - return Stmt{ .loc = .{ .start = t.loc.start, .end = t.loc.end }, .data = .{ .empty = {} } }; - }, - .@"if" => return self.parseIf(), - .@"while" => return self.parseWhile(), - .do => return self.parseDoWhile(), - .@"for" => return self.parseFor(), - .@"continue" => return self.parseContinue(), - .@"break" => return self.parseBreak(), - .@"return" => return self.parseReturn(), - .@"switch" => return self.parseSwitch(), - .throw => return self.parseThrow(), - .@"try" => return self.parseTry(), - .debugger => { - const t = self.advance(); - self.semicolon(); - return Stmt{ .loc = .{ .start = t.loc.start, .end = t.loc.end }, .data = .{ .debugger = {} } }; - }, - .with => return self.parseWith(), - .function => return (try self.parseFunctionDecl(.normal, .stmt)).?, - .async => { - if (self.peekAt(1) == .function) { - return (try self.parseFunctionDecl(.async, .stmt)).?; - } - if (self.peekAt(1) == .@"*") return self.parseExpressionStatement(); - if (self.peekAt(1).isIdentifier()) { - return self.parseExpressionStatement(); - } - return self.parseExpressionStatement(); - }, - .class => return (try self.parseClassDecl(.stmt)).?, - .@"const", .let => return self.parseLexicalDecl(), - .@"var" => return self.parseVarStmt(), - .import => return try self.parseImport(), - .@"export" => return try self.parseExport(), - else => return self.parseExpressionStatement(), - } - } - - fn parseBlock(self: *@This()) ParseError!Stmt { - const start = self.advance().loc.start; - var stmts: [1024]Stmt = undefined; - var count: usize = 0; - - while (self.peek() != .rbrace and self.peek() != .eof) { - const s = try self.parseStatementListItem(); - stmts[count] = s; - count += 1; - if (count >= stmts.len) return ParseError.TooManyTokens; - } - - const end_tok = try self.expect(.rbrace); - const end = end_tok.loc.end; - - const block = Stmt{ - .loc = .{ .start = start, .end = end }, - .data = .{ .block = .{ .loc = .{ .start = start, .end = end }, .body = stmts[0..count] } }, - }; - return block; - } - - fn parseIf(self: *@This()) ParseError!Stmt { - const start = self.advance().loc.start; - try self.expect(.lparen); - const condition = try self.parseExpr(0); - try self.expect(.rparen); - const consequent = try self.statementPointer(); - var alternate: ?*const Stmt = null; - if (self.matchKeyword(.@"else")) { - alternate = try self.statementPointer(); - } - const end = if (alternate) |a| a.loc.end else consequent.loc.end; - return Stmt{ - .loc = .{ .start = start, .end = end }, - .data = .{ .@"if" = .{ .loc = .{ .start = start, .end = end }, .condition = condition, .consequent = consequent, .alternate = alternate } }, - }; - } - - fn parseWhile(self: *@This()) ParseError!Stmt { - const start = self.advance().loc.start; - try self.expect(.lparen); - const condition = try self.parseExpr(0); - try self.expect(.rparen); - const body = try self.statementPointer(); - return Stmt{ - .loc = .{ .start = start, .end = body.loc.end }, - .data = .{ .@"while" = .{ .loc = .{ .start = start, .end = body.loc.end }, .condition = condition, .body = body } }, - }; - } - - fn parseDoWhile(self: *@This()) ParseError!Stmt { - const start = self.advance().loc.start; - const body = try self.statementPointer(); - try self.expect(.@"while"); - try self.expect(.lparen); - const condition = try self.parseExpr(0); - try self.expect(.rparen); - self.semicolon(); - return Stmt{ - .loc = .{ .start = start, .end = condition.loc.end }, - .data = .{ .do_while = .{ .loc = .{ .start = start, .end = condition.loc.end }, .body = body, .condition = condition } }, - }; - } - - fn parseFor(self: *@This()) ParseError!Stmt { - const start = self.advance().loc.start; - try self.expect(.lparen); - - var init: ?union(enum) { expr: *const Expr, decl: *const Decl } = null; - var condition: ?*const Expr = null; - var update: ?*const Expr = null; - - if (self.peek() != .semicolon) { - if (self.peek() == .@"var" or self.peek() == .let or self.peek() == .@"const") { - const decl = try self.parseForDeclaration(); - init = .{ .decl = decl }; - } else { - const expr = try self.parseExpr(0); - init = .{ .expr = expr }; - } - } - - if (self.matchKeyword(.in)) { - const right = try self.parseExpr(0); - try self.expect(.rparen); - const body = try self.statementPointer(); - return Stmt{ - .loc = .{ .start = start, .end = body.loc.end }, - .data = .{ .for_in = .{ .loc = .{ .start = start, .end = body.loc.end }, .left = init.?, .right = right, .body = body } }, - }; - } - - if (self.matchKeyword(.of)) { - const right = try self.parseExpr(0); - try self.expect(.rparen); - const body = try self.statementPointer(); - return Stmt{ - .loc = .{ .start = start, .end = body.loc.end }, - .data = .{ .for_of = .{ .loc = .{ .start = start, .end = body.loc.end }, .await_token = false, .left = init.?, .right = right, .body = body } }, - }; - } - - try self.expect(.semicolon); - if (self.peek() != .semicolon and self.peek() != .rparen) { - condition = try self.parseExpr(0); - } - try self.expect(.semicolon); - if (self.peek() != .rparen) { - update = try self.parseExpr(0); - } - try self.expect(.rparen); - const body = try self.statementPointer(); - return Stmt{ - .loc = .{ .start = start, .end = body.loc.end }, - .data = .{ .@"for" = .{ .loc = .{ .start = start, .end = body.loc.end }, .init = init, .condition = condition, .update = update, .body = body } }, - }; - } - - fn parseForDeclaration(self: *@This()) ParseError!*const Decl { - const kind_tok = self.advance(); - var decls: [128]ast.VarDeclarator = undefined; - var count: usize = 0; - - while (true) { - const pat = try self.parseBindingPattern(); - var init: ?*const Expr = null; - if (self.match(.@"=")) { - init = try self.parseExpr(0); - } - decls[count] = .{ .loc = pat.loc, .id = pat, .init = init }; - count += 1; - if (!self.match(.comma)) break; - } - - const kind: ast.VarKind = switch (kind_tok.kind) { - .@"var" => .@"var", - .let => .let, - .@"const" => .@"const", - else => return ParseError.UnexpectedToken, - }; - - return self.arena.declPtr(.{ .@"var" = .{ .loc = .{ .start = kind_tok.loc.start, .end = decls[count - 1].loc.end }, .kind = kind, .decls = decls[0..count] } }, .{ .start = kind_tok.loc.start, .end = decls[count - 1].loc.end }); - } - - fn parseContinue(self: *@This()) ParseError!Stmt { - const start = self.advance().loc.start; - var label: ?[]const u8 = null; - if (self.peek() == .identifier and !self.isLineTerminatorBeforeNext()) { - const t = self.advance(); - label = self.tokenSlice(t); - } - self.semicolon(); - return Stmt{ .loc = .{ .start = start, .end = start + 8 }, .data = .{ .@"continue" = .{ .loc = .{ .start = start, .end = start + 8 }, .label = label } } }; - } - - fn parseBreak(self: *@This()) ParseError!Stmt { - const start = self.advance().loc.start; - var label: ?[]const u8 = null; - if (self.peek() == .identifier and !self.isLineTerminatorBeforeNext()) { - const t = self.advance(); - label = self.tokenSlice(t); - } - self.semicolon(); - return Stmt{ .loc = .{ .start = start, .end = start + 5 }, .data = .{ .@"break" = .{ .loc = .{ .start = start, .end = start + 5 }, .label = label } } }; - } - - fn parseReturn(self: *@This()) ParseError!Stmt { - const start = self.advance().loc.start; - var arg: ?*const Expr = null; - if (self.peek() != .semicolon and self.peek() != .rbrace and self.peek() != .eof and !self.isLineTerminatorBeforeNext()) { - arg = try self.parseExpr(0); - } - self.semicolon(); - return Stmt{ .loc = .{ .start = start, .end = arg.?.loc.end }, .data = .{ .@"return" = .{ .loc = .{ .start = start, .end = if (arg) |a| a.loc.end else start + 6 }, .arg = arg } } }; - } - - fn parseSwitch(self: *@This()) ParseError!Stmt { - const start = self.advance().loc.start; - try self.expect(.lparen); - const discriminant = try self.parseExpr(0); - try self.expect(.rparen); - try self.expect(.lbrace); - - var cases: [256]ast.SwitchCase = undefined; - var case_count: usize = 0; - - while (self.peek() == .case or self.peek() == .default) { - const case_start = self.tok().loc.start; - if (self.matchKeyword(.case)) { - const case_test = try self.parseExpr(0); - try self.expect(.@":"); - var consequent: [128]Stmt = undefined; - var cons_count: usize = 0; - while (self.peek() != .case and self.peek() != .default and self.peek() != .rbrace and self.peek() != .eof) { - const s = try self.parseStatementListItem(); - consequent[cons_count] = s; - cons_count += 1; - } - cases[case_count] = .{ .loc = .{ .start = case_start, .end = self.tok().loc.start }, .condition = case_test, .consequent = consequent[0..cons_count] }; - case_count += 1; - } else if (self.matchKeyword(.default)) { - try self.expect(.@":"); - var consequent: [128]Stmt = undefined; - var cons_count: usize = 0; - while (self.peek() != .case and self.peek() != .default and self.peek() != .rbrace and self.peek() != .eof) { - const s = try self.parseStatementListItem(); - consequent[cons_count] = s; - cons_count += 1; - } - cases[case_count] = .{ .loc = .{ .start = case_start, .end = self.tok().loc.start }, .condition = null, .consequent = consequent[0..cons_count] }; - case_count += 1; - } - } - - const end_tok = try self.expect(.rbrace); - return Stmt{ - .loc = .{ .start = start, .end = end_tok.loc.end }, - .data = .{ .@"switch" = .{ .loc = .{ .start = start, .end = end_tok.loc.end }, .discriminant = discriminant, .cases = cases[0..case_count] } }, - }; - } - - fn parseThrow(self: *@This()) ParseError!Stmt { - const start = self.advance().loc.start; - if (self.isLineTerminatorBeforeNext()) return ParseError.ExpectedExpression; - const arg = try self.parseExpr(0); - self.semicolon(); - return Stmt{ .loc = .{ .start = start, .end = arg.loc.end }, .data = .{ .throw = .{ .loc = .{ .start = start, .end = arg.loc.end }, .arg = arg } } }; - } - - fn parseTry(self: *@This()) ParseError!Stmt { - const start = self.advance().loc.start; - const block = try self.parseBlockStmt(); - var handler: ?ast.CatchClause = null; - var finalizer: ?ast.BlockStmt = null; - - if (self.matchKeyword(.@"catch")) { - var param: ?Pat = null; - if (self.match(.lparen)) { - const pat = try self.parseBindingPattern(); - param = pat; - try self.expect(.rparen); - } - const catch_body = try self.parseBlockStmt(); - handler = .{ .loc = .{ .start = block.loc.start, .end = catch_body.loc.end }, .param = param, .body = catch_body }; - } - - if (self.matchKeyword(.finally)) { - const finally_body = try self.parseBlockStmt(); - finalizer = finally_body; - } - - const end = if (finalizer) |f| f.loc.end else if (handler) |h| h.loc.end else block.loc.end; - return Stmt{ - .loc = .{ .start = start, .end = end }, - .data = .{ .@"try" = .{ .loc = .{ .start = start, .end = end }, .block = block, .handler = handler, .finalizer = finalizer } }, - }; - } - - fn parseWith(self: *@This()) ParseError!Stmt { - const start = self.advance().loc.start; - try self.expect(.lparen); - const obj = try self.parseExpr(0); - try self.expect(.rparen); - const body = try self.statementPointer(); - return Stmt{ - .loc = .{ .start = start, .end = body.loc.end }, - .data = .{ .with = .{ .loc = .{ .start = start, .end = body.loc.end }, .obj = obj, .body = body } }, - }; - } - - fn parseBlockStmt(self: *@This()) ParseError!ast.BlockStmt { - const start = try self.expect(.lbrace); - var stmts: [1024]Stmt = undefined; - var count: usize = 0; - - while (self.peek() != .rbrace and self.peek() != .eof) { - const s = try self.parseStatementListItem(); - stmts[count] = s; - count += 1; - if (count >= stmts.len) return ParseError.TooManyTokens; - } - - const end = try self.expect(.rbrace); - return .{ .loc = .{ .start = start.loc.start, .end = end.loc.end }, .body = stmts[0..count] }; - } - - fn parseVarStmt(self: *@This()) ParseError!Stmt { - const kind_tok = self.advance(); - var decls: [128]ast.VarDeclarator = undefined; - var count: usize = 0; - - while (true) { - const pat = try self.parseBindingPattern(); - var init: ?*const Expr = null; - if (self.match(.@"=")) { - init = try self.parseExpr(0); - } - decls[count] = .{ .loc = pat.loc, .id = pat, .init = init }; - count += 1; - if (!self.match(.comma)) break; - } - - self.semicolon(); - const kind: ast.VarKind = .@"var"; - return Stmt{ - .loc = .{ .start = kind_tok.loc.start, .end = decls[count - 1].loc.end }, - .data = .{ .variable = .{ .loc = .{ .start = kind_tok.loc.start, .end = decls[count - 1].loc.end }, .kind = kind, .decls = decls[0..count] } }, - }; - } - - fn parseLexicalDecl(self: *@This()) ParseError!Stmt { - const kind_tok = self.advance(); - var decls: [128]ast.VarDeclarator = undefined; - var count: usize = 0; - - while (true) { - const pat = try self.parseBindingPattern(); - var init: ?*const Expr = null; - if (kind_tok.kind == .@"const") { - if (!self.match(.@"=")) return ParseError.ExpectedToken; - init = try self.parseExpr(0); - } else if (self.match(.@"=")) { - init = try self.parseExpr(0); - } - decls[count] = .{ .loc = pat.loc, .id = pat, .init = init }; - count += 1; - if (!self.match(.comma)) break; - } - - self.semicolon(); - const kind: ast.VarKind = switch (kind_tok.kind) { - .let => .let, - .@"const" => .@"const", - else => return ParseError.UnexpectedToken, - }; - return Stmt{ - .loc = .{ .start = kind_tok.loc.start, .end = decls[count - 1].loc.end }, - .data = .{ .lexical = .{ .loc = .{ .start = kind_tok.loc.start, .end = decls[count - 1].loc.end }, .kind = kind, .decls = decls[0..count] } }, - }; - } - - fn parseUsingStmt(self: *@This()) ParseError!Stmt { - const start = self.advance().loc.start; - var decls: [128]ast.VarDeclarator = undefined; - var count: usize = 0; - - while (true) { - const id_tok = try self.expect(.identifier); - const id = self.tokenSlice(id_tok); - var init: ?*const Expr = null; - if (self.match(.@"=")) { - init = try self.parseExpr(0); - } - decls[count] = .{ .loc = .{ .start = id_tok.loc.start, .end = if (init) |e| e.loc.end else id_tok.loc.end }, .id = Pat{ .loc = .{ .start = id_tok.loc.start, .end = id_tok.loc.end }, .data = .{ .ident = id } }, .init = init }; - count += 1; - if (!self.match(.comma)) break; - } - - self.semicolon(); - return Stmt{ - .loc = .{ .start = start, .end = decls[count - 1].loc.end }, - .data = .{ .using = .{ .loc = .{ .start = start, .end = decls[count - 1].loc.end }, .decls = decls[0..count] } }, - }; - } - - fn parseExpressionStatement(self: *@This()) ParseError!Stmt { - const expr = try self.parseExpr(0); - self.semicolon(); - return Stmt{ .loc = expr.loc, .data = .{ .expr = .{ .loc = expr.loc, .expr = expr } } }; - } - - fn parseFunctionDecl(self: *@This(), fn_type: ast.FnType, mode: enum { stmt, expr }) ParseError!?Stmt { - const start = self.tok().loc.start; - - if (fn_type == .async) { - _ = self.advance(); // async - } else if (fn_type == .async_generator) { - _ = self.advance(); // async - _ = self.advance(); // * - } - - _ = self.advance(); // function - - if (fn_type == .normal and self.peek() == .@"*") { - self.advance(); - fn_type = .generator; - } - - var id: ?[]const u8 = null; - if (self.peek().isIdentifier()) { - const id_tok = self.advance(); - id = self.tokenSlice(id_tok); - } - - const params = try self.parseParams(); - const body = try self.parseFunctionBody(); - - const end = body.loc.end; - - if (mode == .stmt) { - return Stmt{ - .loc = .{ .start = start, .end = end }, - .data = .{ .function = .{ .loc = .{ .start = start, .end = end }, .fn_type = fn_type, .id = id, .params = params, .body = body } }, - }; - } - return null; - } - - fn parseFnExpr(self: *@This()) ParseError!*const Expr { - const start = self.advance().loc.start; - - var fn_type: ast.FnType = .normal; - if (self.peek() == .@"*") { - fn_type = .generator; - self.advance(); - } - - var id: ?[]const u8 = null; - if (self.peek() == .identifier or self.peek().isKeywordButNotLet()) { - const id_tok = self.advance(); - id = self.tokenSlice(id_tok); - } - - const params = try self.parseParams(); - const body = try self.parseFunctionBody(); - - return self.arena.exprPtr( - .{ .func = .{ .loc = .{ .start = start, .end = body.loc.end }, .fn_type = fn_type, .id = id, .params = params, .body = body } }, - .{ .start = start, .end = body.loc.end }, - ); - } - - fn parseParams(self: *@This()) ParseError![]const Pat { - try self.expect(.lparen); - var params: [128]Pat = undefined; - var count: usize = 0; - - if (self.peek() != .rparen) { - while (true) { - if (self.peek() == .@"...") { - self.advance(); - const pat = try self.parseBindingPattern(); - params[count] = Pat{ .loc = pat.loc, .data = .{ .rest = .{ .loc = pat.loc, .arg = self.arena.patPtr(pat.data, pat.loc) } } }; - count += 1; - break; - } - const pat = try self.parseBindingPattern(); - if (self.match(.@"=")) { - const expr = try self.parseExpr(0); - params[count] = Pat{ .loc = .{ .start = pat.loc.start, .end = expr.loc.end }, .data = .{ .assign = .{ .loc = .{ .start = pat.loc.start, .end = expr.loc.end }, .left = self.arena.patPtr(pat.data, pat.loc), .right = expr } } }; - } else { - params[count] = pat; - } - count += 1; - if (!self.match(.comma)) break; - if (self.peek() == .@"...") { - self.advance(); - const rest_pat = try self.parseBindingPattern(); - params[count] = Pat{ .loc = rest_pat.loc, .data = .{ .rest = .{ .loc = rest_pat.loc, .arg = self.arena.patPtr(rest_pat.data, rest_pat.loc) } } }; - count += 1; - break; - } - } - } - - try self.expect(.rparen); - return params[0..count]; - } - - fn parseFunctionBody(self: *@This()) ParseError!ast.BlockStmt { - return self.parseBlockStmt(); - } - - fn parseClassDecl(self: *@This(), mode: enum { stmt, expr }) ParseError!?Stmt { - const start = self.advance().loc.start; - - var id: ?[]const u8 = null; - if (self.peek() == .identifier) { - const id_tok = self.advance(); - id = self.tokenSlice(id_tok); - } - - var super_class: ?*const Expr = null; - if (self.matchKeyword(.extends)) { - super_class = try self.parseExpr(0); - } - - try self.expect(.lbrace); - var elems: [128]ast.ClassElem = undefined; - var elem_count: usize = 0; - - while (self.peek() != .rbrace and self.peek() != .eof) { - const elem = try self.parseClassElement(); - elems[elem_count] = elem; - elem_count += 1; - if (elem_count >= elems.len) return ParseError.TooManyTokens; - } - - const end_tok = try self.expect(.rbrace); - - if (mode == .stmt) { - return Stmt{ - .loc = .{ .start = start, .end = end_tok.loc.end }, - .data = .{ .class = .{ .loc = .{ .start = start, .end = end_tok.loc.end }, .id = id, .super_class = super_class, .body = elems[0..elem_count] } }, - }; - } - return null; - } - - fn parseClassExpr(self: *@This()) ParseError!*const Expr { - const start = self.advance().loc.start; - - var id: ?[]const u8 = null; - if (self.peek() == .identifier) { - const id_tok = self.advance(); - id = self.tokenSlice(id_tok); - } - - var super_class: ?*const Expr = null; - if (self.matchKeyword(.extends)) { - super_class = try self.parseExpr(0); - } - - try self.expect(.lbrace); - var elems: [128]ast.ClassElem = undefined; - var elem_count: usize = 0; - - while (self.peek() != .rbrace and self.peek() != .eof) { - const elem = try self.parseClassElement(); - elems[elem_count] = elem; - elem_count += 1; - } - - const end_tok = try self.expect(.rbrace); - return self.arena.exprPtr( - .{ .class = .{ .loc = .{ .start = start, .end = end_tok.loc.end }, .id = id, .super_class = super_class, .body = elems[0..elem_count] } }, - .{ .start = start, .end = end_tok.loc.end }, - ); - } - - fn parseClassElement(self: *@This()) ParseError!ast.ClassElem { - const start = self.tok().loc.start; - const is_static = self.matchKeyword(.static); - - if (is_static and self.peek() == .lbrace) { - const body = try self.parseBlockStmt(); - return ast.ClassElem{ .static_block = body }; - } - - if (self.matchKeyword(.get)) { - const key = try self.parsePropKey(); - try self.expect(.lparen); - try self.expect(.rparen); - const body = try self.parseFunctionBody(); - if (is_static) return ast.ClassElem{ .static_get = .{ .key = key, .body = body } }; - return ast.ClassElem{ .get = .{ .key = key, .body = body } }; - } - - if (self.matchKeyword(.set)) { - const key = try self.parsePropKey(); - try self.expect(.lparen); - const param = try self.parseBindingPattern(); - try self.expect(.rparen); - const body = try self.parseFunctionBody(); - if (is_static) return ast.ClassElem{ .static_set = .{ .key = key, .param = param, .body = body } }; - return ast.ClassElem{ .set = .{ .key = key, .param = param, .body = body } }; - } - - if (self.match(.@"*")) { - const key = try self.parsePropKey(); - try self.expect(.lparen); - const params = try self.parseParams(); - const body = try self.parseFunctionBody(); - const fn_expr = ast.FnExpr{ .loc = .{ .start = start, .end = body.loc.end }, .fn_type = .generator, .id = null, .params = params, .body = body }; - if (is_static) return ast.ClassElem{ .static_method = fn_expr }; - return ast.ClassElem{ .method = fn_expr }; - } - - const key = try self.parsePropKey(); - - if (self.peek() == .lparen) { - const params = try self.parseParams(); - const body = try self.parseFunctionBody(); - const fn_expr = ast.FnExpr{ .loc = .{ .start = start, .end = body.loc.end }, .fn_type = .normal, .id = null, .params = params, .body = body }; - if (is_static) return ast.ClassElem{ .static_method = fn_expr }; - return ast.ClassElem{ .method = fn_expr }; - } - - var value: ?*const Expr = null; - if (self.match(.@"=")) { - value = try self.parseExpr(0); - } - if (self.peek() == .semicolon) self.advance(); - - const field = ast.FieldDef{ .loc = .{ .start = start, .end = if (value) |v| v.loc.end else self.tok().loc.start }, .key = key, .value = value }; - if (is_static) return ast.ClassElem{ .static_field = field }; - return ast.ClassElem{ .field = field }; - } - - fn parseImport(self: *@This()) ParseError!Stmt { - const start = self.advance().loc.start; - var specifiers: [32]ast.ImportSpecifier = undefined; - var spec_count: usize = 0; - - if (self.peek() != .string and self.peek() != .lparen) { - if (self.peek() == .identifier or self.peek().isKeyword()) { - const local_tok = self.advance(); - const local = self.tokenSlice(local_tok); - specifiers[spec_count] = .{ .default = local }; - spec_count += 1; - if (self.match(.comma)) {} - } - - if (self.match(.@"*")) { - try self.expect(.identifier); // as - const ns_tok = try self.expect(.identifier); - const ns = self.tokenSlice(ns_tok); - specifiers[spec_count] = .{ .namespace = ns }; - spec_count += 1; - } else if (self.peek() == .lbrace) { - self.advance(); - if (self.peek() != .rbrace) { - while (true) { - const imported_tok = try self.expect(.identifier); - const imported = self.tokenSlice(imported_tok); - var local = imported; - if (self.match(.identifier)) { - local = self.tokenSlice(self.tokens[self.pos - 1]); - } - specifiers[spec_count] = .{ .named = .{ .imported = imported, .local = local } }; - spec_count += 1; - if (!self.match(.comma)) break; - } - } - try self.expect(.rbrace); - } - } - - var source: []const u8 = ""; - var attrs: [8]ast.ImportAttribute = undefined; - var attr_count: usize = 0; - - if (self.matchKeyword(.from)) { - const str_tok = try self.expect(.string); - source = self.tokenSlice(str_tok); - } else if (self.peek() == .string) { - const str_tok = self.advance(); - source = self.tokenSlice(str_tok); - } - - if (self.matchKeyword(.with)) { - try self.expect(.lbrace); - while (self.peek() != .rbrace and self.peek() != .eof) { - const key_tok = self.advance(); - const key = self.tokenSlice(key_tok); - try self.expect(.@":"); - const val_tok = try self.expect(.string); - const val = self.tokenSlice(val_tok); - attrs[attr_count] = .{ .key = key, .value = val }; - attr_count += 1; - _ = self.match(.comma); - } - try self.expect(.rbrace); - } - - try self.expect(.semicolon); - return Stmt{ - .loc = .{ .start = start, .end = self.tok().loc.start }, - .data = .{ .import = .{ .loc = .{ .start = start, .end = self.tok().loc.start }, .specifiers = specifiers[0..spec_count], .source = source, .attributes = attrs[0..attr_count] } }, - }; - } - - fn parseExport(self: *@This()) ParseError!Stmt { - const start = self.advance().loc.start; - - if (self.matchKeyword(.default)) { - if (self.peek() == .function or self.peek() == .async or self.peek() == .class) { - const decl = try self.parseStatementListItem(); - self.semicolon(); - return Stmt{ .loc = .{ .start = start, .end = decl.loc.end }, .data = decl.data }; - } - const expr = try self.parseExpr(0); - self.semicolon(); - return Stmt{ .loc = .{ .start = start, .end = expr.loc.end }, .data = .{ .expr = .{ .loc = expr.loc, .expr = expr } } }; - } - - if (self.peek() == .@"*") { - self.advance(); - try self.expect(.identifier); // from - const source_tok = try self.expect(.string); - _ = self.tokenSlice(source_tok); - try self.expect(.semicolon); - return Stmt{ .loc = .{ .start = start, .end = self.tok().loc.start }, .data = .{ .empty = {} } }; - } - - if (self.peek() == .lbrace) { - self.advance(); - while (self.peek() != .rbrace and self.peek() != .eof) { - _ = self.advance(); - _ = self.match(.identifier); // as - if (!self.match(.comma)) break; - } - try self.expect(.rbrace); - if (self.matchKeyword(.from)) { - _ = self.advance(); - } - try self.expect(.semicolon); - return Stmt{ .loc = .{ .start = start, .end = self.tok().loc.start }, .data = .{ .empty = {} } }; - } - - const decl = try self.parseStatementListItem(); - self.semicolon(); - return decl; - } - - // Expression parsing with precedence climbing - fn parseExpr(self: *@This(), min_bp: u32) ParseError!*const Expr { - var left = try self.parsePrimaryExpr(); - - while (true) { - const bp = self.bp(self.peek()); - if (bp <= min_bp) break; - - switch (self.peek()) { - .@"=", - .@"+=", - .@"-=", - .@"*=", - .@"/=", - .@"%=", - .@"**=", - .@"<<=", - .@">>=", - .@">>>=", - .@"&=", - .@"|=", - .@"^=", - .@"&&=", - .@"||=", - .@"??=", - => { - self.advance(); - const op = self.assignOpFromToken(); - const right = try self.parseExpr(BP.assign); - const loc = .{ .start = left.loc.start, .end = right.loc.end }; - left = self.arena.exprPtr(.{ .assign = .{ .loc = loc, .op = op, .left = left, .right = right } }, loc); - }, - - .@"?" => { - self.advance(); - const consequent = try self.parseExpr(0); - try self.expect(.@":"); - const alternate = try self.parseExpr(BP.conditional); - const loc = .{ .start = left.loc.start, .end = alternate.loc.end }; - left = self.arena.exprPtr(.{ .conditional = .{ .loc = loc, .condition = left, .consequent = consequent, .alternate = alternate } }, loc); - }, - - .@"??" => { - self.advance(); - const right = try self.parseExpr(BP.coalesce); - const loc = .{ .start = left.loc.start, .end = right.loc.end }; - left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"??", .left = left, .right = right } }, loc); - }, - - .@"||" => { - self.advance(); - const right = try self.parseExpr(BP.log_or); - const loc = .{ .start = left.loc.start, .end = right.loc.end }; - left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"||", .left = left, .right = right } }, loc); - }, - - .@"&&" => { - self.advance(); - const right = try self.parseExpr(BP.log_and); - const loc = .{ .start = left.loc.start, .end = right.loc.end }; - left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"&&", .left = left, .right = right } }, loc); - }, - - .@"|" => { - self.advance(); - const right = try self.parseExpr(BP.bit_or); - const loc = .{ .start = left.loc.start, .end = right.loc.end }; - left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"|", .left = left, .right = right } }, loc); - }, - - .@"^" => { - self.advance(); - const right = try self.parseExpr(BP.bit_xor); - const loc = .{ .start = left.loc.start, .end = right.loc.end }; - left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"^", .left = left, .right = right } }, loc); - }, - - .@"&" => { - self.advance(); - const right = try self.parseExpr(BP.bit_and); - const loc = .{ .start = left.loc.start, .end = right.loc.end }; - left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"&", .left = left, .right = right } }, loc); - }, - - .@"==" => { - self.advance(); - const right = try self.parseExpr(BP.equality); - const loc = .{ .start = left.loc.start, .end = right.loc.end }; - left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"==", .left = left, .right = right } }, loc); - }, - - .@"!=" => { - self.advance(); - const right = try self.parseExpr(BP.equality); - const loc = .{ .start = left.loc.start, .end = right.loc.end }; - left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"!=", .left = left, .right = right } }, loc); - }, - - .@"===" => { - self.advance(); - const right = try self.parseExpr(BP.equality); - const loc = .{ .start = left.loc.start, .end = right.loc.end }; - left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"===", .left = left, .right = right } }, loc); - }, - - .@"!==" => { - self.advance(); - const right = try self.parseExpr(BP.equality); - const loc = .{ .start = left.loc.start, .end = right.loc.end }; - left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"!==", .left = left, .right = right } }, loc); - }, - - .@"<" => { - self.advance(); - const right = try self.parseExpr(BP.relational); - const loc = .{ .start = left.loc.start, .end = right.loc.end }; - left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"<", .left = left, .right = right } }, loc); - }, - - .@">" => { - self.advance(); - const right = try self.parseExpr(BP.relational); - const loc = .{ .start = left.loc.start, .end = right.loc.end }; - left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@">", .left = left, .right = right } }, loc); - }, - - .@"<=" => { - self.advance(); - const right = try self.parseExpr(BP.relational); - const loc = .{ .start = left.loc.start, .end = right.loc.end }; - left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"<=", .left = left, .right = right } }, loc); - }, - - .@">=" => { - self.advance(); - const right = try self.parseExpr(BP.relational); - const loc = .{ .start = left.loc.start, .end = right.loc.end }; - left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@">=", .left = left, .right = right } }, loc); - }, - - .in => { - self.advance(); - const right = try self.parseExpr(BP.relational); - const loc = .{ .start = left.loc.start, .end = right.loc.end }; - left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .in, .left = left, .right = right } }, loc); - }, - - .instanceof => { - self.advance(); - const right = try self.parseExpr(BP.relational); - const loc = .{ .start = left.loc.start, .end = right.loc.end }; - left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .instanceof, .left = left, .right = right } }, loc); - }, - - .@"<<" => { - self.advance(); - const right = try self.parseExpr(BP.shift); - const loc = .{ .start = left.loc.start, .end = right.loc.end }; - left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"<<", .left = left, .right = right } }, loc); - }, - - .@">>" => { - self.advance(); - const right = try self.parseExpr(BP.shift); - const loc = .{ .start = left.loc.start, .end = right.loc.end }; - left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@">>", .left = left, .right = right } }, loc); - }, - - .@">>>" => { - self.advance(); - const right = try self.parseExpr(BP.shift); - const loc = .{ .start = left.loc.start, .end = right.loc.end }; - left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@">>>", .left = left, .right = right } }, loc); - }, - - .@"+" => { - self.advance(); - const right = try self.parseExpr(BP.additive); - const loc = .{ .start = left.loc.start, .end = right.loc.end }; - left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"+", .left = left, .right = right } }, loc); - }, - - .@"-" => { - self.advance(); - const right = try self.parseExpr(BP.additive); - const loc = .{ .start = left.loc.start, .end = right.loc.end }; - left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"-", .left = left, .right = right } }, loc); - }, - - .@"*" => { - self.advance(); - const right = try self.parseExpr(BP.multiplicative); - const loc = .{ .start = left.loc.start, .end = right.loc.end }; - left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"*", .left = left, .right = right } }, loc); - }, - - .@"/" => { - self.advance(); - const right = try self.parseExpr(BP.multiplicative); - const loc = .{ .start = left.loc.start, .end = right.loc.end }; - left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"/", .left = left, .right = right } }, loc); - }, - - .@"%" => { - self.advance(); - const right = try self.parseExpr(BP.multiplicative); - const loc = .{ .start = left.loc.start, .end = right.loc.end }; - left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"%", .left = left, .right = right } }, loc); - }, - - .@"**" => { - self.advance(); - const right = try self.parseExpr(BP.exponentiation - 1); - const loc = .{ .start = left.loc.start, .end = right.loc.end }; - left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"**", .left = left, .right = right } }, loc); - }, - - .lparen => { - // Call expression - self.advance(); - var args: [256]Expr = undefined; - var arg_count: usize = 0; - - if (self.peek() != .rparen) { - while (true) { - if (self.peek() == .@"...") { - self.advance(); - const arg = try self.parseExpr(0); - args[arg_count] = Expr{ .loc = arg.loc, .data = .{ .spread = .{ .loc = arg.loc, .arg = self.arena.exprPtr(arg.data, arg.loc) } } }; - arg_count += 1; - } else { - const arg = try self.parseExpr(0); - args[arg_count] = arg.*; - arg_count += 1; - } - if (!self.match(.comma)) break; - } - } - - try self.expect(.rparen); - const loc = .{ .start = left.loc.start, .end = self.tok().loc.start }; - left = self.arena.exprPtr(.{ .call = .{ .loc = loc, .callee = left, .args = args[0..arg_count], .optional = false } }, loc); - }, - - .@"?." => { - self.advance(); - if (self.peek() == .lparen) { - // optional call - self.advance(); - var args: [256]Expr = undefined; - var arg_count: usize = 0; - if (self.peek() != .rparen) { - while (true) { - const arg = try self.parseExpr(0); - args[arg_count] = arg.*; - arg_count += 1; - if (!self.match(.comma)) break; - } - } - try self.expect(.rparen); - const loc = .{ .start = left.loc.start, .end = self.tok().loc.start }; - left = self.arena.exprPtr(.{ .call = .{ .loc = loc, .callee = left, .args = args[0..arg_count], .optional = true } }, loc); - } else if (self.peek() == .lbracket) { - self.advance(); - const expr = try self.parseExpr(0); - try self.expect(.rbracket); - const loc = .{ .start = left.loc.start, .end = self.tok().loc.start }; - left = self.arena.exprPtr(.{ .computed_member = .{ .loc = loc, .obj = left, .expr = expr } }, loc); - } else if (self.peek() == .identifier or self.peek().isKeyword()) { - const prop_tok = self.advance(); - const prop = self.tokenSlice(prop_tok); - const loc = .{ .start = left.loc.start, .end = prop_tok.loc.end }; - left = self.arena.exprPtr(.{ .member = .{ .loc = loc, .obj = left, .prop = .{ .ident = prop } } }, loc); - } else if (self.peek() == .private_identifier) { - const prop_tok = self.advance(); - const prop = self.tokenSlice(prop_tok); - const loc = .{ .start = left.loc.start, .end = prop_tok.loc.end }; - left = self.arena.exprPtr(.{ .member = .{ .loc = loc, .obj = left, .prop = .{ .private = prop } } }, loc); - } - }, - - .@"[" => { - self.advance(); - const expr = try self.parseExpr(0); - try self.expect(.rbracket); - const loc = .{ .start = left.loc.start, .end = self.tok().loc.start }; - left = self.arena.exprPtr(.{ .computed_member = .{ .loc = loc, .obj = left, .expr = expr } }, loc); - }, - - .@"." => { - self.advance(); - if (self.peek() == .private_identifier) { - const prop_tok = self.advance(); - const prop = self.tokenSlice(prop_tok); - const loc = .{ .start = left.loc.start, .end = prop_tok.loc.end }; - left = self.arena.exprPtr(.{ .member = .{ .loc = loc, .obj = left, .prop = .{ .private = prop } } }, loc); - } else { - const prop_tok = try self.expect(.identifier); - const prop = self.tokenSlice(prop_tok); - const loc = .{ .start = left.loc.start, .end = prop_tok.loc.end }; - left = self.arena.exprPtr(.{ .member = .{ .loc = loc, .obj = left, .prop = .{ .ident = prop } } }, loc); - } - }, - - .template_head, .no_sub_template => { - const template = try self.parseTemplateLit(); - const loc = .{ .start = left.loc.start, .end = template.loc.end }; - left = self.arena.exprPtr(.{ .tagged_template = .{ .loc = loc, .tag = left, .quasi = template } }, loc); - }, - - .@"++" => { - if (!self.isLineTerminatorBeforeNext()) { - self.advance(); - const loc = .{ .start = left.loc.start, .end = left.loc.end + 2 }; - left = self.arena.exprPtr(.{ .update = .{ .loc = loc, .op = .@"++", .arg = left, .prefix = false } }, loc); - } else break; - }, - - .@"--" => { - if (!self.isLineTerminatorBeforeNext()) { - self.advance(); - const loc = .{ .start = left.loc.start, .end = left.loc.end + 2 }; - left = self.arena.exprPtr(.{ .update = .{ .loc = loc, .op = .@"--", .arg = left, .prefix = false } }, loc); - } else break; - }, - - .comma => { - self.advance(); - const right = try self.parseExpr(BP.comma); - const loc = .{ .start = left.loc.start, .end = right.loc.end }; - var exprs: [2]Expr = .{ left.*, right.* }; - left = self.arena.exprPtr(.{ .sequence = .{ .loc = loc, .exprs = &exprs } }, loc); - }, - - else => break, - } - } - - return left; - } - - fn parsePrimaryExpr(self: *@This()) ParseError!*const Expr { - const start = self.tok().loc.start; - - switch (self.peek()) { - .this => { - const t = self.advance(); - return self.arena.exprPtr(.{ .this = {} }, .{ .start = start, .end = t.loc.end }); - }, - .super => { - const t = self.advance(); - return self.arena.exprPtr(.{ .super = {} }, .{ .start = start, .end = t.loc.end }); - }, - .null => { - const t = self.advance(); - return self.arena.exprPtr(.{ .null = {} }, .{ .start = start, .end = t.loc.end }); - }, - .true => { - const t = self.advance(); - return self.arena.exprPtr(.{ .bool = true }, .{ .start = start, .end = t.loc.end }); - }, - .false => { - const t = self.advance(); - return self.arena.exprPtr(.{ .bool = false }, .{ .start = start, .end = t.loc.end }); - }, - .number => { - const t = self.advance(); - const val = std.fmt.parseFloat(f64, self.tokenSlice(t)) catch 0.0; - return self.arena.exprPtr(.{ .number = val }, .{ .start = start, .end = t.loc.end }); - }, - .string => { - const t = self.advance(); - return self.arena.exprPtr(.{ .string = self.tokenSlice(t) }, .{ .start = start, .end = t.loc.end }); - }, - .bigint => { - const t = self.advance(); - const slice = self.tokenSlice(t); - return self.arena.exprPtr(.{ .number = 0 }, .{ .start = start, .end = t.loc.end }); - }, - .regex => { - const t = self.advance(); - const slice = self.tokenSlice(t); - const slash_pos = std.mem.indexOfScalar(u8, slice[1..], '/') orelse 0; - const pattern = slice[1 .. slash_pos + 1]; - const flags = slice[slash_pos + 2 ..]; - return self.arena.exprPtr( - .{ .regex = .{ .pattern = pattern, .flags = flags } }, - .{ .start = start, .end = t.loc.end }, - ); - }, - .identifier => { - const t = self.advance(); - const name = self.tokenSlice(t); - return self.arena.exprPtr(.{ .identifier = name }, .{ .start = start, .end = t.loc.end }); - }, - .private_identifier => { - const t = self.advance(); - const name = self.tokenSlice(t); - return self.arena.exprPtr(.{ .private_ident = name }, .{ .start = start, .end = t.loc.end }); - }, - .lbrace => return self.parseObjLiteral(), - .lbracket => return self.parseArrayLiteral(), - .lparen => { - self.advance(); - if (self.peek() == .rparen) { - self.advance(); - if (self.peek() == .@"=>") { - return self.parseArrowExpr(.{ .loc = .{ .start = start, .end = self.tok().loc.start }, .params = &.{} }); - } - return self.arena.exprPtr(.{ .identifier = "" }, .{ .start = start, .end = self.tok().loc.start }); - } - if (self.peek() == .@"...") { - self.advance(); - const arg = try self.parseExpr(0); - try self.expect(.rparen); - if (self.peek() == .@"=>") { - return self.parseArrowExpr(.{ .loc = .{ .start = start, .end = arg.loc.end }, .params = &.{} }); - } - const loc = .{ .start = start, .end = self.tok().loc.start }; - return self.arena.exprPtr(.{ .spread = .{ .loc = loc, .arg = arg } }, loc); - } - const expr = try self.parseExpr(0); - if (self.peek() == .comma) { - var params: [128]Pat = undefined; - var count: usize = 0; - params[count] = Pat{ .loc = expr.loc, .data = .{ .ident = expr.data.identifier } }; - count += 1; - while (self.match(.comma)) { - const p = try self.parseBindingPattern(); - params[count] = p; - count += 1; - } - try self.expect(.rparen); - if (self.peek() == .@"=>") { - return self.parseArrowExpr(.{ .loc = .{ .start = start, .end = self.tok().loc.start }, .params = params[0..count] }); - } - return self.arena.exprPtr(.{ .identifier = "" }, .{ .start = start, .end = self.tok().loc.start }); - } - try self.expect(.rparen); - if (self.peek() == .@"=>") { - const pat = Pat{ .loc = expr.loc, .data = .{ .ident = expr.data.identifier } }; - return self.parseArrowExpr(.{ .loc = .{ .start = start, .end = self.tok().loc.start }, .params = &.{pat} }); - } - return self.arena.exprPtr(.{ .parenthesized = expr }, .{ .start = start, .end = self.tok().loc.start }); - }, - .function => return self.parseFnExpr(), - .class => return self.parseClassExpr(), - .async => { - self.advance(); - if (self.peek() == .function) { - return self.parseFnExpr(); - } - // async arrow function - const id_tok = try self.expect(.identifier); - const name = self.tokenSlice(id_tok); - if (self.peek() == .@"=>") { - const pat = Pat{ .loc = .{ .start = start, .end = id_tok.loc.end }, .data = .{ .ident = name } }; - return self.parseArrowExpr(.{ .loc = .{ .start = start, .end = id_tok.loc.end }, .params = &.{pat} }); - } - return self.arena.exprPtr(.{ .identifier = name }, .{ .start = start, .end = id_tok.loc.end }); - }, - .new => { - self.advance(); - const callee = try self.parseExpr(BP.member); - var args: [256]Expr = undefined; - var arg_count: usize = 0; - if (self.peek() == .lparen) { - self.advance(); - if (self.peek() != .rparen) { - while (true) { - const arg = try self.parseExpr(0); - args[arg_count] = arg.*; - arg_count += 1; - if (!self.match(.comma)) break; - } - } - try self.expect(.rparen); - } - const loc = .{ .start = start, .end = if (arg_count > 0) args[arg_count - 1].loc.end else callee.loc.end }; - return self.arena.exprPtr(.{ .new = .{ .loc = loc, .callee = callee, .args = args[0..arg_count] } }, loc); - }, - .delete, .typeof, .void, .@"!", .@"~", .@"+", .@"-" => { - const op = self.unaryOpFromToken(); - self.advance(); - const arg = try self.parseExpr(BP.unary); - const loc = .{ .start = start, .end = arg.loc.end }; - return self.arena.exprPtr(.{ .unary = .{ .loc = loc, .op = op, .arg = arg, .prefix = true } }, loc); - }, - .@"++" => { - self.advance(); - const arg = try self.parseExpr(BP.update); - const loc = .{ .start = start, .end = arg.loc.end }; - return self.arena.exprPtr(.{ .update = .{ .loc = loc, .op = .@"++", .arg = arg, .prefix = true } }, loc); - }, - .@"--" => { - self.advance(); - const arg = try self.parseExpr(BP.update); - const loc = .{ .start = start, .end = arg.loc.end }; - return self.arena.exprPtr(.{ .update = .{ .loc = loc, .op = .@"--", .arg = arg, .prefix = true } }, loc); - }, - .await => { - self.advance(); - const arg = try self.parseExpr(BP.unary); - const loc = .{ .start = start, .end = arg.loc.end }; - return self.arena.exprPtr(.{ .await = .{ .loc = loc, .arg = arg } }, loc); - }, - .yield => { - self.advance(); - var delegate = false; - var arg: ?*const Expr = null; - if (!self.isLineTerminatorBeforeNext()) { - if (self.match(.@"*")) { - delegate = true; - } - if (self.peek() != .rbrace and self.peek() != .rparen and self.peek() != .rbracket and self.peek() != .semicolon and self.peek() != .comma and self.peek() != .@":" and self.peek() != .eof) { - arg = try self.parseExpr(0); - } - } - const loc = .{ .start = start, .end = if (arg) |a| a.loc.end else start + 5 }; - return self.arena.exprPtr(.{ .yield = .{ .loc = loc, .arg = arg, .delegate = delegate } }, loc); - }, - .template_head, .no_sub_template => return self.arena.exprPtr(.{ .template = try self.parseTemplateLit() }, .{ .start = start, .end = self.tok().loc.start }), - .import => { - self.advance(); - try self.expect(.lparen); - const source = try self.parseExpr(0); - try self.expect(.rparen); - return self.arena.exprPtr(.{ .identifier = "import" }, .{ .start = start, .end = self.tok().loc.start }); - }, - .@"#" => { - self.advance(); - const id_tok = try self.expect(.identifier); - return self.arena.exprPtr(.{ .private_ident = self.tokenSlice(id_tok) }, .{ .start = start, .end = id_tok.loc.end }); - }, - else => { - if (self.peek().isIdentifier()) { - const t = self.advance(); - const name = self.tokenSlice(t); - if (self.peek() == .@"=>") { - const pat = Pat{ .loc = .{ .start = t.loc.start, .end = t.loc.end }, .data = .{ .ident = name } }; - return self.parseArrowExpr(.{ .loc = .{ .start = t.loc.start, .end = t.loc.end }, .params = &.{pat} }); - } - return self.arena.exprPtr(.{ .identifier = name }, .{ .start = t.loc.start, .end = t.loc.end }); - } - return ParseError.ExpectedExpression; - }, - } - } - - fn parseArrowExpr(self: *@This(), params_info: struct { loc: Loc, params: []const Pat }) ParseError!*const Expr { - _ = params_info; - try self.expect(.@"=>"); - const async_token = false; - - if (self.peek() == .lbrace) { - const body = try self.parseFunctionBody(); - return self.arena.exprPtr( - .{ .arrow = .{ .loc = .{ .start = params_info.loc.start, .end = body.loc.end }, .async_token = async_token, .params = params_info.params, .body = .{ .block = body } } }, - .{ .start = params_info.loc.start, .end = body.loc.end }, - ); - } - - const expr = try self.parseExpr(0); - return self.arena.exprPtr( - .{ .arrow = .{ .loc = .{ .start = params_info.loc.start, .end = expr.loc.end }, .async_token = async_token, .params = params_info.params, .body = .{ .expr = expr } } }, - .{ .start = params_info.loc.start, .end = expr.loc.end }, - ); - } - - fn parseObjLiteral(self: *@This()) ParseError!*const Expr { - const start = self.advance().loc.start; - var props: [128]ast.Prop = undefined; - var count: usize = 0; - - while (self.peek() != .rbrace and self.peek() != .eof) { - if (self.peek() == .@"...") { - self.advance(); - const arg = try self.parseExpr(0); - props[count] = .{ .loc = arg.loc, .key = .{ .ident = "" }, .data = .{ .spread = .{ .loc = arg.loc, .arg = arg } } }; - count += 1; - } else if (self.peek() == .identifier) { - const name_tok = self.tok(); - const name = self.tokenSlice(name_tok); - self.advance(); - if (self.peek() == .@":") { - self.advance(); - const value = try self.parseExpr(0); - props[count] = .{ .loc = .{ .start = name_tok.loc.start, .end = value.loc.end }, .key = .{ .ident = name }, .data = .{ .init = .{ .key = .{ .ident = name }, .value = value } } }; - count += 1; - } else if (self.peek() == .@"(") { - const params = try self.parseParams(); - const body = try self.parseFunctionBody(); - props[count] = .{ .loc = .{ .start = name_tok.loc.start, .end = body.loc.end }, .key = .{ .ident = name }, .data = .{ .method = .{ .loc = .{ .start = name_tok.loc.start, .end = body.loc.end }, .fn_type = .normal, .id = null, .params = params, .body = body } } }; - count += 1; - } else if (self.peek() == .@"=") { - self.advance(); - const value = try self.parseExpr(0); - props[count] = .{ .loc = .{ .start = name_tok.loc.start, .end = value.loc.end }, .key = .{ .ident = name }, .data = .{ .init = .{ .key = .{ .ident = name }, .value = value } } }; - count += 1; - } else if (self.peek() == .@"," or self.peek() == .rbrace) { - props[count] = .{ .loc = .{ .start = name_tok.loc.start, .end = name_tok.loc.end }, .key = .{ .ident = name }, .data = .{ .shorthand = name } }; - count += 1; - } - } else if (self.peek() == .number or self.peek() == .string) { - const key = try self.parsePropKey(); - try self.expect(.@":"); - const value = try self.parseExpr(0); - props[count] = .{ .loc = .{ .start = start, .end = value.loc.end }, .key = key, .data = .{ .init = .{ .key = key, .value = value } } }; - count += 1; - } else if (self.peek() == .lbracket) { - const key = try self.parsePropKey(); - try self.expect(.@":"); - const value = try self.parseExpr(0); - props[count] = .{ .loc = .{ .start = start, .end = value.loc.end }, .key = key, .data = .{ .init = .{ .key = key, .value = value } } }; - count += 1; - } else if (self.matchKeyword(.get)) { - const key = try self.parsePropKey(); - try self.expect(.lparen); - try self.expect(.rparen); - const body = try self.parseFunctionBody(); - props[count] = .{ .loc = .{ .start = start, .end = body.loc.end }, .key = key, .data = .{ .get = .{ .key = key, .body = body } } }; - count += 1; - } else if (self.matchKeyword(.set)) { - const key = try self.parsePropKey(); - try self.expect(.lparen); - const param = try self.parseBindingPattern(); - try self.expect(.rparen); - const body = try self.parseFunctionBody(); - props[count] = .{ .loc = .{ .start = start, .end = body.loc.end }, .key = key, .data = .{ .set = .{ .key = key, .param = param, .body = body } } }; - count += 1; - } else if (self.match(.@"*")) { - const key = try self.parsePropKey(); - const params = try self.parseParams(); - const body = try self.parseFunctionBody(); - props[count] = .{ .loc = .{ .start = start, .end = body.loc.end }, .key = key, .data = .{ .method = .{ .loc = .{ .start = start, .end = body.loc.end }, .fn_type = .generator, .id = null, .params = params, .body = body } } }; - count += 1; - } else { - break; - } - - if (!self.match(.comma)) break; - } - - try self.expect(.rbrace); - return self.arena.exprPtr(.{ .object = .{ .loc = .{ .start = start, .end = self.tok().loc.start }, .props = props[0..count] } }, .{ .start = start, .end = self.tok().loc.start }); - } - - fn parseArrayLiteral(self: *@This()) ParseError!*const Expr { - const start = self.advance().loc.start; - var elems: [256]?Expr = undefined; - var count: usize = 0; - - while (self.peek() != .rbracket and self.peek() != .eof) { - if (self.match(.comma)) { - elems[count] = null; - count += 1; - } else if (self.peek() == .@"...") { - self.advance(); - const arg = try self.parseExpr(0); - elems[count] = Expr{ .loc = arg.loc, .data = .{ .spread = .{ .loc = arg.loc, .arg = arg } } }; - count += 1; - if (!self.match(.comma)) break; - } else { - const expr = try self.parseExpr(0); - elems[count] = expr.*; - count += 1; - if (!self.match(.comma)) break; - } - } - - try self.expect(.rbracket); - return self.arena.exprPtr(.{ .array = .{ .loc = .{ .start = start, .end = self.tok().loc.start }, .elems = elems[0..count] } }, .{ .start = start, .end = self.tok().loc.start }); - } - - fn parseTemplateLit(self: *@This()) ParseError!ast.TemplateLit { - const start = self.tok().loc.start; - var quasis: [64]ast.TemplateElem = undefined; - var exprs: [64]Expr = undefined; - var q_count: usize = 0; - var e_count: usize = 0; - - while (true) { - switch (self.peek()) { - .no_sub_template => { - const t = self.advance(); - const slice = self.tokenSlice(t); - quasis[q_count] = .{ .loc = .{ .start = t.loc.start, .end = t.loc.end }, .value = slice, .tail = true }; - q_count += 1; - break; - }, - .template_head => { - const t = self.advance(); - const slice = self.tokenSlice(t); - quasis[q_count] = .{ .loc = .{ .start = t.loc.start, .end = t.loc.end }, .value = slice, .tail = false }; - q_count += 1; - const expr = try self.parseExpr(0); - exprs[e_count] = expr.*; - e_count += 1; - try self.expect(.rbrace); - }, - .template_middle => { - const t = self.advance(); - const slice = self.tokenSlice(t); - quasis[q_count] = .{ .loc = .{ .start = t.loc.start, .end = t.loc.end }, .value = slice, .tail = false }; - q_count += 1; - const expr = try self.parseExpr(0); - exprs[e_count] = expr.*; - e_count += 1; - try self.expect(.rbrace); - }, - .template_tail => { - const t = self.advance(); - const slice = self.tokenSlice(t); - quasis[q_count] = .{ .loc = .{ .start = t.loc.start, .end = t.loc.end }, .value = slice, .tail = true }; - q_count += 1; - break; - }, - else => break, - } - } - - return .{ .loc = .{ .start = start, .end = self.tok().loc.start }, .quasis = quasis[0..q_count], .exprs = exprs[0..e_count] }; - } - - fn parsePropKey(self: *@This()) ParseError!ast.PropKey { - switch (self.peek()) { - .identifier, .let, .static, .get, .set, .async => { - const t = self.advance(); - return ast.PropKey{ .ident = self.tokenSlice(t) }; - }, - .string => { - const t = self.advance(); - return ast.PropKey{ .string = self.tokenSlice(t) }; - }, - .number => { - const t = self.advance(); - const val = std.fmt.parseFloat(f64, self.tokenSlice(t)) catch 0.0; - return ast.PropKey{ .number = val }; - }, - .lbracket => { - self.advance(); - const expr = try self.parseExpr(0); - try self.expect(.rbracket); - return ast.PropKey{ .computed = expr }; - }, - .private_identifier => { - const t = self.advance(); - return ast.PropKey{ .private = self.tokenSlice(t) }; - }, - else => return ParseError.ExpectedPropertyName, - } - } - - fn parseBindingPattern(self: *@This()) ParseError!Pat { - const start = self.tok().loc.start; - - switch (self.peek()) { - .identifier, .await, .yield => { - const t = self.advance(); - const name = self.tokenSlice(t); - return Pat{ .loc = .{ .start = start, .end = t.loc.end }, .data = .{ .ident = name } }; - }, - .lbrace => { - self.advance(); - var props: [128]ast.PatProp = undefined; - var count: usize = 0; - var rest: ?*const Pat = null; - - while (self.peek() != .rbrace and self.peek() != .eof) { - if (self.peek() == .@"...") { - self.advance(); - const pat = try self.parseBindingPattern(); - rest = self.arena.patPtr(pat.data, pat.loc); - break; - } - if (self.peek() == .identifier) { - const name_tok = self.tok(); - const name = self.tokenSlice(name_tok); - self.advance(); - if (self.peek() == .@":") { - self.advance(); - const val_pat = try self.parseBindingPattern(); - props[count] = .{ .key_value = .{ .key = .{ .ident = name }, .value = self.arena.patPtr(val_pat.data, val_pat.loc) } }; - count += 1; - } else { - props[count] = .{ .shorthand = name }; - count += 1; - } - } else if (self.peek() == .lbracket) { - const key = try self.parsePropKey(); - try self.expect(.@":"); - const val_pat = try self.parseBindingPattern(); - props[count] = .{ .key_value = .{ .key = key, .value = self.arena.patPtr(val_pat.data, val_pat.loc) } }; - count += 1; - } else { - break; - } - if (!self.match(.comma)) break; - } - - try self.expect(.rbrace); - return Pat{ .loc = .{ .start = start, .end = self.tok().loc.start }, .data = .{ .object = .{ .loc = .{ .start = start, .end = self.tok().loc.start }, .props = props[0..count], .rest = rest } } }; - }, - .lbracket => { - self.advance(); - var elems: [128]?Pat = undefined; - var count: usize = 0; - var rest: ?*const Pat = null; - - while (self.peek() != .rbracket and self.peek() != .eof) { - if (self.match(.comma)) { - elems[count] = null; - count += 1; - } else if (self.peek() == .@"...") { - self.advance(); - const pat = try self.parseBindingPattern(); - rest = self.arena.patPtr(pat.data, pat.loc); - break; - } else { - const pat = try self.parseBindingPattern(); - elems[count] = pat; - count += 1; - if (!self.match(.comma)) break; - } - } - - try self.expect(.rbracket); - return Pat{ .loc = .{ .start = start, .end = self.tok().loc.start }, .data = .{ .array = .{ .loc = .{ .start = start, .end = self.tok().loc.start }, .elems = elems[0..count], .rest = rest } } }; - }, - else => return ParseError.ExpectedParameterName, - } - } - - fn bp(self: *@This(), kind: TokenType) u32 { - return switch (kind) { - .comma => BP.comma, - .@"=", .@"+=", .@"-=", .@"*=", .@"/=", .@"%=", .@"**=", .@"<<=", .@">>=", .@">>>=", .@"&=", .@"|=", .@"^=", .@"&&=", .@"||=", .@"??=" => BP.assign, - .@"?" => BP.conditional, - .@"??" => BP.coalesce, - .@"||" => BP.log_or, - .@"&&" => BP.log_and, - .@"|" => BP.bit_or, - .@"^" => BP.bit_xor, - .@"&" => BP.bit_and, - .@"==", .@"!=", .@"===", .@"!==" => BP.equality, - .@"<", .@">", .@"<=", .@">=", .in, .instanceof => BP.relational, - .@"<<", .@">>", .@">>>" => BP.shift, - .@"+", .@"-" => BP.additive, - .@"*", .@"/", .@"%" => BP.multiplicative, - .@"**" => BP.exponentiation, - .lparen => BP.call, - .@"[", .@".", .@"?." => BP.member, - .@"++", .@"--" => BP.update, - .template_head, .no_sub_template => BP.member, - else => 0, - }; - } - - fn unaryOpFromToken(self: *@This()) ast.UnaryOp { - return switch (self.tok().kind) { - .@"+" => .@"+", - .@"-" => .@"-", - .@"!" => .@"!", - .@"~" => .@"~", - .typeof => .typeof, - .void => .void, - .delete => .delete, - else => .@"+", - }; - } - - fn assignOpFromToken(self: *@This()) ast.AssignOp { - return switch (self.tokens[self.pos - 1].kind) { - .@"=" => .@"=", - .@"+=" => .@"+=", - .@"-=" => .@"-=", - .@"*=" => .@"*=", - .@"/=" => .@"/=", - .@"%=" => .@"%=", - .@"**=" => .@"**=", - .@"<<=" => .@"<<=", - .@">>=" => .@">>=", - .@">>>=" => .@">>>=", - .@"&=" => .@"&=", - .@"|=" => .@"|=", - .@"^=" => .@"^=", - .@"&&=" => .@"&&=", - .@"||=" => .@"||=", - .@"??=" => .@"??=", - else => .@"=", - }; - } - - fn semicolon(self: *@This()) void { - if (self.peek() == .semicolon) { - self.advance(); - } - } - - fn statementPointer(self: *@This()) ParseError!*const Stmt { - const s = try self.parseStatement(); - return self.arena.stmtPtr(s.data, s.loc); - } -}; - -pub fn parse(comptime source: []const u8) ParseError!ast.Program { - comptime { - const tokens = lex.Lexer.tokenize(source); - var arena: NodeArena = .{}; - var parser = Parser.init(tokens, source, &arena); - const result = try parser.parseProgram(); - return result; - } -} - -pub fn parseTokens(comptime source: []const u8) ParseError![]const Token { - comptime { - return lex.Lexer.tokenize(source); - } -} - -pub fn tokenType(kind: TokenType) []const u8 { - return @tagName(kind); -} -- cgit v1.2.3-70-g09d2