aboutsummaryrefslogtreecommitdiff
path: root/src/z/parser/js/ast.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/z/parser/js/ast.zig')
-rw-r--r--src/z/parser/js/ast.zig1975
1 files changed, 609 insertions, 1366 deletions
diff --git a/src/z/parser/js/ast.zig b/src/z/parser/js/ast.zig
index 0e49a4b..3f11e8a 100644
--- a/src/z/parser/js/ast.zig
+++ b/src/z/parser/js/ast.zig
@@ -216,217 +216,16 @@ pub const Parser = struct {
return self.source[t.loc.start..t.loc.end];
}
- pub fn parseProgram(self: *@This()) ParseError!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.advanceTok();
- 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.advanceTok();
- 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.advanceTok().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.expectTok(.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.advanceTok().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.advanceTok().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.advanceTok().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.advanceTok().loc.start;
- try self.expect(.lparen);
-
- var for_init: ?ForInit = 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();
- for_init = .{ .decl = decl };
- } else {
- const expr = try self.parseExpr(0);
- for_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 = for_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 = for_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 = for_init, .condition = condition, .update = update, .body = body } },
- };
- }
-
fn parseForDeclaration(self: *@This()) ParseError!*const Decl {
const kind_tok = self.advanceTok();
var decls: [128]VarDeclarator = undefined;
var count: usize = 0;
while (true) {
- const pat = try self.parseBindingPattern();
+ const pat = try Pat.parse(self);
var decl_init: ?*const Expr = null;
if (self.match(.@"=")) {
- decl_init = try self.parseExpr(0);
+ decl_init = try Expr.parse(self, 0);
}
decls[count] = .{ .loc = pat.loc, .id = pat, .init = decl_init };
count += 1;
@@ -443,295 +242,6 @@ pub const Parser = struct {
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.advanceTok().loc.start;
- var label: ?[]const u8 = null;
- if (self.peek() == .identifier and !self.isLineTerminatorBeforeNext()) {
- const t = self.advanceTok();
- 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.advanceTok().loc.start;
- var label: ?[]const u8 = null;
- if (self.peek() == .identifier and !self.isLineTerminatorBeforeNext()) {
- const t = self.advanceTok();
- 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.advanceTok().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.advanceTok().loc.start;
- try self.expect(.lparen);
- const discriminant = try self.parseExpr(0);
- try self.expect(.rparen);
- try self.expect(.lbrace);
-
- var cases: [256]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.expectTok(.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.advanceTok().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.advanceTok().loc.start;
- const block = try self.parseBlockStmt();
- var handler: ?CatchClause = null;
- var finalizer: ?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.advanceTok().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!BlockStmt {
- const start = try self.expectTok(.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.expectTok(.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.advanceTok();
- var decls: [128]VarDeclarator = undefined;
- var count: usize = 0;
-
- while (true) {
- const pat = try self.parseBindingPattern();
- var decl_init: ?*const Expr = null;
- if (self.match(.@"=")) {
- decl_init = try self.parseExpr(0);
- }
- decls[count] = .{ .loc = pat.loc, .id = pat, .init = decl_init };
- count += 1;
- if (!self.match(.comma)) break;
- }
-
- self.semicolon();
- const kind: 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.advanceTok();
- var decls: [128]VarDeclarator = undefined;
- var count: usize = 0;
-
- while (true) {
- const pat = try self.parseBindingPattern();
- var decl_init: ?*const Expr = null;
- if (kind_tok.kind == .@"const") {
- if (!self.match(.@"=")) return ParseError.ExpectedToken;
- decl_init = try self.parseExpr(0);
- } else if (self.match(.@"=")) {
- decl_init = try self.parseExpr(0);
- }
- decls[count] = .{ .loc = pat.loc, .id = pat, .init = decl_init };
- count += 1;
- if (!self.match(.comma)) break;
- }
-
- self.semicolon();
- const kind: 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.advanceTok().loc.start;
- var decls: [128]VarDeclarator = undefined;
- var count: usize = 0;
-
- while (true) {
- const id_tok = try self.expectTok(.identifier);
- const id = self.tokenSlice(id_tok);
- var decl_init: ?*const Expr = null;
- if (self.match(.@"=")) {
- decl_init = try self.parseExpr(0);
- }
- decls[count] = .{ .loc = .{ .start = id_tok.loc.start, .end = if (decl_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 = decl_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_arg: FnType, mode: enum { stmt, expr }) ParseError!?Stmt {
- const start = self.tok().loc.start;
- var fn_type = fn_type_arg;
-
- if (fn_type == .async) {
- _ = self.advanceTok(); // async
- } else if (fn_type == .async_generator) {
- _ = self.advanceTok(); // async
- _ = self.advanceTok(); // *
- }
-
- _ = self.advanceTok(); // 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.advanceTok();
- 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.advanceTok().loc.start;
-
- var fn_type: 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.advanceTok();
- 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;
@@ -741,14 +251,14 @@ pub const Parser = struct {
while (true) {
if (self.peek() == .@"...") {
self.advance();
- const pat = try self.parseBindingPattern();
+ const pat = try Pat.parse(self);
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();
+ const pat = try Pat.parse(self);
if (self.match(.@"=")) {
- const expr = try self.parseExpr(0);
+ const expr = try Expr.parse(self, 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;
@@ -757,7 +267,7 @@ pub const Parser = struct {
if (!self.match(.comma)) break;
if (self.peek() == .@"...") {
self.advance();
- const rest_pat = try self.parseBindingPattern();
+ const rest_pat = try Pat.parse(self);
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;
@@ -770,587 +280,7 @@ pub const Parser = struct {
}
fn parseFunctionBody(self: *@This()) ParseError!BlockStmt {
- return self.parseBlockStmt();
- }
-
- fn parseClassDecl(self: *@This(), mode: enum { stmt, expr }) ParseError!?Stmt {
- const start = self.advanceTok().loc.start;
-
- var id: ?[]const u8 = null;
- if (self.peek() == .identifier) {
- const id_tok = self.advanceTok();
- 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]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.expectTok(.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.advanceTok().loc.start;
-
- var id: ?[]const u8 = null;
- if (self.peek() == .identifier) {
- const id_tok = self.advanceTok();
- 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]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.expectTok(.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!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 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 ClassElem{ .static_get = .{ .key = key, .body = body } };
- return 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 ClassElem{ .static_set = .{ .key = key, .param = param, .body = body } };
- return ClassElem{ .set = .{ .key = key, .param = param, .body = body } };
- }
-
- if (self.match(.@"*")) {
- _ = try self.parsePropKey();
- try self.expect(.lparen);
- const params = try self.parseParams();
- const body = try self.parseFunctionBody();
- const fn_expr = FnExpr{ .loc = .{ .start = start, .end = body.loc.end }, .fn_type = .generator, .id = null, .params = params, .body = body };
- if (is_static) return ClassElem{ .static_method = fn_expr };
- return 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 = FnExpr{ .loc = .{ .start = start, .end = body.loc.end }, .fn_type = .normal, .id = null, .params = params, .body = body };
- if (is_static) return ClassElem{ .static_method = fn_expr };
- return 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 = FieldDef{ .loc = .{ .start = start, .end = if (value) |v| v.loc.end else self.tok().loc.start }, .key = key, .value = value };
- if (is_static) return ClassElem{ .static_field = field };
- return ClassElem{ .field = field };
- }
-
- fn parseImport(self: *@This()) ParseError!Stmt {
- const start = self.advanceTok().loc.start;
- var specifiers: [32]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.advanceTok();
- 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.expectTok(.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.expectTok(.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]ImportAttribute = undefined;
- var attr_count: usize = 0;
-
- if (self.matchKeyword(.from)) {
- const str_tok = try self.expectTok(.string);
- source = self.tokenSlice(str_tok);
- } else if (self.peek() == .string) {
- const str_tok = self.advanceTok();
- 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.advanceTok();
- const key = self.tokenSlice(key_tok);
- try self.expect(.@":");
- const val_tok = try self.expectTok(.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.advanceTok().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.expectTok(.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.advanceTok();
- _ = self.match(.identifier); // as
- if (!self.match(.comma)) break;
- }
- try self.expect(.rbrace);
- if (self.matchKeyword(.from)) {
- _ = self.advanceTok();
- }
- 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;
- }
-
- fn parseExpr(self: *@This(), min_bp: u32) ParseError!*const Expr {
- var left = try self.parsePrimaryExpr();
-
- while (true) {
- const cur_bp = self.bp(self.peek());
- if (cur_bp <= min_bp) break;
-
- switch (self.peek()) {
- .@"=",
- .@"+=",
- .@"-=",
- .@"*=",
- .@"/=",
- .@"%=",
- .@"**=",
- .@"<<=",
- .@">>=",
- .@">>>=",
- .@"&=",
- .@"|=",
- .@"^=",
- .@"&&=",
- .@"||=",
- .@"??=",
- => {
- self.advance();
- const op = self.assignOpFromToken();
- const right = try self.parseExpr(BP.assign);
- const loc: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: Loc = .{ .start = left.loc.start, .end = right.loc.end };
- left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"**", .left = left, .right = right } }, loc);
- },
-
- .lparen => {
- 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: 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) {
- 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: 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: 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.advanceTok();
- const prop = self.tokenSlice(prop_tok);
- const loc: 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.advanceTok();
- const prop = self.tokenSlice(prop_tok);
- const loc: Loc = .{ .start = left.loc.start, .end = prop_tok.loc.end };
- left = self.arena.exprPtr(.{ .member = .{ .loc = loc, .obj = left, .prop = .{ .private = prop } } }, loc);
- }
- },
-
- .lbracket => {
- self.advance();
- const expr = try self.parseExpr(0);
- try self.expect(.rbracket);
- const loc: 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.advanceTok();
- const prop = self.tokenSlice(prop_tok);
- const loc: 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.expectTok(.identifier);
- const prop = self.tokenSlice(prop_tok);
- const loc: 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: 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: 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: 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: 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;
+ return BlockStmt.parse(self);
}
fn parsePrimaryExpr(self: *@This()) ParseError!*const Expr {
@@ -1411,76 +341,76 @@ pub const Parser = struct {
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(),
+ .lbrace => return ObjectExpr.parse(self),
+ .lbracket => return ArrayExpr.parse(self),
.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 ArrowFn.parse(self, .{ .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);
+ const arg = try Expr.parse(self, 0);
try self.expect(.rparen);
if (self.peek() == .@"=>") {
- return self.parseArrowExpr(.{ .loc = .{ .start = start, .end = arg.loc.end }, .params = &.{} });
+ return ArrowFn.parse(self, .{ .loc = .{ .start = start, .end = arg.loc.end }, .params = &.{} });
}
const loc: Loc = .{ .start = start, .end = self.tok().loc.start };
return self.arena.exprPtr(.{ .spread = .{ .loc = loc, .arg = arg } }, loc);
}
- const expr = try self.parseExpr(0);
+ const expr = try Expr.parse(self, 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();
+ const p = try Pat.parse(self);
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 ArrowFn.parse(self, .{ .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 ArrowFn.parse(self, .{ .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(),
+ .function => return FnExpr.parse(self),
+ .class => return ClassExpr.parse(self),
.async => {
self.advance();
if (self.peek() == .function) {
- return self.parseFnExpr();
+ return FnExpr.parse(self);
}
const id_tok = try self.expectTok(.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 ArrowFn.parse(self, .{ .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);
+ const callee = try Expr.parse(self, 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);
+ const arg = try Expr.parse(self, 0);
args[arg_count] = arg.*;
arg_count += 1;
if (!self.match(.comma)) break;
@@ -1494,25 +424,25 @@ pub const Parser = struct {
.delete, .typeof, .void, .@"!", .@"~", .@"+", .@"-" => {
const op = self.unaryOpFromToken();
self.advance();
- const arg = try self.parseExpr(BP.unary);
+ const arg = try Expr.parse(self, BP.unary);
const loc: 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 arg = try Expr.parse(self, BP.update);
const loc: 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 arg = try Expr.parse(self, BP.update);
const loc: 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 arg = try Expr.parse(self, BP.unary);
const loc: Loc = .{ .start = start, .end = arg.loc.end };
return self.arena.exprPtr(.{ .await = .{ .loc = loc, .arg = arg } }, loc);
},
@@ -1525,17 +455,17 @@ pub const Parser = struct {
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);
+ arg = try Expr.parse(self, 0);
}
}
const loc: 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 }),
+ .template_head, .no_sub_template => return self.arena.exprPtr(.{ .template = try TemplateLit.parse(self) }, .{ .start = start, .end = self.tok().loc.start }),
.import => {
self.advance();
try self.expect(.lparen);
- _ = try self.parseExpr(0);
+ _ = try Expr.parse(self, 0);
try self.expect(.rparen);
return self.arena.exprPtr(.{ .identifier = "import" }, .{ .start = start, .end = self.tok().loc.start });
},
@@ -1550,7 +480,7 @@ pub const Parser = struct {
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 ArrowFn.parse(self, .{ .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 });
}
@@ -1559,180 +489,6 @@ pub const Parser = struct {
}
}
- fn parseArrowExpr(self: *@This(), params_info: struct { loc: Loc, params: []const Pat }) ParseError!*const Expr {
- 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.advanceTok().loc.start;
- var props: [128]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() == .lparen) {
- 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() == .comma 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.advanceTok().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!TemplateLit {
- const start = self.tok().loc.start;
- var quasis: [64]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.advanceTok();
- 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.advanceTok();
- 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.advanceTok();
- 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.advanceTok();
- 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!PropKey {
switch (self.peek()) {
.identifier, .let, .static, .get, .set, .async => {
@@ -1750,7 +506,7 @@ pub const Parser = struct {
},
.lbracket => {
self.advance();
- const expr = try self.parseExpr(0);
+ const expr = try Expr.parse(self, 0);
try self.expect(.rbracket);
return PropKey{ .computed = expr };
},
@@ -1762,86 +518,6 @@ pub const Parser = struct {
}
}
- fn parseBindingPattern(self: *@This()) ParseError!Pat {
- const start = self.tok().loc.start;
-
- switch (self.peek()) {
- .identifier, .await, .yield => {
- const t = self.advanceTok();
- const name = self.tokenSlice(t);
- return Pat{ .loc = .{ .start = start, .end = t.loc.end }, .data = .{ .ident = name } };
- },
- .lbrace => {
- self.advance();
- var props: [128]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(_: *@This(), kind: TokenType) u32 {
return switch (kind) {
.comma => BP.comma,
@@ -1909,7 +585,7 @@ pub const Parser = struct {
}
fn statementPointer(self: *@This()) ParseError!*const Stmt {
- const s = try self.parseStatement();
+ const s = try Stmt.parse(self);
return self.arena.stmtPtr(s.data, s.loc);
}
};
@@ -1945,7 +621,17 @@ pub const Program = struct {
const tokens = lex.Lexer.tokenizeBuf(source, &token_buf);
var arena: NodeArena = .{};
var parser = Parser.init(tokens, source, &arena);
- break :blk try parser.parseProgram();
+ var stmts: [2048]Stmt = undefined;
+ var count: usize = 0;
+ while (parser.peek() != .eof) {
+ const item = try Stmt.parse(&parser);
+ 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 };
+ break :blk .{ .body = body, .loc = loc };
};
}
@@ -1990,6 +676,123 @@ pub const Stmt = struct {
loc: Loc,
data: StmtType,
+ pub fn parse(p: *Parser) ParseError!Stmt {
+ switch (p.peek()) {
+ .lbrace => {
+ const b = try BlockStmt.parse(p);
+ return Stmt{ .loc = b.loc, .data = .{ .block = b } };
+ },
+ .semicolon => {
+ const t = p.advanceTok();
+ return Stmt{ .loc = .{ .start = t.loc.start, .end = t.loc.end }, .data = .{ .empty = {} } };
+ },
+ .@"if" => {
+ const s = try IfStmt.parse(p);
+ return Stmt{ .loc = s.loc, .data = .{ .@"if" = s } };
+ },
+ .@"while" => {
+ const s = try WhileStmt.parse(p);
+ return Stmt{ .loc = s.loc, .data = .{ .@"while" = s } };
+ },
+ .do => {
+ const s = try DoWhileStmt.parse(p);
+ return Stmt{ .loc = s.loc, .data = .{ .do_while = s } };
+ },
+ .@"for" => {
+ const start = p.advanceTok().loc.start;
+ try p.expect(.lparen);
+ var for_init: ?ForInit = null;
+ if (p.peek() != .semicolon) {
+ if (p.peek() == .@"var" or p.peek() == .let or p.peek() == .@"const") {
+ const decl = try p.parseForDeclaration();
+ for_init = .{ .decl = decl };
+ } else {
+ const expr = try Expr.parse(p, 0);
+ for_init = .{ .expr = expr };
+ }
+ }
+ if (p.matchKeyword(.in)) {
+ const s = try ForInStmt.parse(p, start, for_init);
+ return Stmt{ .loc = s.loc, .data = .{ .for_in = s } };
+ }
+ if (p.matchKeyword(.of)) {
+ const s = try ForOfStmt.parse(p, start, for_init);
+ return Stmt{ .loc = s.loc, .data = .{ .for_of = s } };
+ }
+ const s = try ForStmt.parse(p, start, for_init);
+ return Stmt{ .loc = s.loc, .data = .{ .@"for" = s } };
+ },
+ .@"continue" => {
+ const s = try ContinueStmt.parse(p);
+ return Stmt{ .loc = s.loc, .data = .{ .@"continue" = s } };
+ },
+ .@"break" => {
+ const s = try BreakStmt.parse(p);
+ return Stmt{ .loc = s.loc, .data = .{ .@"break" = s } };
+ },
+ .@"return" => {
+ const s = try ReturnStmt.parse(p);
+ return Stmt{ .loc = s.loc, .data = .{ .@"return" = s } };
+ },
+ .@"switch" => {
+ const s = try SwitchStmt.parse(p);
+ return Stmt{ .loc = s.loc, .data = .{ .@"switch" = s } };
+ },
+ .throw => {
+ const s = try ThrowStmt.parse(p);
+ return Stmt{ .loc = s.loc, .data = .{ .throw = s } };
+ },
+ .@"try" => {
+ const s = try TryStmt.parse(p);
+ return Stmt{ .loc = s.loc, .data = .{ .@"try" = s } };
+ },
+ .debugger => {
+ const t = p.advanceTok();
+ p.semicolon();
+ return Stmt{ .loc = .{ .start = t.loc.start, .end = t.loc.end }, .data = .{ .debugger = {} } };
+ },
+ .with => {
+ const s = try WithStmt.parse(p);
+ return Stmt{ .loc = s.loc, .data = .{ .with = s } };
+ },
+ .function => return (try FnDecl.parse(p, .normal, .stmt)).?,
+ .async => {
+ if (p.peekAt(1) == .function) {
+ return (try FnDecl.parse(p, .async, .stmt)).?;
+ }
+ if (p.peekAt(1) == .@"*" and p.peekAt(2) == .function) {
+ return (try FnDecl.parse(p, .async_generator, .stmt)).?;
+ }
+ return Stmt.parse(p);
+ },
+ .class => return (try ClassDecl.parse(p, .stmt)).?,
+ .@"const", .let => {
+ const d = try LexicalDecl.parse(p);
+ return Stmt{ .loc = d.loc, .data = .{ .lexical = d } };
+ },
+ .@"var" => {
+ const s = try VarStmt.parse(p);
+ return Stmt{ .loc = s.loc, .data = .{ .variable = s } };
+ },
+ .using => {
+ const s = try UsingStmt.parse(p);
+ return Stmt{ .loc = s.loc, .data = .{ .using = s } };
+ },
+ .import => {
+ const d = try ImportDecl.parse(p);
+ return Stmt{ .loc = d.loc, .data = .{ .import = d } };
+ },
+ .@"export" => {
+ const d = try ExportDecl.parse(p);
+ return Stmt{ .loc = d.loc, .data = .{ .@"export" = d } };
+ },
+ else => {
+ const s = try ExprStmt.parse(p);
+ return Stmt{ .loc = s.loc, .data = .{ .expr = s } };
+ },
+ }
+ }
+
pub fn fmt(self: @This(), f: *Formatter) void {
switch (self.data) {
.block => |b| b.fmt(f),
@@ -2104,6 +907,344 @@ pub const Expr = struct {
loc: Loc,
data: ExprType,
+ pub fn parse(p: *Parser, min_bp: u32) ParseError!*const Expr {
+ var left = try p.parsePrimaryExpr();
+
+ while (true) {
+ const cur_bp = p.bp(p.peek());
+ if (cur_bp <= min_bp) break;
+
+ switch (p.peek()) {
+ .@"=",
+ .@"+=",
+ .@"-=",
+ .@"*=",
+ .@"/=",
+ .@"%=",
+ .@"**=",
+ .@"<<=",
+ .@">>=",
+ .@">>>=",
+ .@"&=",
+ .@"|=",
+ .@"^=",
+ .@"&&=",
+ .@"||=",
+ .@"??=",
+ => {
+ p.advance();
+ const op = p.assignOpFromToken();
+ const right = try Expr.parse(p, BP.assign);
+ const loc: Loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = p.arena.exprPtr(.{ .assign = .{ .loc = loc, .op = op, .left = left, .right = right } }, loc);
+ },
+
+ .@"?" => {
+ p.advance();
+ const consequent = try Expr.parse(p, 0);
+ try p.expect(.@":");
+ const alternate = try Expr.parse(p, BP.conditional);
+ const loc: Loc = .{ .start = left.loc.start, .end = alternate.loc.end };
+ left = p.arena.exprPtr(.{ .conditional = .{ .loc = loc, .condition = left, .consequent = consequent, .alternate = alternate } }, loc);
+ },
+
+ .@"??" => {
+ p.advance();
+ const right = try Expr.parse(p, BP.coalesce);
+ const loc: Loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = p.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"??", .left = left, .right = right } }, loc);
+ },
+
+ .@"||" => {
+ p.advance();
+ const right = try Expr.parse(p, BP.log_or);
+ const loc: Loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = p.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"||", .left = left, .right = right } }, loc);
+ },
+
+ .@"&&" => {
+ p.advance();
+ const right = try Expr.parse(p, BP.log_and);
+ const loc: Loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = p.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"&&", .left = left, .right = right } }, loc);
+ },
+
+ .@"|" => {
+ p.advance();
+ const right = try Expr.parse(p, BP.bit_or);
+ const loc: Loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = p.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"|", .left = left, .right = right } }, loc);
+ },
+
+ .@"^" => {
+ p.advance();
+ const right = try Expr.parse(p, BP.bit_xor);
+ const loc: Loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = p.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"^", .left = left, .right = right } }, loc);
+ },
+
+ .@"&" => {
+ p.advance();
+ const right = try Expr.parse(p, BP.bit_and);
+ const loc: Loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = p.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"&", .left = left, .right = right } }, loc);
+ },
+
+ .@"==" => {
+ p.advance();
+ const right = try Expr.parse(p, BP.equality);
+ const loc: Loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = p.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"==", .left = left, .right = right } }, loc);
+ },
+
+ .@"!=" => {
+ p.advance();
+ const right = try Expr.parse(p, BP.equality);
+ const loc: Loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = p.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"!=", .left = left, .right = right } }, loc);
+ },
+
+ .@"===" => {
+ p.advance();
+ const right = try Expr.parse(p, BP.equality);
+ const loc: Loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = p.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"===", .left = left, .right = right } }, loc);
+ },
+
+ .@"!==" => {
+ p.advance();
+ const right = try Expr.parse(p, BP.equality);
+ const loc: Loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = p.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"!==", .left = left, .right = right } }, loc);
+ },
+
+ .@"<" => {
+ p.advance();
+ const right = try Expr.parse(p, BP.relational);
+ const loc: Loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = p.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"<", .left = left, .right = right } }, loc);
+ },
+
+ .@">" => {
+ p.advance();
+ const right = try Expr.parse(p, BP.relational);
+ const loc: Loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = p.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@">", .left = left, .right = right } }, loc);
+ },
+
+ .@"<=" => {
+ p.advance();
+ const right = try Expr.parse(p, BP.relational);
+ const loc: Loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = p.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"<=", .left = left, .right = right } }, loc);
+ },
+
+ .@">=" => {
+ p.advance();
+ const right = try Expr.parse(p, BP.relational);
+ const loc: Loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = p.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@">=", .left = left, .right = right } }, loc);
+ },
+
+ .in => {
+ p.advance();
+ const right = try Expr.parse(p, BP.relational);
+ const loc: Loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = p.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .in, .left = left, .right = right } }, loc);
+ },
+
+ .instanceof => {
+ p.advance();
+ const right = try Expr.parse(p, BP.relational);
+ const loc: Loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = p.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .instanceof, .left = left, .right = right } }, loc);
+ },
+
+ .@"<<" => {
+ p.advance();
+ const right = try Expr.parse(p, BP.shift);
+ const loc: Loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = p.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"<<", .left = left, .right = right } }, loc);
+ },
+
+ .@">>" => {
+ p.advance();
+ const right = try Expr.parse(p, BP.shift);
+ const loc: Loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = p.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@">>", .left = left, .right = right } }, loc);
+ },
+
+ .@">>>" => {
+ p.advance();
+ const right = try Expr.parse(p, BP.shift);
+ const loc: Loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = p.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@">>>", .left = left, .right = right } }, loc);
+ },
+
+ .@"+" => {
+ p.advance();
+ const right = try Expr.parse(p, BP.additive);
+ const loc: Loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = p.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"+", .left = left, .right = right } }, loc);
+ },
+
+ .@"-" => {
+ p.advance();
+ const right = try Expr.parse(p, BP.additive);
+ const loc: Loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = p.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"-", .left = left, .right = right } }, loc);
+ },
+
+ .@"*" => {
+ p.advance();
+ const right = try Expr.parse(p, BP.multiplicative);
+ const loc: Loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = p.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"*", .left = left, .right = right } }, loc);
+ },
+
+ .@"/" => {
+ p.advance();
+ const right = try Expr.parse(p, BP.multiplicative);
+ const loc: Loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = p.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"/", .left = left, .right = right } }, loc);
+ },
+
+ .@"%" => {
+ p.advance();
+ const right = try Expr.parse(p, BP.multiplicative);
+ const loc: Loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = p.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"%", .left = left, .right = right } }, loc);
+ },
+
+ .@"**" => {
+ p.advance();
+ const right = try Expr.parse(p, BP.exponentiation - 1);
+ const loc: Loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = p.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"**", .left = left, .right = right } }, loc);
+ },
+
+ .lparen => {
+ p.advance();
+ var args: [256]Expr = undefined;
+ var arg_count: usize = 0;
+
+ if (p.peek() != .rparen) {
+ while (true) {
+ if (p.peek() == .@"...") {
+ p.advance();
+ const arg = try Expr.parse(p, 0);
+ args[arg_count] = Expr{ .loc = arg.loc, .data = .{ .spread = .{ .loc = arg.loc, .arg = p.arena.exprPtr(arg.data, arg.loc) } } };
+ arg_count += 1;
+ } else {
+ const arg = try Expr.parse(p, 0);
+ args[arg_count] = arg.*;
+ arg_count += 1;
+ }
+ if (!p.match(.comma)) break;
+ }
+ }
+
+ try p.expect(.rparen);
+ const loc: Loc = .{ .start = left.loc.start, .end = p.tok().loc.start };
+ left = p.arena.exprPtr(.{ .call = .{ .loc = loc, .callee = left, .args = args[0..arg_count], .optional = false } }, loc);
+ },
+
+ .@"?." => {
+ p.advance();
+ if (p.peek() == .lparen) {
+ p.advance();
+ var args: [256]Expr = undefined;
+ var arg_count: usize = 0;
+ if (p.peek() != .rparen) {
+ while (true) {
+ const arg = try Expr.parse(p, 0);
+ args[arg_count] = arg.*;
+ arg_count += 1;
+ if (!p.match(.comma)) break;
+ }
+ }
+ try p.expect(.rparen);
+ const loc: Loc = .{ .start = left.loc.start, .end = p.tok().loc.start };
+ left = p.arena.exprPtr(.{ .call = .{ .loc = loc, .callee = left, .args = args[0..arg_count], .optional = true } }, loc);
+ } else if (p.peek() == .lbracket) {
+ p.advance();
+ const expr = try Expr.parse(p, 0);
+ try p.expect(.rbracket);
+ const loc: Loc = .{ .start = left.loc.start, .end = p.tok().loc.start };
+ left = p.arena.exprPtr(.{ .computed_member = .{ .loc = loc, .obj = left, .expr = expr } }, loc);
+ } else if (p.peek() == .identifier or p.peek().isKeyword()) {
+ const prop_tok = p.advanceTok();
+ const prop = p.tokenSlice(prop_tok);
+ const loc: Loc = .{ .start = left.loc.start, .end = prop_tok.loc.end };
+ left = p.arena.exprPtr(.{ .member = .{ .loc = loc, .obj = left, .prop = .{ .ident = prop } } }, loc);
+ } else if (p.peek() == .private_identifier) {
+ const prop_tok = p.advanceTok();
+ const prop = p.tokenSlice(prop_tok);
+ const loc: Loc = .{ .start = left.loc.start, .end = prop_tok.loc.end };
+ left = p.arena.exprPtr(.{ .member = .{ .loc = loc, .obj = left, .prop = .{ .private = prop } } }, loc);
+ }
+ },
+
+ .lbracket => {
+ p.advance();
+ const expr = try Expr.parse(p, 0);
+ try p.expect(.rbracket);
+ const loc: Loc = .{ .start = left.loc.start, .end = p.tok().loc.start };
+ left = p.arena.exprPtr(.{ .computed_member = .{ .loc = loc, .obj = left, .expr = expr } }, loc);
+ },
+
+ .@"." => {
+ p.advance();
+ if (p.peek() == .private_identifier) {
+ const prop_tok = p.advanceTok();
+ const prop = p.tokenSlice(prop_tok);
+ const loc: Loc = .{ .start = left.loc.start, .end = prop_tok.loc.end };
+ left = p.arena.exprPtr(.{ .member = .{ .loc = loc, .obj = left, .prop = .{ .private = prop } } }, loc);
+ } else {
+ const prop_tok = try p.expectTok(.identifier);
+ const prop = p.tokenSlice(prop_tok);
+ const loc: Loc = .{ .start = left.loc.start, .end = prop_tok.loc.end };
+ left = p.arena.exprPtr(.{ .member = .{ .loc = loc, .obj = left, .prop = .{ .ident = prop } } }, loc);
+ }
+ },
+
+ .template_head, .no_sub_template => {
+ const template = try TemplateLit.parse(p);
+ const loc: Loc = .{ .start = left.loc.start, .end = template.loc.end };
+ left = p.arena.exprPtr(.{ .tagged_template = .{ .loc = loc, .tag = left, .quasi = template } }, loc);
+ },
+
+ .@"++" => {
+ if (!p.isLineTerminatorBeforeNext()) {
+ p.advance();
+ const loc: Loc = .{ .start = left.loc.start, .end = left.loc.end + 2 };
+ left = p.arena.exprPtr(.{ .update = .{ .loc = loc, .op = .@"++", .arg = left, .prefix = false } }, loc);
+ } else break;
+ },
+
+ .@"--" => {
+ if (!p.isLineTerminatorBeforeNext()) {
+ p.advance();
+ const loc: Loc = .{ .start = left.loc.start, .end = left.loc.end + 2 };
+ left = p.arena.exprPtr(.{ .update = .{ .loc = loc, .op = .@"--", .arg = left, .prefix = false } }, loc);
+ } else break;
+ },
+
+ .comma => {
+ p.advance();
+ const right = try Expr.parse(p, BP.comma);
+ const loc: Loc = .{ .start = left.loc.start, .end = right.loc.end };
+ var exprs: [2]Expr = .{ left.*, right.* };
+ left = p.arena.exprPtr(.{ .sequence = .{ .loc = loc, .exprs = &exprs } }, loc);
+ },
+
+ else => break,
+ }
+ }
+
+ return left;
+ }
+
pub fn fmt(self: @This(), f: *Formatter) void {
switch (self.data) {
.identifier => |name| f.write(name),
@@ -2349,6 +1490,41 @@ pub const DeclType = union(enum) {
pub const Decl = struct {
loc: Loc,
data: DeclType,
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ switch (self.data) {
+ .@"fn" => |d| d.fmt(f),
+ .class => |c| c.fmt(f),
+ .@"var" => |v| {
+ f.write(@tagName(v.kind));
+ f.writeChar(' ');
+ for (v.decls, 0..) |d, i| {
+ if (i > 0) f.write(", ");
+ d.fmt(f);
+ }
+ },
+ .using => |u| {
+ f.write("using ");
+ for (u.decls, 0..) |d, i| {
+ if (i > 0) f.write(", ");
+ d.fmt(f);
+ }
+ },
+ .import => |i| i.fmt(f),
+ .@"export" => |e| e.fmt(f),
+ .export_default => |e| e.fmt(f),
+ }
+ }
+
+ pub fn parse(p: *Parser) ParseError!Decl {
+ switch (p.peek()) {
+ .@"var" => {
+ const s = try VarStmt.parse(p);
+ return Decl{ .loc = s.loc, .data = .{ .@"var" = .{ .loc = s.loc, .kind = s.kind, .decls = s.decls } } };
+ },
+ else => return ParseError.ExpectedStatement,
+ }
+ }
};
pub const LitType = union(enum) {
@@ -2533,10 +1709,12 @@ pub const IfStmt = struct {
try p.expect(.lparen);
const condition = try Expr.parse(p, 0);
try p.expect(.rparen);
- const consequent = try p.arena.stmtPtr((try Stmt.parse(p)).data, (try Stmt.parse(p)).loc);
+ const consequent_stmt = try Stmt.parse(p);
+ const consequent = p.arena.stmtPtr(consequent_stmt.data, consequent_stmt.loc);
var alternate: ?*const Stmt = null;
if (p.matchKeyword(.@"else")) {
- alternate = try p.arena.stmtPtr((try Stmt.parse(p)).data, (try Stmt.parse(p)).loc);
+ const alt_stmt = try Stmt.parse(p);
+ alternate = p.arena.stmtPtr(alt_stmt.data, alt_stmt.loc);
}
const end = if (alternate) |a| a.loc.end else consequent.loc.end;
return .{ .loc = .{ .start = start, .end = end }, .condition = condition, .consequent = consequent, .alternate = alternate };
@@ -2564,7 +1742,10 @@ pub const WhileStmt = struct {
try p.expect(.lparen);
const condition = try Expr.parse(p, 0);
try p.expect(.rparen);
- const body = try p.arena.stmtPtr((try Stmt.parse(p)).data, (try Stmt.parse(p)).loc);
+ const body = blk: {
+ const s = try Stmt.parse(p);
+ break :blk p.arena.stmtPtr(s.data, s.loc);
+};
return .{ .loc = .{ .start = start, .end = body.loc.end }, .condition = condition, .body = body };
}
@@ -2583,7 +1764,10 @@ pub const DoWhileStmt = struct {
pub fn parse(p: *Parser) ParseError!DoWhileStmt {
const start = p.advanceTok().loc.start;
- const body = try p.arena.stmtPtr((try Stmt.parse(p)).data, (try Stmt.parse(p)).loc);
+ const body = blk: {
+ const s = try Stmt.parse(p);
+ break :blk p.arena.stmtPtr(s.data, s.loc);
+};
try p.expect(.@"while");
try p.expect(.lparen);
const condition = try Expr.parse(p, 0);
@@ -2626,7 +1810,10 @@ pub const ForStmt = struct {
update = try Expr.parse(p, 0);
}
try p.expect(.rparen);
- const body = try p.arena.stmtPtr((try Stmt.parse(p)).data, (try Stmt.parse(p)).loc);
+ const body = blk: {
+ const s = try Stmt.parse(p);
+ break :blk p.arena.stmtPtr(s.data, s.loc);
+};
return .{ .loc = .{ .start = start, .end = body.loc.end }, .init = for_init, .condition = condition, .update = update, .body = body };
}
@@ -2662,7 +1849,10 @@ pub const ForInStmt = struct {
pub fn parse(p: *Parser, start: usize, for_init: ?ForInit) ParseError!ForInStmt {
const right = try Expr.parse(p, 0);
try p.expect(.rparen);
- const body = try p.arena.stmtPtr((try Stmt.parse(p)).data, (try Stmt.parse(p)).loc);
+ const body = blk: {
+ const s = try Stmt.parse(p);
+ break :blk p.arena.stmtPtr(s.data, s.loc);
+};
return .{ .loc = .{ .start = start, .end = body.loc.end }, .left = for_init.?, .right = right, .body = body };
}
@@ -2689,7 +1879,10 @@ pub const ForOfStmt = struct {
pub fn parse(p: *Parser, start: usize, for_init: ?ForInit) ParseError!ForOfStmt {
const right = try Expr.parse(p, 0);
try p.expect(.rparen);
- const body = try p.arena.stmtPtr((try Stmt.parse(p)).data, (try Stmt.parse(p)).loc);
+ const body = blk: {
+ const s = try Stmt.parse(p);
+ break :blk p.arena.stmtPtr(s.data, s.loc);
+};
return .{ .loc = .{ .start = start, .end = body.loc.end }, .await_token = false, .left = for_init.?, .right = right, .body = body };
}
@@ -2932,7 +2125,10 @@ pub const WithStmt = struct {
try p.expect(.lparen);
const obj = try Expr.parse(p, 0);
try p.expect(.rparen);
- const body = try p.arena.stmtPtr((try Stmt.parse(p)).data, (try Stmt.parse(p)).loc);
+ const body = blk: {
+ const s = try Stmt.parse(p);
+ break :blk p.arena.stmtPtr(s.data, s.loc);
+};
return .{ .loc = .{ .start = start, .end = body.loc.end }, .obj = obj, .body = body };
}
};
@@ -3654,7 +2850,54 @@ pub const ExportDecl = struct {
pub fn parse(p: *Parser) ParseError!ExportDecl {
const start = p.advanceTok().loc.start;
- return .{ .loc = .{ .start = start, .end = p.tok().loc.start }, .declaration = null, .specifiers = null, .source = null };
+ if (p.matchKeyword(.default)) {
+ if (p.peek() == .function or p.peek() == .async or p.peek() == .class) {
+ const decl = try Stmt.parse(p);
+ p.semicolon();
+ return .{ .loc = .{ .start = start, .end = decl.loc.end }, .declaration = null, .specifiers = null, .source = null };
+ }
+ const expr = try Expr.parse(p, 0);
+ p.semicolon();
+ return .{ .loc = .{ .start = start, .end = expr.loc.end }, .declaration = null, .specifiers = null, .source = null };
+ }
+
+ if (p.peek() == .@"*") {
+ p.advance();
+ try p.expect(.identifier);
+ const source_tok = try p.expectTok(.string);
+ const src = p.tokenSlice(source_tok);
+ try p.expect(.semicolon);
+ return .{ .loc = .{ .start = start, .end = p.tok().loc.start }, .declaration = null, .specifiers = null, .source = src };
+ }
+
+ if (p.peek() == .lbrace) {
+ p.advance();
+ var specs: [32]ExportSpecifier = undefined;
+ var spec_count: usize = 0;
+ while (p.peek() != .rbrace and p.peek() != .eof) {
+ const exported_tok = p.advanceTok();
+ const exported = p.tokenSlice(exported_tok);
+ var local = exported;
+ if (p.match(.identifier)) {
+ local = p.tokenSlice(p.tokens[p.pos - 1]);
+ }
+ specs[spec_count] = .{ .loc = .{ .start = exported_tok.loc.start, .end = p.tok().loc.start }, .exported = exported, .local = local };
+ spec_count += 1;
+ if (!p.match(.comma)) break;
+ }
+ try p.expect(.rbrace);
+ var source: ?[]const u8 = null;
+ if (p.matchKeyword(.from)) {
+ const src_tok = try p.expectTok(.string);
+ source = p.tokenSlice(src_tok);
+ }
+ try p.expect(.semicolon);
+ return .{ .loc = .{ .start = start, .end = p.tok().loc.start }, .declaration = null, .specifiers = specs[0..spec_count], .source = source };
+ }
+
+ const decl = try Stmt.parse(p);
+ p.semicolon();
+ return .{ .loc = .{ .start = start, .end = decl.loc.end }, .declaration = null, .specifiers = null, .source = null };
}
pub fn fmt(self: @This(), f: *Formatter) void {