aboutsummaryrefslogtreecommitdiff
path: root/src/z
diff options
context:
space:
mode:
authorNathan Reiner <nathan@nathanreiner.xyz>2026-07-30 10:24:19 +0200
committerNathan Reiner <nathan@nathanreiner.xyz>2026-07-30 10:24:19 +0200
commit00920ddd36a8ebb851b52c3c048c0017753176bd (patch)
tree8498e65ca6c055333aa95b6c3a0d28c8370cb0ef /src/z
parente8259360516078517a2fbeddbf781f701f35d0d0 (diff)
complete js parser + minifierjs-parser
Diffstat (limited to 'src/z')
-rw-r--r--src/z/parser/js/ast.zig3368
-rw-r--r--src/z/parser/js/ast.zig.bak3334
-rw-r--r--src/z/parser/js/ast/core.zig162
-rw-r--r--src/z/parser/js/ast/decl.zig432
-rw-r--r--src/z/parser/js/ast/expr.zig1245
-rw-r--r--src/z/parser/js/ast/lit.zig48
-rw-r--r--src/z/parser/js/ast/loc.zig4
-rw-r--r--src/z/parser/js/ast/pat.zig190
-rw-r--r--src/z/parser/js/ast/stmt.zig865
-rw-r--r--src/z/parser/js/formatter.zig67
-rw-r--r--src/z/parser/js/lexical_grammar.zig3
-rw-r--r--src/z/parser/js/parser.zig553
-rw-r--r--src/z/parser/js/root.zig14
13 files changed, 3580 insertions, 6705 deletions
diff --git a/src/z/parser/js/ast.zig b/src/z/parser/js/ast.zig
deleted file mode 100644
index 3f11e8a..0000000
--- a/src/z/parser/js/ast.zig
+++ /dev/null
@@ -1,3368 +0,0 @@
-const std = @import("std");
-
-pub const Loc = struct {
- start: usize,
- end: usize,
-};
-
-const lex = @import("lexical_grammar.zig");
-const Token = lex.Token;
-const TokenType = lex.TokenType;
-
-pub const FormatOptions = struct {
- indent_size: usize = 4,
-};
-
-pub const Formatter = struct {
- buf: []u8,
- pos: usize,
- indent_level: usize,
- options: FormatOptions,
-
- pub fn write(self: *@This(), s: []const u8) void {
- @memcpy(self.buf[self.pos..][0..s.len], s);
- self.pos += s.len;
- }
-
- pub fn writeChar(self: *@This(), c: u8) void {
- self.buf[self.pos] = c;
- self.pos += 1;
- }
-
- pub fn writeIndent(self: *@This()) void {
- const spaces = self.indent_level * self.options.indent_size;
- var i: usize = 0;
- while (i < spaces) : (i += 1) {
- self.writeChar(' ');
- }
- }
-
- pub fn newline(self: *@This()) void {
- self.writeChar('\n');
- }
-};
-
-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()) void {
- self.pos += 1;
- }
-
- fn advanceTok(self: *@This()) Token {
- const t = self.tokens[self.pos];
- self.pos += 1;
- return t;
- }
-
- fn expect(self: *@This(), kind: TokenType) ParseError!void {
- if (self.peek() != kind) return ParseError.ExpectedToken;
- self.advance();
- }
-
- fn expectTok(self: *@This(), kind: TokenType) ParseError!Token {
- if (self.peek() != kind) return ParseError.ExpectedToken;
- return self.advanceTok();
- }
-
- fn match(self: *@This(), kind: TokenType) bool {
- if (self.peek() == kind) {
- _ = self.advanceTok();
- return true;
- }
- return false;
- }
-
- fn matchKeyword(self: *@This(), kw: TokenType) bool {
- if (self.peek() == kw) {
- _ = self.advanceTok();
- 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];
- 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' => return true,
- else => {},
- }
- }
- return false;
- }
-
- fn as_identifier(self: *@This(), t: Token) []const u8 {
- return self.source[t.loc.start..t.loc.end];
- }
-
- fn tokenSlice(self: *@This(), t: Token) []const u8 {
- return self.source[t.loc.start..t.loc.end];
- }
-
- 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 Pat.parse(self);
- var decl_init: ?*const Expr = null;
- if (self.match(.@"=")) {
- decl_init = try Expr.parse(self, 0);
- }
- decls[count] = .{ .loc = pat.loc, .id = pat, .init = decl_init };
- count += 1;
- if (!self.match(.comma)) break;
- }
-
- const kind: 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 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 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 Pat.parse(self);
- if (self.match(.@"=")) {
- 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;
- }
- count += 1;
- if (!self.match(.comma)) break;
- if (self.peek() == .@"...") {
- self.advance();
- 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;
- }
- }
- }
-
- try self.expect(.rparen);
- return params[0..count];
- }
-
- fn parseFunctionBody(self: *@This()) ParseError!BlockStmt {
- return BlockStmt.parse(self);
- }
-
- fn parsePrimaryExpr(self: *@This()) ParseError!*const Expr {
- const start = self.tok().loc.start;
-
- switch (self.peek()) {
- .this => {
- const t = self.advanceTok();
- return self.arena.exprPtr(.{ .this = {} }, .{ .start = start, .end = t.loc.end });
- },
- .super => {
- const t = self.advanceTok();
- return self.arena.exprPtr(.{ .super = {} }, .{ .start = start, .end = t.loc.end });
- },
- .null => {
- const t = self.advanceTok();
- return self.arena.exprPtr(.{ .null = {} }, .{ .start = start, .end = t.loc.end });
- },
- .true => {
- const t = self.advanceTok();
- return self.arena.exprPtr(.{ .bool = true }, .{ .start = start, .end = t.loc.end });
- },
- .false => {
- const t = self.advanceTok();
- return self.arena.exprPtr(.{ .bool = false }, .{ .start = start, .end = t.loc.end });
- },
- .number => {
- const t = self.advanceTok();
- 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.advanceTok();
- return self.arena.exprPtr(.{ .string = self.tokenSlice(t) }, .{ .start = start, .end = t.loc.end });
- },
- .bigint => {
- const t = self.advanceTok();
- return self.arena.exprPtr(.{ .number = 0 }, .{ .start = start, .end = t.loc.end });
- },
- .regex => {
- const t = self.advanceTok();
- 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.advanceTok();
- const name = self.tokenSlice(t);
- return self.arena.exprPtr(.{ .identifier = name }, .{ .start = start, .end = t.loc.end });
- },
- .private_identifier => {
- const t = self.advanceTok();
- const name = self.tokenSlice(t);
- return self.arena.exprPtr(.{ .private_ident = name }, .{ .start = start, .end = t.loc.end });
- },
- .lbrace => return ObjectExpr.parse(self),
- .lbracket => return ArrayExpr.parse(self),
- .lparen => {
- self.advance();
- if (self.peek() == .rparen) {
- self.advance();
- if (self.peek() == .@"=>") {
- 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 Expr.parse(self, 0);
- try self.expect(.rparen);
- if (self.peek() == .@"=>") {
- 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 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 Pat.parse(self);
- params[count] = p;
- count += 1;
- }
- try self.expect(.rparen);
- if (self.peek() == .@"=>") {
- 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 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 FnExpr.parse(self),
- .class => return ClassExpr.parse(self),
- .async => {
- self.advance();
- if (self.peek() == .function) {
- 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 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 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 Expr.parse(self, 0);
- args[arg_count] = arg.*;
- arg_count += 1;
- if (!self.match(.comma)) break;
- }
- }
- try self.expect(.rparen);
- }
- const loc: 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 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 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 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 Expr.parse(self, BP.unary);
- const loc: 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 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 TemplateLit.parse(self) }, .{ .start = start, .end = self.tok().loc.start }),
- .import => {
- self.advance();
- try self.expect(.lparen);
- _ = try Expr.parse(self, 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.expectTok(.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.advanceTok();
- const name = self.tokenSlice(t);
- if (self.peek() == .@"=>") {
- const pat = Pat{ .loc = .{ .start = t.loc.start, .end = t.loc.end }, .data = .{ .ident = name } };
- 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 });
- }
- return ParseError.ExpectedExpression;
- },
- }
- }
-
- fn parsePropKey(self: *@This()) ParseError!PropKey {
- switch (self.peek()) {
- .identifier, .let, .static, .get, .set, .async => {
- const t = self.advanceTok();
- return PropKey{ .ident = self.tokenSlice(t) };
- },
- .string => {
- const t = self.advanceTok();
- return PropKey{ .string = self.tokenSlice(t) };
- },
- .number => {
- const t = self.advanceTok();
- const val = std.fmt.parseFloat(f64, self.tokenSlice(t)) catch 0.0;
- return PropKey{ .number = val };
- },
- .lbracket => {
- self.advance();
- const expr = try Expr.parse(self, 0);
- try self.expect(.rbracket);
- return PropKey{ .computed = expr };
- },
- .private_identifier => {
- const t = self.advanceTok();
- return PropKey{ .private = self.tokenSlice(t) };
- },
- else => return ParseError.ExpectedPropertyName,
- }
- }
-
- fn bp(_: *@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,
- .lbracket, .@".", .@"?." => BP.member,
- .@"++", .@"--" => BP.update,
- .template_head, .no_sub_template => BP.member,
- else => 0,
- };
- }
-
- fn unaryOpFromToken(self: *@This()) UnaryOp {
- return switch (self.tok().kind) {
- .@"+" => .@"+",
- .@"-" => .@"-",
- .@"!" => .@"!",
- .@"~" => .@"~",
- .typeof => .typeof,
- .void => .void,
- .delete => .delete,
- else => .@"+",
- };
- }
-
- fn assignOpFromToken(self: *@This()) 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 Stmt.parse(self);
- return self.arena.stmtPtr(s.data, s.loc);
- }
-};
-
-pub const Node = union(enum) {
- program: Program,
-
- stmt: Stmt,
- expr: Expr,
- pat: Pat,
-
- decl: Decl,
- literal: Lit,
- property: Prop,
- template: TemplateLit,
- spread: SpreadElem,
- decorator: Decorator,
-
- import_specifier: ImportSpecifier,
- export_specifier: ExportSpecifier,
- import_attr: ImportAttribute,
- catch_clause: CatchClause,
- switch_case: SwitchCase,
-};
-
-pub const Program = struct {
- body: []const Stmt,
- loc: Loc,
-
- pub fn parse(comptime source: []const u8) ParseError!Program {
- return comptime blk: {
- var token_buf: [4096]Token = undefined;
- const tokens = lex.Lexer.tokenizeBuf(source, &token_buf);
- var arena: NodeArena = .{};
- var parser = Parser.init(tokens, source, &arena);
- 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 };
- };
- }
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- @setEvalBranchQuota(100000);
- for (self.body, 0..) |stmt, i| {
- if (i > 0) f.newline();
- stmt.fmt(f);
- }
- }
-};
-
-pub const StmtType = union(enum) {
- block: BlockStmt,
- empty: void,
- debugger: void,
- expr: ExprStmt,
- @"if": IfStmt,
- @"while": WhileStmt,
- do_while: DoWhileStmt,
- @"for": ForStmt,
- for_in: ForInStmt,
- for_of: ForOfStmt,
- @"continue": ContinueStmt,
- @"break": BreakStmt,
- @"return": ReturnStmt,
- @"switch": SwitchStmt,
- throw: ThrowStmt,
- @"try": TryStmt,
- labelled: LabelledStmt,
- with: WithStmt,
- variable: VarStmt,
- lexical: LexicalDecl,
- function: FnDecl,
- class: ClassDecl,
- using: UsingStmt,
- import: ImportDecl,
- @"export": ExportDecl,
-};
-
-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),
- .empty => f.writeChar(';'),
- .debugger => f.write("debugger;"),
- .expr => |e| {
- e.expr.*.fmt(f);
- f.writeChar(';');
- },
- .@"if" => |s| s.fmt(f),
- .@"while" => |w| w.fmt(f),
- .do_while => |d| d.fmt(f),
- .@"for" => |fo| fo.fmt(f),
- .for_in => |fi| fi.fmt(f),
- .for_of => |fo| fo.fmt(f),
- .@"continue" => |c| {
- f.write("continue");
- if (c.label) |l| {
- f.writeChar(' ');
- f.write(l);
- }
- f.writeChar(';');
- },
- .@"break" => |b| {
- f.write("break");
- if (b.label) |l| {
- f.writeChar(' ');
- f.write(l);
- }
- f.writeChar(';');
- },
- .@"return" => |r| {
- f.write("return");
- if (r.arg) |a| {
- f.writeChar(' ');
- a.*.fmt(f);
- }
- f.writeChar(';');
- },
- .@"switch" => |s| s.fmt(f),
- .throw => |t| {
- f.write("throw ");
- t.arg.*.fmt(f);
- f.writeChar(';');
- },
- .@"try" => |t| t.fmt(f),
- .labelled => |l| {
- f.write(l.label);
- f.write(": ");
- l.body.*.fmt(f);
- },
- .with => |w| {
- f.write("with (");
- w.obj.*.fmt(f);
- f.write(") ");
- w.body.*.fmt(f);
- },
- .variable => |v| v.fmt(f),
- .lexical => |l| l.fmt(f),
- .using => |u| {
- f.write("using ");
- for (u.decls, 0..) |d, i| {
- if (i > 0) f.write(", ");
- d.fmt(f);
- }
- f.writeChar(';');
- },
- .import => |i| i.fmt(f),
- .@"export" => |e| e.fmt(f),
- .class => |c| c.fmt(f),
- .function => |d| d.fmt(f),
- }
- }
-};
-
-pub const ExprType = union(enum) {
- identifier: []const u8,
- this: void,
- super: void,
- null: void,
- bool: bool,
- number: f64,
- string: []const u8,
- regex: Regex,
- array: ArrayExpr,
- object: ObjectExpr,
- func: FnExpr,
- arrow: ArrowFn,
- class: ClassExpr,
- template: TemplateLit,
- tagged_template: TaggedTemplate,
- member: MemberExpr,
- computed_member: ComputedMember,
- call: CallExpr,
- new: NewExpr,
- chain: ChainExpr,
- unary: UnaryExpr,
- binary: BinaryExpr,
- update: UpdateExpr,
- conditional: IfExpr,
- assign: AssignExpr,
- sequence: SeqExpr,
- spread: SpreadElem,
- yield: YieldExpr,
- await: AwaitExpr,
- meta_prop: MetaProp,
- parenthesized: *const Expr,
- private_ident: []const u8,
-};
-
-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),
- .this => f.write("this"),
- .super => f.write("super"),
- .null => f.write("null"),
- .bool => |b| f.write(if (b) "true" else "false"),
- .number => |n| {
- var buf: [64]u8 = undefined;
- f.write(std.fmt.bufPrint(&buf, "{d}", .{n}) catch "0");
- },
- .string => |s| {
- f.writeChar('"');
- f.write(s);
- f.writeChar('"');
- },
- .regex => |r| {
- f.writeChar('/');
- f.write(r.pattern);
- f.writeChar('/');
- f.write(r.flags);
- },
- .array => |a| a.fmt(f),
- .object => |o| o.fmt(f),
- .func => |fn_expr| fn_expr.fmt(f),
- .arrow => |arrow| arrow.fmt(f),
- .class => |c| c.fmt(f),
- .template => |t| t.fmt(f),
- .tagged_template => |tt| tt.fmt(f),
- .member => |m| m.fmt(f),
- .computed_member => |cm| cm.fmt(f),
- .call => |call| call.fmt(f),
- .new => |n| {
- f.write("new ");
- n.callee.*.fmt(f);
- f.writeChar('(');
- for (n.args, 0..) |a, i| {
- if (i > 0) f.write(", ");
- a.fmt(f);
- }
- f.writeChar(')');
- },
- .chain => |c| c.expr.*.fmt(f),
- .unary => |u| {
- if (u.prefix) {
- f.write(@tagName(u.op));
- u.arg.*.fmt(f);
- } else {
- u.arg.*.fmt(f);
- f.write(@tagName(u.op));
- }
- },
- .binary => |b| {
- f.writeChar('(');
- b.left.*.fmt(f);
- f.writeChar(' ');
- f.write(@tagName(b.op));
- f.writeChar(' ');
- b.right.*.fmt(f);
- f.writeChar(')');
- },
- .update => |u| {
- const s = if (u.op == .@"++") "++" else "--";
- if (u.prefix) {
- f.write(s);
- u.arg.*.fmt(f);
- } else {
- u.arg.*.fmt(f);
- f.write(s);
- }
- },
- .conditional => |c| {
- c.condition.*.fmt(f);
- f.write(" ? ");
- c.consequent.*.fmt(f);
- f.write(" : ");
- c.alternate.*.fmt(f);
- },
- .assign => |a| {
- a.left.*.fmt(f);
- f.writeChar(' ');
- f.write(@tagName(a.op));
- f.writeChar(' ');
- a.right.*.fmt(f);
- },
- .sequence => |s| {
- for (s.exprs, 0..) |e, i| {
- if (i > 0) f.write(", ");
- e.fmt(f);
- }
- },
- .spread => |s| {
- f.write("...");
- s.arg.*.fmt(f);
- },
- .yield => |y| {
- f.write("yield");
- if (y.delegate) f.write("*");
- if (y.arg) |a| {
- f.writeChar(' ');
- a.*.fmt(f);
- }
- },
- .await => |a| {
- f.write("await ");
- a.arg.*.fmt(f);
- },
- .meta_prop => |m| {
- f.write(m.meta);
- f.writeChar('.');
- f.write(m.prop);
- },
- .parenthesized => |p| {
- f.writeChar('(');
- p.*.fmt(f);
- f.writeChar(')');
- },
- .private_ident => |name| f.write(name),
- }
- }
-};
-
-pub const PatType = union(enum) {
- ident: []const u8,
- object: ObjectPat,
- array: ArrayPat,
- assign: AssignPat,
- rest: RestPat,
-};
-
-pub const Pat = struct {
- loc: Loc,
- data: PatType,
-
- pub fn parse(p: *Parser) ParseError!Pat {
- const start = p.tok().loc.start;
-
- switch (p.peek()) {
- .identifier, .await, .yield => {
- const t = p.advanceTok();
- const name = p.tokenSlice(t);
- return Pat{ .loc = .{ .start = start, .end = t.loc.end }, .data = .{ .ident = name } };
- },
- .lbrace => {
- p.advance();
- var props: [128]PatProp = undefined;
- var count: usize = 0;
- var rest: ?*const Pat = null;
-
- while (p.peek() != .rbrace and p.peek() != .eof) {
- if (p.peek() == .@"...") {
- p.advance();
- const pat = try Pat.parse(p);
- rest = p.arena.patPtr(pat.data, pat.loc);
- break;
- }
- if (p.peek() == .identifier) {
- const name_tok = p.tok();
- const name = p.tokenSlice(name_tok);
- p.advance();
- if (p.peek() == .@":") {
- p.advance();
- const val_pat = try Pat.parse(p);
- props[count] = .{ .key_value = .{ .key = .{ .ident = name }, .value = p.arena.patPtr(val_pat.data, val_pat.loc) } };
- count += 1;
- } else {
- props[count] = .{ .shorthand = name };
- count += 1;
- }
- } else if (p.peek() == .lbracket) {
- const key = try p.parsePropKey();
- try p.expect(.@":");
- const val_pat = try Pat.parse(p);
- props[count] = .{ .key_value = .{ .key = key, .value = p.arena.patPtr(val_pat.data, val_pat.loc) } };
- count += 1;
- } else {
- break;
- }
- if (!p.match(.comma)) break;
- }
-
- try p.expect(.rbrace);
- return Pat{ .loc = .{ .start = start, .end = p.tok().loc.start }, .data = .{ .object = .{ .loc = .{ .start = start, .end = p.tok().loc.start }, .props = props[0..count], .rest = rest } } };
- },
- .lbracket => {
- p.advance();
- var elems: [128]?Pat = undefined;
- var count: usize = 0;
- var rest: ?*const Pat = null;
-
- while (p.peek() != .rbracket and p.peek() != .eof) {
- if (p.match(.comma)) {
- elems[count] = null;
- count += 1;
- } else if (p.peek() == .@"...") {
- p.advance();
- const pat = try Pat.parse(p);
- rest = p.arena.patPtr(pat.data, pat.loc);
- break;
- } else {
- const pat = try Pat.parse(p);
- elems[count] = pat;
- count += 1;
- if (!p.match(.comma)) break;
- }
- }
-
- try p.expect(.rbracket);
- return Pat{ .loc = .{ .start = start, .end = p.tok().loc.start }, .data = .{ .array = .{ .loc = .{ .start = start, .end = p.tok().loc.start }, .elems = elems[0..count], .rest = rest } } };
- },
- else => return ParseError.ExpectedParameterName,
- }
- }
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- switch (self.data) {
- .ident => |name| f.write(name),
- .object => |o| o.fmt(f),
- .array => |a| a.fmt(f),
- .assign => |a| {
- a.left.*.fmt(f);
- f.write(" = ");
- a.right.*.fmt(f);
- },
- .rest => |r| {
- f.write("...");
- r.arg.*.fmt(f);
- },
- }
- }
-};
-
-pub const DeclType = union(enum) {
- @"fn": FnDecl,
- class: ClassDecl,
- @"var": VarDecl,
- using: UsingDecl,
- import: ImportDecl,
- @"export": ExportDecl,
- export_default: ExportDefault,
-};
-
-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) {
- null: void,
- bool: bool,
- number: f64,
- string: []const u8,
- regex: Regex,
- bigint: []const u8,
-};
-
-pub const Lit = struct {
- loc: Loc,
- data: LitType,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- switch (self.data) {
- .null => f.write("null"),
- .bool => |b| f.write(if (b) "true" else "false"),
- .number => |n| {
- var buf: [64]u8 = undefined;
- f.write(std.fmt.bufPrint(&buf, "{d}", .{n}) catch "0");
- },
- .string => |s| {
- f.writeChar('"');
- f.write(s);
- f.writeChar('"');
- },
- .regex => |r| {
- f.writeChar('/');
- f.write(r.pattern);
- f.writeChar('/');
- f.write(r.flags);
- },
- .bigint => |s| {
- f.write(s);
- f.writeChar('n');
- },
- }
- }
-};
-
-pub const Regex = struct {
- pattern: []const u8,
- flags: []const u8,
-};
-
-pub const PropType = union(enum) {
- init: PropInit,
- get: PropGet,
- set: PropSet,
- shorthand: []const u8,
- spread: SpreadElem,
- method: FnExpr,
-};
-
-pub const Prop = struct {
- loc: Loc,
- key: PropKey,
- data: PropType,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- switch (self.data) {
- .init => |p_init| {
- self.key.fmt(f);
- f.write(": ");
- p_init.value.*.fmt(f);
- },
- .get => |g| g.fmt(f),
- .set => |s| s.fmt(f),
- .shorthand => |name| f.write(name),
- .spread => |sp| {
- f.write("...");
- sp.arg.*.fmt(f);
- },
- .method => |m| m.fmt(f),
- }
- }
-};
-
-pub const PropKey = union(enum) {
- ident: []const u8,
- string: []const u8,
- number: f64,
- computed: *const Expr,
- private: []const u8,
-};
-
-pub const PropInit = struct {
- key: PropKey,
- value: *const Expr,
-};
-
-pub const PropGet = struct {
- key: PropKey,
- body: BlockStmt,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("get ");
- self.key.fmt(f);
- f.write("() ");
- self.body.fmt(f);
- }
-};
-
-pub const PropSet = struct {
- key: PropKey,
- param: Pat,
- body: BlockStmt,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("set ");
- self.key.fmt(f);
- f.writeChar('(');
- self.param.fmt(f);
- f.write(") ");
- self.body.fmt(f);
- }
-};
-
-pub const SpreadElem = struct {
- loc: Loc,
- arg: *const Expr,
-};
-
-pub const BlockStmt = struct {
- loc: Loc,
- body: []const Stmt,
-
- pub fn parse(p: *Parser) ParseError!BlockStmt {
- const start = try p.expectTok(.lbrace);
- var stmts: [1024]Stmt = undefined;
- var count: usize = 0;
-
- while (p.peek() != .rbrace and p.peek() != .eof) {
- const s = try Stmt.parse(p);
- stmts[count] = s;
- count += 1;
- if (count >= stmts.len) return ParseError.TooManyTokens;
- }
-
- const end = try p.expectTok(.rbrace);
- return .{ .loc = .{ .start = start.loc.start, .end = end.loc.end }, .body = stmts[0..count] };
- }
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.writeChar('{');
- if (self.body.len > 0) {
- f.newline();
- f.indent_level += 1;
- for (self.body) |s| {
- f.writeIndent();
- s.fmt(f);
- f.newline();
- }
- f.indent_level -= 1;
- f.writeIndent();
- }
- f.writeChar('}');
- }
-};
-
-pub const ExprStmt = struct {
- loc: Loc,
- expr: *const Expr,
-
- pub fn parse(p: *Parser) ParseError!ExprStmt {
- const expr = try Expr.parse(p, 0);
- p.semicolon();
- return .{ .loc = expr.loc, .expr = expr };
- }
-};
-
-pub const IfStmt = struct {
- loc: Loc,
- condition: *const Expr,
- consequent: *const Stmt,
- alternate: ?*const Stmt,
-
- pub fn parse(p: *Parser) ParseError!IfStmt {
- const start = p.advanceTok().loc.start;
- try p.expect(.lparen);
- const condition = try Expr.parse(p, 0);
- try p.expect(.rparen);
- 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")) {
- 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 };
- }
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("if (");
- self.condition.*.fmt(f);
- f.write(") ");
- self.consequent.*.fmt(f);
- if (self.alternate) |alt| {
- f.write(" else ");
- alt.*.fmt(f);
- }
- }
-};
-
-pub const WhileStmt = struct {
- loc: Loc,
- condition: *const Expr,
- body: *const Stmt,
-
- pub fn parse(p: *Parser) ParseError!WhileStmt {
- const start = p.advanceTok().loc.start;
- try p.expect(.lparen);
- const condition = try Expr.parse(p, 0);
- try p.expect(.rparen);
- 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 };
- }
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("while (");
- self.condition.*.fmt(f);
- f.write(") ");
- self.body.*.fmt(f);
- }
-};
-
-pub const DoWhileStmt = struct {
- loc: Loc,
- body: *const Stmt,
- condition: *const Expr,
-
- pub fn parse(p: *Parser) ParseError!DoWhileStmt {
- const start = p.advanceTok().loc.start;
- 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);
- try p.expect(.rparen);
- p.semicolon();
- return .{ .loc = .{ .start = start, .end = condition.loc.end }, .body = body, .condition = condition };
- }
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("do ");
- self.body.*.fmt(f);
- f.write(" while (");
- self.condition.*.fmt(f);
- f.write(");");
- }
-};
-
-pub const ForInit = union(enum) {
- expr: *const Expr,
- decl: *const Decl,
-};
-
-pub const ForStmt = struct {
- loc: Loc,
- init: ?ForInit,
- condition: ?*const Expr,
- update: ?*const Expr,
- body: *const Stmt,
-
- pub fn parse(p: *Parser, start: usize, for_init: ?ForInit) ParseError!ForStmt {
- var condition: ?*const Expr = null;
- var update: ?*const Expr = null;
-
- try p.expect(.semicolon);
- if (p.peek() != .semicolon and p.peek() != .rparen) {
- condition = try Expr.parse(p, 0);
- }
- try p.expect(.semicolon);
- if (p.peek() != .rparen) {
- update = try Expr.parse(p, 0);
- }
- try p.expect(.rparen);
- 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 };
- }
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("for (");
- if (self.init) |init| {
- switch (init) {
- .expr => |e| e.*.fmt(f),
- .decl => |d| d.*.fmt(f),
- }
- }
- f.writeChar(';');
- if (self.condition) |c| {
- f.writeChar(' ');
- c.*.fmt(f);
- }
- f.writeChar(';');
- if (self.update) |u| {
- f.writeChar(' ');
- u.*.fmt(f);
- }
- f.write(") ");
- self.body.*.fmt(f);
- }
-};
-
-pub const ForInStmt = struct {
- loc: Loc,
- left: ForInit,
- right: *const Expr,
- body: *const Stmt,
-
- 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 = 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 };
- }
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("for (");
- switch (self.left) {
- .expr => |e| e.*.fmt(f),
- .decl => |d| d.*.fmt(f),
- }
- f.write(" in ");
- self.right.*.fmt(f);
- f.write(") ");
- self.body.*.fmt(f);
- }
-};
-
-pub const ForOfStmt = struct {
- loc: Loc,
- await_token: bool,
- left: ForInit,
- right: *const Expr,
- body: *const Stmt,
-
- 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 = 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 };
- }
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("for");
- if (self.await_token) f.write(" await");
- f.write(" (");
- switch (self.left) {
- .expr => |e| e.*.fmt(f),
- .decl => |d| d.*.fmt(f),
- }
- f.write(" of ");
- self.right.*.fmt(f);
- f.write(") ");
- self.body.*.fmt(f);
- }
-};
-
-pub const ContinueStmt = struct {
- loc: Loc,
- label: ?[]const u8,
-
- pub fn parse(p: *Parser) ParseError!ContinueStmt {
- const start = p.advanceTok().loc.start;
- var label: ?[]const u8 = null;
- if (p.peek() == .identifier and !p.isLineTerminatorBeforeNext()) {
- const t = p.advanceTok();
- label = p.tokenSlice(t);
- }
- p.semicolon();
- return .{ .loc = .{ .start = start, .end = start + 8 }, .label = label };
- }
-};
-
-pub const BreakStmt = struct {
- loc: Loc,
- label: ?[]const u8,
-
- pub fn parse(p: *Parser) ParseError!BreakStmt {
- const start = p.advanceTok().loc.start;
- var label: ?[]const u8 = null;
- if (p.peek() == .identifier and !p.isLineTerminatorBeforeNext()) {
- const t = p.advanceTok();
- label = p.tokenSlice(t);
- }
- p.semicolon();
- return .{ .loc = .{ .start = start, .end = start + 5 }, .label = label };
- }
-};
-
-pub const ReturnStmt = struct {
- loc: Loc,
- arg: ?*const Expr,
-
- pub fn parse(p: *Parser) ParseError!ReturnStmt {
- const start = p.advanceTok().loc.start;
- var arg: ?*const Expr = null;
- if (p.peek() != .semicolon and p.peek() != .rbrace and p.peek() != .eof and !p.isLineTerminatorBeforeNext()) {
- arg = try Expr.parse(p, 0);
- }
- p.semicolon();
- return .{ .loc = .{ .start = start, .end = if (arg) |a| a.loc.end else start + 6 }, .arg = arg };
- }
-};
-
-pub const SwitchStmt = struct {
- loc: Loc,
- discriminant: *const Expr,
- cases: []const SwitchCase,
-
- pub fn parse(p: *Parser) ParseError!SwitchStmt {
- const start = p.advanceTok().loc.start;
- try p.expect(.lparen);
- const discriminant = try Expr.parse(p, 0);
- try p.expect(.rparen);
- try p.expect(.lbrace);
-
- var cases: [256]SwitchCase = undefined;
- var case_count: usize = 0;
-
- while (p.peek() == .case or p.peek() == .default) {
- const case_start = p.tok().loc.start;
- if (p.matchKeyword(.case)) {
- const case_test = try Expr.parse(p, 0);
- try p.expect(.@":");
- var consequent: [128]Stmt = undefined;
- var cons_count: usize = 0;
- while (p.peek() != .case and p.peek() != .default and p.peek() != .rbrace and p.peek() != .eof) {
- const s = try Stmt.parse(p);
- consequent[cons_count] = s;
- cons_count += 1;
- }
- cases[case_count] = .{ .loc = .{ .start = case_start, .end = p.tok().loc.start }, .condition = case_test, .consequent = consequent[0..cons_count] };
- case_count += 1;
- } else if (p.matchKeyword(.default)) {
- try p.expect(.@":");
- var consequent: [128]Stmt = undefined;
- var cons_count: usize = 0;
- while (p.peek() != .case and p.peek() != .default and p.peek() != .rbrace and p.peek() != .eof) {
- const s = try Stmt.parse(p);
- consequent[cons_count] = s;
- cons_count += 1;
- }
- cases[case_count] = .{ .loc = .{ .start = case_start, .end = p.tok().loc.start }, .condition = null, .consequent = consequent[0..cons_count] };
- case_count += 1;
- }
- }
-
- const end_tok = try p.expectTok(.rbrace);
- return .{ .loc = .{ .start = start, .end = end_tok.loc.end }, .discriminant = discriminant, .cases = cases[0..case_count] };
- }
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("switch (");
- self.discriminant.*.fmt(f);
- f.write(") {");
- f.newline();
- f.indent_level += 1;
- for (self.cases) |c| {
- f.writeIndent();
- c.fmt(f);
- f.newline();
- }
- f.indent_level -= 1;
- f.writeIndent();
- f.writeChar('}');
- }
-};
-
-pub const SwitchCase = struct {
- loc: Loc,
- condition: ?*const Expr,
- consequent: []const Stmt,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- if (self.condition) |c| {
- f.write("case ");
- c.*.fmt(f);
- } else {
- f.write("default");
- }
- f.writeChar(':');
- for (self.consequent) |s| {
- f.writeChar(' ');
- s.fmt(f);
- }
- }
-};
-
-pub const ThrowStmt = struct {
- loc: Loc,
- arg: *const Expr,
-
- pub fn parse(p: *Parser) ParseError!ThrowStmt {
- const start = p.advanceTok().loc.start;
- if (p.isLineTerminatorBeforeNext()) return ParseError.ExpectedExpression;
- const arg = try Expr.parse(p, 0);
- p.semicolon();
- return .{ .loc = .{ .start = start, .end = arg.loc.end }, .arg = arg };
- }
-};
-
-pub const TryStmt = struct {
- loc: Loc,
- block: BlockStmt,
- handler: ?CatchClause,
- finalizer: ?BlockStmt,
-
- pub fn parse(p: *Parser) ParseError!TryStmt {
- const start = p.advanceTok().loc.start;
- const block = try p.parseFunctionBody();
- var handler: ?CatchClause = null;
- var finalizer: ?BlockStmt = null;
-
- if (p.matchKeyword(.@"catch")) {
- var param: ?Pat = null;
- if (p.match(.lparen)) {
- const pat = try Pat.parse(p);
- param = pat;
- try p.expect(.rparen);
- }
- const catch_body = try p.parseFunctionBody();
- handler = .{ .loc = .{ .start = block.loc.start, .end = catch_body.loc.end }, .param = param, .body = catch_body };
- }
-
- if (p.matchKeyword(.finally)) {
- const finally_body = try p.parseFunctionBody();
- finalizer = finally_body;
- }
-
- const end = if (finalizer) |f| f.loc.end else if (handler) |h| h.loc.end else block.loc.end;
- return .{ .loc = .{ .start = start, .end = end }, .block = block, .handler = handler, .finalizer = finalizer };
- }
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("try ");
- self.block.fmt(f);
- if (self.handler) |h| {
- f.writeChar(' ');
- h.fmt(f);
- }
- if (self.finalizer) |fin| {
- f.write(" finally ");
- fin.fmt(f);
- }
- }
-};
-
-pub const CatchClause = struct {
- loc: Loc,
- param: ?Pat,
- body: BlockStmt,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("catch");
- if (self.param) |p| {
- f.write(" (");
- p.fmt(f);
- f.write(") ");
- } else {
- f.writeChar(' ');
- }
- self.body.fmt(f);
- }
-};
-
-pub const LabelledStmt = struct {
- loc: Loc,
- label: []const u8,
- body: *const Stmt,
-};
-
-pub const WithStmt = struct {
- loc: Loc,
- obj: *const Expr,
- body: *const Stmt,
-
- pub fn parse(p: *Parser) ParseError!WithStmt {
- const start = p.advanceTok().loc.start;
- try p.expect(.lparen);
- const obj = try Expr.parse(p, 0);
- try p.expect(.rparen);
- 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 };
- }
-};
-
-pub const VarStmt = struct {
- loc: Loc,
- kind: VarKind,
- decls: []const VarDeclarator,
-
- pub fn parse(p: *Parser) ParseError!VarStmt {
- const kind_tok = p.advanceTok();
- var decls: [128]VarDeclarator = undefined;
- var count: usize = 0;
-
- while (true) {
- const pat = try Pat.parse(p);
- var decl_init: ?*const Expr = null;
- if (p.match(.@"=")) {
- decl_init = try Expr.parse(p, 0);
- }
- decls[count] = .{ .loc = pat.loc, .id = pat, .init = decl_init };
- count += 1;
- if (!p.match(.comma)) break;
- }
-
- p.semicolon();
- return .{ .loc = .{ .start = kind_tok.loc.start, .end = decls[count - 1].loc.end }, .kind = .@"var", .decls = decls[0..count] };
- }
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write(@tagName(self.kind));
- f.writeChar(' ');
- for (self.decls, 0..) |d, i| {
- if (i > 0) f.write(", ");
- d.fmt(f);
- }
- f.writeChar(';');
- }
-};
-
-pub const UsingStmt = struct {
- loc: Loc,
- decls: []const VarDeclarator,
-
- pub fn parse(p: *Parser) ParseError!UsingStmt {
- const start = p.advanceTok().loc.start;
- var decls: [128]VarDeclarator = undefined;
- var count: usize = 0;
-
- while (true) {
- const id_tok = try p.expectTok(.identifier);
- const id = p.tokenSlice(id_tok);
- var decl_init: ?*const Expr = null;
- if (p.match(.@"=")) {
- decl_init = try Expr.parse(p, 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 (!p.match(.comma)) break;
- }
-
- p.semicolon();
- return .{ .loc = .{ .start = start, .end = decls[count - 1].loc.end }, .decls = decls[0..count] };
- }
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("using ");
- for (self.decls, 0..) |d, i| {
- if (i > 0) f.write(", ");
- d.fmt(f);
- }
- }
-};
-
-pub const VarKind = enum {
- @"var",
- let,
- @"const",
-};
-
-pub const VarDecl = struct {
- loc: Loc,
- kind: VarKind,
- decls: []const VarDeclarator,
-};
-
-pub const LexicalDecl = struct {
- loc: Loc,
- kind: VarKind,
- decls: []const VarDeclarator,
-
- pub fn parse(p: *Parser) ParseError!LexicalDecl {
- const kind_tok = p.advanceTok();
- var decls: [128]VarDeclarator = undefined;
- var count: usize = 0;
-
- while (true) {
- const pat = try Pat.parse(p);
- var decl_init: ?*const Expr = null;
- if (kind_tok.kind == .@"const") {
- if (!p.match(.@"=")) return ParseError.ExpectedToken;
- decl_init = try Expr.parse(p, 0);
- } else if (p.match(.@"=")) {
- decl_init = try Expr.parse(p, 0);
- }
- decls[count] = .{ .loc = pat.loc, .id = pat, .init = decl_init };
- count += 1;
- if (!p.match(.comma)) break;
- }
-
- p.semicolon();
- const kind: VarKind = switch (kind_tok.kind) {
- .let => .let,
- .@"const" => .@"const",
- else => return ParseError.UnexpectedToken,
- };
- return .{ .loc = .{ .start = kind_tok.loc.start, .end = decls[count - 1].loc.end }, .kind = kind, .decls = decls[0..count] };
- }
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write(@tagName(self.kind));
- f.writeChar(' ');
- for (self.decls, 0..) |d, i| {
- if (i > 0) f.write(", ");
- d.fmt(f);
- }
- f.writeChar(';');
- }
-};
-
-pub const UsingDecl = struct {
- loc: Loc,
- is_await: bool,
- decls: []const VarDeclarator,
-};
-
-pub const VarDeclarator = struct {
- loc: Loc,
- id: Pat,
- init: ?*const Expr,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- self.id.fmt(f);
- if (self.init) |init| {
- f.write(" = ");
- init.*.fmt(f);
- }
- }
-};
-
-pub const ObjectPat = struct {
- loc: Loc,
- props: []const PatProp,
- rest: ?*const Pat,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.writeChar('{');
- var first = true;
- for (self.props) |prop| {
- if (!first) f.write(", ");
- first = false;
- switch (prop) {
- .key_value => |kv| {
- kv.key.fmt(f);
- f.write(": ");
- kv.value.*.fmt(f);
- },
- .shorthand => |name| f.write(name),
- .rest => |r| {
- f.write("...");
- r.*.fmt(f);
- },
- }
- }
- if (self.rest) |rest| {
- if (!first) f.write(", ");
- f.write("...");
- rest.*.fmt(f);
- }
- f.writeChar('}');
- }
-};
-
-pub const PatProp = union(enum) {
- key_value: struct {
- key: PropKey,
- value: *const Pat,
- },
- shorthand: []const u8,
- rest: *const Pat,
-};
-
-pub const ArrayPat = struct {
- loc: Loc,
- elems: []const ?Pat,
- rest: ?*const Pat,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.writeChar('[');
- for (self.elems, 0..) |elem, i| {
- if (i > 0) f.write(", ");
- if (elem) |pat| pat.fmt(f);
- }
- if (self.rest) |rest| {
- if (self.elems.len > 0) f.write(", ");
- f.write("...");
- rest.*.fmt(f);
- }
- f.writeChar(']');
- }
-};
-
-pub const AssignPat = struct {
- loc: Loc,
- left: *const Pat,
- right: *const Expr,
-};
-
-pub const RestPat = struct {
- loc: Loc,
- arg: *const Pat,
-};
-
-pub const FnType = union(enum) {
- normal: void,
- generator: void,
- async: void,
- async_generator: void,
-};
-
-pub const FnDecl = struct {
- loc: Loc,
- fn_type: FnType,
- id: ?[]const u8,
- params: []const Pat,
- body: BlockStmt,
-
- pub fn parse(p: *Parser, fn_type_arg: FnType, mode: enum { stmt, expr }) ParseError!?Stmt {
- const start = p.tok().loc.start;
- var fn_type = fn_type_arg;
-
- if (fn_type == .async) {
- _ = p.advanceTok();
- } else if (fn_type == .async_generator) {
- _ = p.advanceTok();
- _ = p.advanceTok();
- }
-
- _ = p.advanceTok();
-
- if (fn_type == .normal and p.peek() == .@"*") {
- p.advance();
- fn_type = .generator;
- }
-
- var id: ?[]const u8 = null;
- if (p.peek().isIdentifier()) {
- const id_tok = p.advanceTok();
- id = p.tokenSlice(id_tok);
- }
-
- const params = try p.parseParams();
- const body = try p.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;
- }
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("function");
- if (self.fn_type == .generator or self.fn_type == .async_generator) {
- f.write("*");
- }
- if (self.fn_type == .async or self.fn_type == .async_generator) {
- f.write(" async");
- }
- if (self.id) |id| {
- f.writeChar(' ');
- f.write(id);
- }
- f.writeChar('(');
- for (self.params, 0..) |p, i| {
- if (i > 0) f.write(", ");
- p.fmt(f);
- }
- f.write(") ");
- self.body.fmt(f);
- }
-};
-
-pub const FnExpr = struct {
- loc: Loc,
- fn_type: FnType,
- id: ?[]const u8,
- params: []const Pat,
- body: BlockStmt,
-
- pub fn parse(p: *Parser) ParseError!*const Expr {
- const start = p.advanceTok().loc.start;
-
- var fn_type: FnType = .normal;
- if (p.peek() == .@"*") {
- fn_type = .generator;
- p.advance();
- }
-
- var id: ?[]const u8 = null;
- if (p.peek() == .identifier or p.peek().isKeywordButNotLet()) {
- const id_tok = p.advanceTok();
- id = p.tokenSlice(id_tok);
- }
-
- const params = try p.parseParams();
- const body = try p.parseFunctionBody();
-
- return p.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 },
- );
- }
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("function");
- if (self.fn_type == .generator or self.fn_type == .async_generator) {
- f.write("*");
- }
- if (self.fn_type == .async or self.fn_type == .async_generator) {
- f.write(" async");
- }
- if (self.id) |id| {
- f.writeChar(' ');
- f.write(id);
- }
- f.writeChar('(');
- for (self.params, 0..) |p, i| {
- if (i > 0) f.write(", ");
- p.fmt(f);
- }
- f.write(") ");
- self.body.fmt(f);
- }
-};
-
-pub const ArrowFn = struct {
- loc: Loc,
- async_token: bool,
- params: []const Pat,
- body: union(enum) {
- block: BlockStmt,
- expr: *const Expr,
- },
-
- pub fn parse(p: *Parser, params_info: struct { loc: Loc, params: []const Pat }) ParseError!*const Expr {
- try p.expect(.@"=>");
- const async_token = false;
-
- if (p.peek() == .lbrace) {
- const body = try p.parseFunctionBody();
- return p.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 Expr.parse(p, 0);
- return p.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 },
- );
- }
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- if (self.async_token) f.write("async ");
- f.writeChar('(');
- for (self.params, 0..) |p, i| {
- if (i > 0) f.write(", ");
- p.fmt(f);
- }
- f.write(") => ");
- switch (self.body) {
- .block => |b| b.fmt(f),
- .expr => |e| e.*.fmt(f),
- }
- }
-};
-
-pub const ClassDecl = struct {
- loc: Loc,
- id: ?[]const u8,
- super_class: ?*const Expr,
- body: []const ClassElem,
-
- pub fn parse(p: *Parser, mode: enum { stmt, expr }) ParseError!?Stmt {
- const start = p.advanceTok().loc.start;
-
- var id: ?[]const u8 = null;
- if (p.peek() == .identifier) {
- const id_tok = p.advanceTok();
- id = p.tokenSlice(id_tok);
- }
-
- var super_class: ?*const Expr = null;
- if (p.matchKeyword(.extends)) {
- super_class = try Expr.parse(p, 0);
- }
-
- try p.expect(.lbrace);
- var elems: [128]ClassElem = undefined;
- var elem_count: usize = 0;
-
- while (p.peek() != .rbrace and p.peek() != .eof) {
- const elem = try ClassElem.parse(p);
- elems[elem_count] = elem;
- elem_count += 1;
- if (elem_count >= elems.len) return ParseError.TooManyTokens;
- }
-
- const end_tok = try p.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;
- }
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("class");
- if (self.id) |id| {
- f.writeChar(' ');
- f.write(id);
- }
- if (self.super_class) |sc| {
- f.write(" extends ");
- sc.*.fmt(f);
- }
- f.write(" {");
- f.newline();
- f.indent_level += 1;
- for (self.body) |elem| {
- f.writeIndent();
- elem.fmt(f);
- f.newline();
- }
- f.indent_level -= 1;
- f.writeIndent();
- f.writeChar('}');
- }
-};
-
-pub const ClassExpr = struct {
- loc: Loc,
- id: ?[]const u8,
- super_class: ?*const Expr,
- body: []const ClassElem,
-
- pub fn parse(p: *Parser) ParseError!*const Expr {
- const start = p.advanceTok().loc.start;
-
- var id: ?[]const u8 = null;
- if (p.peek() == .identifier) {
- const id_tok = p.advanceTok();
- id = p.tokenSlice(id_tok);
- }
-
- var super_class: ?*const Expr = null;
- if (p.matchKeyword(.extends)) {
- super_class = try Expr.parse(p, 0);
- }
-
- try p.expect(.lbrace);
- var elems: [128]ClassElem = undefined;
- var elem_count: usize = 0;
-
- while (p.peek() != .rbrace and p.peek() != .eof) {
- const elem = try ClassElem.parse(p);
- elems[elem_count] = elem;
- elem_count += 1;
- }
-
- const end_tok = try p.expectTok(.rbrace);
- return p.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 },
- );
- }
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("class");
- if (self.id) |id| {
- f.writeChar(' ');
- f.write(id);
- }
- if (self.super_class) |sc| {
- f.write(" extends ");
- sc.*.fmt(f);
- }
- f.write(" {");
- f.newline();
- f.indent_level += 1;
- for (self.body) |elem| {
- f.writeIndent();
- elem.fmt(f);
- f.newline();
- }
- f.indent_level -= 1;
- f.writeIndent();
- f.writeChar('}');
- }
-};
-
-pub const ClassElem = union(enum) {
- method: FnExpr,
- get: PropGet,
- set: PropSet,
- field: FieldDef,
- static_block: BlockStmt,
- static_method: FnExpr,
- static_get: PropGet,
- static_set: PropSet,
- static_field: FieldDef,
-
- pub fn parse(p: *Parser) ParseError!ClassElem {
- const start = p.tok().loc.start;
- const is_static = p.matchKeyword(.static);
-
- if (is_static and p.peek() == .lbrace) {
- const body = try p.parseFunctionBody();
- return ClassElem{ .static_block = body };
- }
-
- if (p.matchKeyword(.get)) {
- const key = try p.parsePropKey();
- try p.expect(.lparen);
- try p.expect(.rparen);
- const body = try p.parseFunctionBody();
- if (is_static) return ClassElem{ .static_get = .{ .key = key, .body = body } };
- return ClassElem{ .get = .{ .key = key, .body = body } };
- }
-
- if (p.matchKeyword(.set)) {
- const key = try p.parsePropKey();
- try p.expect(.lparen);
- const param = try Pat.parse(p);
- try p.expect(.rparen);
- const body = try p.parseFunctionBody();
- if (is_static) return ClassElem{ .static_set = .{ .key = key, .param = param, .body = body } };
- return ClassElem{ .set = .{ .key = key, .param = param, .body = body } };
- }
-
- if (p.match(.@"*")) {
- _ = try p.parsePropKey();
- try p.expect(.lparen);
- const params = try p.parseParams();
- const body = try p.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 p.parsePropKey();
-
- if (p.peek() == .lparen) {
- const params = try p.parseParams();
- const body = try p.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 (p.match(.@"=")) {
- value = try Expr.parse(p, 0);
- }
- if (p.peek() == .semicolon) p.advance();
-
- const field = FieldDef{ .loc = .{ .start = start, .end = if (value) |v| v.loc.end else p.tok().loc.start }, .key = key, .value = value };
- if (is_static) return ClassElem{ .static_field = field };
- return ClassElem{ .field = field };
- }
-};
-
-pub const FieldDef = struct {
- loc: Loc,
- key: PropKey,
- value: ?*const Expr,
-};
-
-pub const ImportDecl = struct {
- loc: Loc,
- specifiers: []const ImportSpecifier,
- source: []const u8,
- attributes: []const ImportAttribute,
-
- pub fn parse(p: *Parser) ParseError!ImportDecl {
- const start = p.advanceTok().loc.start;
- var specifiers: [32]ImportSpecifier = undefined;
- var spec_count: usize = 0;
-
- if (p.peek() != .string and p.peek() != .lparen) {
- if (p.peek() == .identifier or p.peek().isKeyword()) {
- const local_tok = p.advanceTok();
- const local = p.tokenSlice(local_tok);
- specifiers[spec_count] = .{ .default = local };
- spec_count += 1;
- if (p.match(.comma)) {}
- }
-
- if (p.match(.@"*")) {
- try p.expect(.identifier);
- const ns_tok = try p.expectTok(.identifier);
- const ns = p.tokenSlice(ns_tok);
- specifiers[spec_count] = .{ .namespace = ns };
- spec_count += 1;
- } else if (p.peek() == .lbrace) {
- p.advance();
- if (p.peek() != .rbrace) {
- while (true) {
- const imported_tok = try p.expectTok(.identifier);
- const imported = p.tokenSlice(imported_tok);
- var local = imported;
- if (p.match(.identifier)) {
- local = p.tokenSlice(p.tokens[p.pos - 1]);
- }
- specifiers[spec_count] = .{ .named = .{ .imported = imported, .local = local } };
- spec_count += 1;
- if (!p.match(.comma)) break;
- }
- }
- try p.expect(.rbrace);
- }
- }
-
- var source: []const u8 = "";
- var attrs: [8]ImportAttribute = undefined;
- var attr_count: usize = 0;
-
- if (p.matchKeyword(.from)) {
- const str_tok = try p.expectTok(.string);
- source = p.tokenSlice(str_tok);
- } else if (p.peek() == .string) {
- const str_tok = p.advanceTok();
- source = p.tokenSlice(str_tok);
- }
-
- if (p.matchKeyword(.with)) {
- try p.expect(.lbrace);
- while (p.peek() != .rbrace and p.peek() != .eof) {
- const key_tok = p.advanceTok();
- const key = p.tokenSlice(key_tok);
- try p.expect(.@":");
- const val_tok = try p.expectTok(.string);
- const val = p.tokenSlice(val_tok);
- attrs[attr_count] = .{ .key = key, .value = val };
- attr_count += 1;
- _ = p.match(.comma);
- }
- try p.expect(.rbrace);
- }
-
- try p.expect(.semicolon);
- return .{ .loc = .{ .start = start, .end = p.tok().loc.start }, .specifiers = specifiers[0..spec_count], .source = source, .attributes = attrs[0..attr_count] };
- }
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("import ");
- if (self.specifiers.len > 0) {
- for (self.specifiers, 0..) |spec, j| {
- if (j > 0) f.write(", ");
- spec.fmt(f);
- }
- f.write(" from ");
- }
- f.writeChar('"');
- f.write(self.source);
- f.writeChar('"');
- for (self.attributes) |a| {
- f.write(" /* ");
- a.fmt(f);
- f.write(" */");
- }
- f.writeChar(';');
- }
-};
-
-pub const ImportSpecifier = union(enum) {
- default: []const u8,
- namespace: []const u8,
- named: struct { imported: []const u8, local: []const u8 },
-};
-
-pub const ImportAttribute = struct {
- key: []const u8,
- value: []const u8,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write(self.key);
- f.write(": \"");
- f.write(self.value);
- f.writeChar('"');
- }
-};
-
-pub const ExportDecl = struct {
- loc: Loc,
- declaration: ?*const Decl,
- specifiers: ?[]const ExportSpecifier,
- source: ?[]const u8,
-
- pub fn parse(p: *Parser) ParseError!ExportDecl {
- const start = p.advanceTok().loc.start;
-
- 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 {
- f.write("export ");
- if (self.declaration) |d| {
- d.*.fmt(f);
- } else if (self.specifiers) |s| {
- f.writeChar('{');
- for (s, 0..) |sp, i| {
- if (i > 0) f.write(", ");
- sp.fmt(f);
- }
- f.writeChar('}');
- if (self.source) |src| {
- f.write(" from \"");
- f.write(src);
- f.writeChar('"');
- }
- f.writeChar(';');
- } else if (self.source) |src| {
- f.write("* from \"");
- f.write(src);
- f.writeChar("\";");
- }
- }
-};
-
-pub const ExportSpecifier = struct {
- loc: Loc,
- exported: []const u8,
- local: []const u8,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write(self.local);
- if (!std.mem.eql(u8, self.local, self.exported)) {
- f.write(" as ");
- f.write(self.exported);
- }
- }
-};
-
-pub const ExportDefault = struct {
- loc: Loc,
- declaration: *const Decl,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("export default ");
- self.declaration.*.fmt(f);
- }
-};
-
-pub const ArrayExpr = struct {
- loc: Loc,
- elems: []const ?Expr,
-
- pub fn parse(p: *Parser) ParseError!*const Expr {
- const start = p.advanceTok().loc.start;
- var elems: [256]?Expr = undefined;
- var count: usize = 0;
-
- while (p.peek() != .rbracket and p.peek() != .eof) {
- if (p.match(.comma)) {
- elems[count] = null;
- count += 1;
- } else if (p.peek() == .@"...") {
- p.advance();
- const arg = try Expr.parse(p, 0);
- elems[count] = Expr{ .loc = arg.loc, .data = .{ .spread = .{ .loc = arg.loc, .arg = arg } } };
- count += 1;
- if (!p.match(.comma)) break;
- } else {
- const expr = try Expr.parse(p, 0);
- elems[count] = expr.*;
- count += 1;
- if (!p.match(.comma)) break;
- }
- }
-
- try p.expect(.rbracket);
- return p.arena.exprPtr(.{ .array = .{ .loc = .{ .start = start, .end = p.tok().loc.start }, .elems = elems[0..count] } }, .{ .start = start, .end = p.tok().loc.start });
- }
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.writeChar('[');
- for (self.elems, 0..) |elem, i| {
- if (i > 0) f.write(", ");
- if (elem) |e| e.fmt(f);
- }
- f.writeChar(']');
- }
-};
-
-pub const ObjectExpr = struct {
- loc: Loc,
- props: []const Prop,
-
- pub fn parse(p: *Parser) ParseError!*const Expr {
- const start = p.advanceTok().loc.start;
- var props: [128]Prop = undefined;
- var count: usize = 0;
-
- while (p.peek() != .rbrace and p.peek() != .eof) {
- if (p.peek() == .@"...") {
- p.advance();
- const arg = try Expr.parse(p, 0);
- props[count] = .{ .loc = arg.loc, .key = .{ .ident = "" }, .data = .{ .spread = .{ .loc = arg.loc, .arg = arg } } };
- count += 1;
- } else if (p.peek() == .identifier) {
- const name_tok = p.tok();
- const name = p.tokenSlice(name_tok);
- p.advance();
- if (p.peek() == .@":") {
- p.advance();
- const value = try Expr.parse(p, 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 (p.peek() == .lparen) {
- const params = try p.parseParams();
- const body = try p.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 (p.peek() == .@"=") {
- p.advance();
- const value = try Expr.parse(p, 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 (p.peek() == .comma or p.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 (p.peek() == .number or p.peek() == .string) {
- const key = try p.parsePropKey();
- try p.expect(.@":");
- const value = try Expr.parse(p, 0);
- props[count] = .{ .loc = .{ .start = start, .end = value.loc.end }, .key = key, .data = .{ .init = .{ .key = key, .value = value } } };
- count += 1;
- } else if (p.peek() == .lbracket) {
- const key = try p.parsePropKey();
- try p.expect(.@":");
- const value = try Expr.parse(p, 0);
- props[count] = .{ .loc = .{ .start = start, .end = value.loc.end }, .key = key, .data = .{ .init = .{ .key = key, .value = value } } };
- count += 1;
- } else if (p.matchKeyword(.get)) {
- const key = try p.parsePropKey();
- try p.expect(.lparen);
- try p.expect(.rparen);
- const body = try p.parseFunctionBody();
- props[count] = .{ .loc = .{ .start = start, .end = body.loc.end }, .key = key, .data = .{ .get = .{ .key = key, .body = body } } };
- count += 1;
- } else if (p.matchKeyword(.set)) {
- const key = try p.parsePropKey();
- try p.expect(.lparen);
- const param = try Pat.parse(p);
- try p.expect(.rparen);
- const body = try p.parseFunctionBody();
- props[count] = .{ .loc = .{ .start = start, .end = body.loc.end }, .key = key, .data = .{ .set = .{ .key = key, .param = param, .body = body } } };
- count += 1;
- } else if (p.match(.@"*")) {
- const key = try p.parsePropKey();
- const params = try p.parseParams();
- const body = try p.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 (!p.match(.comma)) break;
- }
-
- try p.expect(.rbrace);
- return p.arena.exprPtr(.{ .object = .{ .loc = .{ .start = start, .end = p.tok().loc.start }, .props = props[0..count] } }, .{ .start = start, .end = p.tok().loc.start });
- }
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.writeChar('{');
- for (self.props, 0..) |p, i| {
- if (i > 0) f.write(", ");
- p.fmt(f);
- }
- f.writeChar('}');
- }
-};
-
-pub const MemberExpr = struct {
- loc: Loc,
- obj: *const Expr,
- prop: union(enum) {
- ident: []const u8,
- private: []const u8,
- },
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- self.obj.*.fmt(f);
- switch (self.prop) {
- .ident => |name| {
- f.writeChar('.');
- f.write(name);
- },
- .private => |name| {
- f.writeChar('.');
- f.write(name);
- },
- }
- }
-};
-
-pub const ComputedMember = struct {
- loc: Loc,
- obj: *const Expr,
- expr: *const Expr,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- self.obj.*.fmt(f);
- f.writeChar('[');
- self.expr.*.fmt(f);
- f.writeChar(']');
- }
-};
-
-pub const CallExpr = struct {
- loc: Loc,
- callee: *const Expr,
- args: []const Expr,
- optional: bool,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- self.callee.*.fmt(f);
- if (self.optional) {
- f.write("?.(");
- } else {
- f.writeChar('(');
- }
- for (self.args, 0..) |a, i| {
- if (i > 0) f.write(", ");
- a.fmt(f);
- }
- f.writeChar(')');
- }
-};
-
-pub const NewExpr = struct {
- loc: Loc,
- callee: *const Expr,
- args: []const Expr,
-};
-
-pub const ChainExpr = struct {
- loc: Loc,
- expr: *const Expr,
-};
-
-pub const UnaryOp = enum {
- @"-",
- @"+",
- @"!",
- @"~",
- typeof,
- void,
- delete,
-};
-
-pub const UnaryExpr = struct {
- loc: Loc,
- op: UnaryOp,
- arg: *const Expr,
- prefix: bool,
-};
-
-pub const BinaryOp = enum {
- @"==",
- @"!=",
- @"===",
- @"!==",
- @"<",
- @"<=",
- @">",
- @">=",
- @"<<",
- @">>",
- @">>>",
- @"+",
- @"-",
- @"*",
- @"/",
- @"%",
- @"**",
- @"|",
- @"^",
- @"&",
- in,
- instanceof,
- @"||",
- @"&&",
- @"??",
-};
-
-pub const BinaryExpr = struct {
- loc: Loc,
- op: BinaryOp,
- left: *const Expr,
- right: *const Expr,
-};
-
-pub const UpdateOp = enum {
- @"++",
- @"--",
-};
-
-pub const UpdateExpr = struct {
- loc: Loc,
- op: UpdateOp,
- arg: *const Expr,
- prefix: bool,
-};
-
-pub const IfExpr = struct {
- loc: Loc,
- condition: *const Expr,
- consequent: *const Expr,
- alternate: *const Expr,
-};
-
-pub const AssignOp = enum {
- @"=",
- @"+=",
- @"-=",
- @"*=",
- @"/=",
- @"%=",
- @"**=",
- @"<<=",
- @">>=",
- @">>>=",
- @"|=",
- @"^=",
- @"&=",
- @"||=",
- @"&&=",
- @"??=",
-};
-
-pub const AssignExpr = struct {
- loc: Loc,
- op: AssignOp,
- left: *const Expr,
- right: *const Expr,
-};
-
-pub const SeqExpr = struct {
- loc: Loc,
- exprs: []const Expr,
-};
-
-pub const YieldExpr = struct {
- loc: Loc,
- arg: ?*const Expr,
- delegate: bool,
-};
-
-pub const AwaitExpr = struct {
- loc: Loc,
- arg: *const Expr,
-};
-
-pub const MetaProp = struct {
- loc: Loc,
- meta: []const u8,
- prop: []const u8,
-};
-
-pub const TemplateLit = struct {
- loc: Loc,
- quasis: []const TemplateElem,
- exprs: []const Expr,
-
- pub fn parse(p: *Parser) ParseError!TemplateLit {
- const start = p.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 (p.peek()) {
- .no_sub_template => {
- const t = p.advanceTok();
- const slice = p.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 = p.advanceTok();
- const slice = p.tokenSlice(t);
- quasis[q_count] = .{ .loc = .{ .start = t.loc.start, .end = t.loc.end }, .value = slice, .tail = false };
- q_count += 1;
- const expr = try Expr.parse(p, 0);
- exprs[e_count] = expr.*;
- e_count += 1;
- try p.expect(.rbrace);
- },
- .template_middle => {
- const t = p.advanceTok();
- const slice = p.tokenSlice(t);
- quasis[q_count] = .{ .loc = .{ .start = t.loc.start, .end = t.loc.end }, .value = slice, .tail = false };
- q_count += 1;
- const expr = try Expr.parse(p, 0);
- exprs[e_count] = expr.*;
- e_count += 1;
- try p.expect(.rbrace);
- },
- .template_tail => {
- const t = p.advanceTok();
- const slice = p.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 = p.tok().loc.start }, .quasis = quasis[0..q_count], .exprs = exprs[0..e_count] };
- }
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.writeChar('`');
- var ei: usize = 0;
- for (self.quasis, 0..) |q, i| {
- if (i > 0 and ei < self.exprs.len) {
- f.write("${");
- self.exprs[ei].fmt(f);
- f.writeChar('}');
- ei += 1;
- }
- f.write(q.value);
- }
- f.writeChar('`');
- }
-};
-
-pub const TemplateElem = struct {
- loc: Loc,
- value: []const u8,
- tail: bool,
-};
-
-pub const TaggedTemplate = struct {
- loc: Loc,
- tag: *const Expr,
- quasi: TemplateLit,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- self.tag.*.fmt(f);
- self.quasi.fmt(f);
- }
-};
-
-pub const Decorator = struct {
- loc: Loc,
- expr: *const Expr,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.writeChar('@');
- self.expr.*.fmt(f);
- }
-};
diff --git a/src/z/parser/js/ast.zig.bak b/src/z/parser/js/ast.zig.bak
deleted file mode 100644
index 3dc29a3..0000000
--- a/src/z/parser/js/ast.zig.bak
+++ /dev/null
@@ -1,3334 +0,0 @@
-const std = @import("std");
-
-pub const Loc = struct {
- start: usize,
- end: usize,
-};
-
-const lex = @import("lexical_grammar.zig");
-const Token = lex.Token;
-const TokenType = lex.TokenType;
-
-pub const FormatOptions = struct {
- indent_size: usize = 4,
-};
-
-pub const Formatter = struct {
- buf: []u8,
- pos: usize,
- indent_level: usize,
- options: FormatOptions,
-
- pub fn write(self: *@This(), s: []const u8) void {
- @memcpy(self.buf[self.pos..][0..s.len], s);
- self.pos += s.len;
- }
-
- pub fn writeChar(self: *@This(), c: u8) void {
- self.buf[self.pos] = c;
- self.pos += 1;
- }
-
- pub fn writeIndent(self: *@This()) void {
- const spaces = self.indent_level * self.options.indent_size;
- var i: usize = 0;
- while (i < spaces) : (i += 1) {
- self.writeChar(' ');
- }
- }
-
- pub fn newline(self: *@This()) void {
- self.writeChar('\n');
- }
-};
-
-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()) void {
- self.pos += 1;
- }
-
- fn advanceTok(self: *@This()) Token {
- const t = self.tokens[self.pos];
- self.pos += 1;
- return t;
- }
-
- fn expect(self: *@This(), kind: TokenType) ParseError!void {
- if (self.peek() != kind) return ParseError.ExpectedToken;
- self.advance();
- }
-
- fn expectTok(self: *@This(), kind: TokenType) ParseError!Token {
- if (self.peek() != kind) return ParseError.ExpectedToken;
- return self.advanceTok();
- }
-
- fn match(self: *@This(), kind: TokenType) bool {
- if (self.peek() == kind) {
- _ = self.advanceTok();
- return true;
- }
- return false;
- }
-
- fn matchKeyword(self: *@This(), kw: TokenType) bool {
- if (self.peek() == kw) {
- _ = self.advanceTok();
- 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];
- 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' => return true,
- else => {},
- }
- }
- return false;
- }
-
- fn as_identifier(self: *@This(), t: Token) []const u8 {
- return self.source[t.loc.start..t.loc.end];
- }
-
- fn tokenSlice(self: *@This(), t: Token) []const u8 {
- 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();
- 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;
- }
-
- const kind: 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.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;
- 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!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;
- }
-
- fn parsePrimaryExpr(self: *@This()) ParseError!*const Expr {
- const start = self.tok().loc.start;
-
- switch (self.peek()) {
- .this => {
- const t = self.advanceTok();
- return self.arena.exprPtr(.{ .this = {} }, .{ .start = start, .end = t.loc.end });
- },
- .super => {
- const t = self.advanceTok();
- return self.arena.exprPtr(.{ .super = {} }, .{ .start = start, .end = t.loc.end });
- },
- .null => {
- const t = self.advanceTok();
- return self.arena.exprPtr(.{ .null = {} }, .{ .start = start, .end = t.loc.end });
- },
- .true => {
- const t = self.advanceTok();
- return self.arena.exprPtr(.{ .bool = true }, .{ .start = start, .end = t.loc.end });
- },
- .false => {
- const t = self.advanceTok();
- return self.arena.exprPtr(.{ .bool = false }, .{ .start = start, .end = t.loc.end });
- },
- .number => {
- const t = self.advanceTok();
- 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.advanceTok();
- return self.arena.exprPtr(.{ .string = self.tokenSlice(t) }, .{ .start = start, .end = t.loc.end });
- },
- .bigint => {
- const t = self.advanceTok();
- return self.arena.exprPtr(.{ .number = 0 }, .{ .start = start, .end = t.loc.end });
- },
- .regex => {
- const t = self.advanceTok();
- 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.advanceTok();
- const name = self.tokenSlice(t);
- return self.arena.exprPtr(.{ .identifier = name }, .{ .start = start, .end = t.loc.end });
- },
- .private_identifier => {
- const t = self.advanceTok();
- 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: 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();
- }
- 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 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: 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: 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: 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: 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: 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: 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);
- _ = 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.expectTok(.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.advanceTok();
- 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 {
- 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 => {
- const t = self.advanceTok();
- return PropKey{ .ident = self.tokenSlice(t) };
- },
- .string => {
- const t = self.advanceTok();
- return PropKey{ .string = self.tokenSlice(t) };
- },
- .number => {
- const t = self.advanceTok();
- const val = std.fmt.parseFloat(f64, self.tokenSlice(t)) catch 0.0;
- return PropKey{ .number = val };
- },
- .lbracket => {
- self.advance();
- const expr = try self.parseExpr(0);
- try self.expect(.rbracket);
- return PropKey{ .computed = expr };
- },
- .private_identifier => {
- const t = self.advanceTok();
- return 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.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,
- .@"=", .@"+=", .@"-=", .@"*=", .@"/=", .@"%=", .@"**=", .@"<<=", .@">>=", .@">>>=", .@"&=", .@"|=", .@"^=", .@"&&=", .@"||=", .@"??=" => 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,
- .lbracket, .@".", .@"?." => BP.member,
- .@"++", .@"--" => BP.update,
- .template_head, .no_sub_template => BP.member,
- else => 0,
- };
- }
-
- fn unaryOpFromToken(self: *@This()) UnaryOp {
- return switch (self.tok().kind) {
- .@"+" => .@"+",
- .@"-" => .@"-",
- .@"!" => .@"!",
- .@"~" => .@"~",
- .typeof => .typeof,
- .void => .void,
- .delete => .delete,
- else => .@"+",
- };
- }
-
- fn assignOpFromToken(self: *@This()) 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 const Node = union(enum) {
- program: Program,
-
- stmt: Stmt,
- expr: Expr,
- pat: Pat,
-
- decl: Decl,
- literal: Lit,
- property: Prop,
- template: TemplateLit,
- spread: SpreadElem,
- decorator: Decorator,
-
- import_specifier: ImportSpecifier,
- export_specifier: ExportSpecifier,
- import_attr: ImportAttribute,
- catch_clause: CatchClause,
- switch_case: SwitchCase,
-};
-
-pub const Program = struct {
- body: []const Stmt,
- loc: Loc,
-
- pub fn parse(comptime source: []const u8) ParseError!Program {
- return comptime blk: {
- var token_buf: [4096]Token = undefined;
- const tokens = lex.Lexer.tokenizeBuf(source, &token_buf);
- var arena: NodeArena = .{};
- var parser = Parser.init(tokens, source, &arena);
- break :blk try parser.parseProgram();
- };
- }
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- @setEvalBranchQuota(100000);
- for (self.body, 0..) |stmt, i| {
- if (i > 0) f.newline();
- stmt.fmt(f);
- }
- }
-};
-
-pub const StmtType = union(enum) {
- block: BlockStmt,
- empty: void,
- debugger: void,
- expr: ExprStmt,
- @"if": IfStmt,
- @"while": WhileStmt,
- do_while: DoWhileStmt,
- @"for": ForStmt,
- for_in: ForInStmt,
- for_of: ForOfStmt,
- @"continue": ContinueStmt,
- @"break": BreakStmt,
- @"return": ReturnStmt,
- @"switch": SwitchStmt,
- throw: ThrowStmt,
- @"try": TryStmt,
- labelled: LabelledStmt,
- with: WithStmt,
- variable: VarStmt,
- lexical: LexicalDecl,
- function: FnDecl,
- class: ClassDecl,
- using: UsingStmt,
- import: ImportDecl,
- @"export": ExportDecl,
-};
-
-pub const Stmt = struct {
- loc: Loc,
- data: StmtType,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- switch (self.data) {
- .block => |b| b.fmt(f),
- .empty => f.writeChar(';'),
- .debugger => f.write("debugger;"),
- .expr => |e| {
- e.expr.*.fmt(f);
- f.writeChar(';');
- },
- .@"if" => |s| s.fmt(f),
- .@"while" => |w| w.fmt(f),
- .do_while => |d| d.fmt(f),
- .@"for" => |fo| fo.fmt(f),
- .for_in => |fi| fi.fmt(f),
- .for_of => |fo| fo.fmt(f),
- .@"continue" => |c| {
- f.write("continue");
- if (c.label) |l| {
- f.writeChar(' ');
- f.write(l);
- }
- f.writeChar(';');
- },
- .@"break" => |b| {
- f.write("break");
- if (b.label) |l| {
- f.writeChar(' ');
- f.write(l);
- }
- f.writeChar(';');
- },
- .@"return" => |r| {
- f.write("return");
- if (r.arg) |a| {
- f.writeChar(' ');
- a.*.fmt(f);
- }
- f.writeChar(';');
- },
- .@"switch" => |s| s.fmt(f),
- .throw => |t| {
- f.write("throw ");
- t.arg.*.fmt(f);
- f.writeChar(';');
- },
- .@"try" => |t| t.fmt(f),
- .labelled => |l| {
- f.write(l.label);
- f.write(": ");
- l.body.*.fmt(f);
- },
- .with => |w| {
- f.write("with (");
- w.obj.*.fmt(f);
- f.write(") ");
- w.body.*.fmt(f);
- },
- .variable => |v| v.fmt(f),
- .lexical => |l| l.fmt(f),
- .using => |u| {
- f.write("using ");
- for (u.decls, 0..) |d, i| {
- if (i > 0) f.write(", ");
- d.fmt(f);
- }
- f.writeChar(';');
- },
- .import => |i| i.fmt(f),
- .@"export" => |e| e.fmt(f),
- .class => |c| c.fmt(f),
- .function => |d| d.fmt(f),
- }
- }
-};
-
-pub const ExprType = union(enum) {
- identifier: []const u8,
- this: void,
- super: void,
- null: void,
- bool: bool,
- number: f64,
- string: []const u8,
- regex: Regex,
- array: ArrayExpr,
- object: ObjectExpr,
- func: FnExpr,
- arrow: ArrowFn,
- class: ClassExpr,
- template: TemplateLit,
- tagged_template: TaggedTemplate,
- member: MemberExpr,
- computed_member: ComputedMember,
- call: CallExpr,
- new: NewExpr,
- chain: ChainExpr,
- unary: UnaryExpr,
- binary: BinaryExpr,
- update: UpdateExpr,
- conditional: IfExpr,
- assign: AssignExpr,
- sequence: SeqExpr,
- spread: SpreadElem,
- yield: YieldExpr,
- await: AwaitExpr,
- meta_prop: MetaProp,
- parenthesized: *const Expr,
- private_ident: []const u8,
-};
-
-pub const Expr = struct {
- loc: Loc,
- data: ExprType,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- switch (self.data) {
- .identifier => |name| f.write(name),
- .this => f.write("this"),
- .super => f.write("super"),
- .null => f.write("null"),
- .bool => |b| f.write(if (b) "true" else "false"),
- .number => |n| {
- var buf: [64]u8 = undefined;
- f.write(std.fmt.bufPrint(&buf, "{d}", .{n}) catch "0");
- },
- .string => |s| {
- f.writeChar('"');
- f.write(s);
- f.writeChar('"');
- },
- .regex => |r| {
- f.writeChar('/');
- f.write(r.pattern);
- f.writeChar('/');
- f.write(r.flags);
- },
- .array => |a| a.fmt(f),
- .object => |o| o.fmt(f),
- .func => |fn_expr| fn_expr.fmt(f),
- .arrow => |arrow| arrow.fmt(f),
- .class => |c| c.fmt(f),
- .template => |t| t.fmt(f),
- .tagged_template => |tt| tt.fmt(f),
- .member => |m| m.fmt(f),
- .computed_member => |cm| cm.fmt(f),
- .call => |call| call.fmt(f),
- .new => |n| {
- f.write("new ");
- n.callee.*.fmt(f);
- f.writeChar('(');
- for (n.args, 0..) |a, i| {
- if (i > 0) f.write(", ");
- a.fmt(f);
- }
- f.writeChar(')');
- },
- .chain => |c| c.expr.*.fmt(f),
- .unary => |u| {
- if (u.prefix) {
- f.write(@tagName(u.op));
- u.arg.*.fmt(f);
- } else {
- u.arg.*.fmt(f);
- f.write(@tagName(u.op));
- }
- },
- .binary => |b| {
- f.writeChar('(');
- b.left.*.fmt(f);
- f.writeChar(' ');
- f.write(@tagName(b.op));
- f.writeChar(' ');
- b.right.*.fmt(f);
- f.writeChar(')');
- },
- .update => |u| {
- const s = if (u.op == .@"++") "++" else "--";
- if (u.prefix) {
- f.write(s);
- u.arg.*.fmt(f);
- } else {
- u.arg.*.fmt(f);
- f.write(s);
- }
- },
- .conditional => |c| {
- c.condition.*.fmt(f);
- f.write(" ? ");
- c.consequent.*.fmt(f);
- f.write(" : ");
- c.alternate.*.fmt(f);
- },
- .assign => |a| {
- a.left.*.fmt(f);
- f.writeChar(' ');
- f.write(@tagName(a.op));
- f.writeChar(' ');
- a.right.*.fmt(f);
- },
- .sequence => |s| {
- for (s.exprs, 0..) |e, i| {
- if (i > 0) f.write(", ");
- e.fmt(f);
- }
- },
- .spread => |s| {
- f.write("...");
- s.arg.*.fmt(f);
- },
- .yield => |y| {
- f.write("yield");
- if (y.delegate) f.write("*");
- if (y.arg) |a| {
- f.writeChar(' ');
- a.*.fmt(f);
- }
- },
- .await => |a| {
- f.write("await ");
- a.arg.*.fmt(f);
- },
- .meta_prop => |m| {
- f.write(m.meta);
- f.writeChar('.');
- f.write(m.prop);
- },
- .parenthesized => |p| {
- f.writeChar('(');
- p.*.fmt(f);
- f.writeChar(')');
- },
- .private_ident => |name| f.write(name),
- }
- }
-};
-
-pub const PatType = union(enum) {
- ident: []const u8,
- object: ObjectPat,
- array: ArrayPat,
- assign: AssignPat,
- rest: RestPat,
-};
-
-pub const Pat = struct {
- loc: Loc,
- data: PatType,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- switch (self.data) {
- .ident => |name| f.write(name),
- .object => |o| o.fmt(f),
- .array => |a| a.fmt(f),
- .assign => |a| {
- a.left.*.fmt(f);
- f.write(" = ");
- a.right.*.fmt(f);
- },
- .rest => |r| {
- f.write("...");
- r.arg.*.fmt(f);
- },
- }
- }
-};
-
-pub const DeclType = union(enum) {
- @"fn": FnDecl,
- class: ClassDecl,
- @"var": VarDecl,
- using: UsingDecl,
- import: ImportDecl,
- @"export": ExportDecl,
- export_default: ExportDefault,
-};
-
-pub const Decl = struct {
- loc: Loc,
- data: DeclType,
-};
-
-pub const LitType = union(enum) {
- null: void,
- bool: bool,
- number: f64,
- string: []const u8,
- regex: Regex,
- bigint: []const u8,
-};
-
-pub const Lit = struct {
- loc: Loc,
- data: LitType,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- switch (self.data) {
- .null => f.write("null"),
- .bool => |b| f.write(if (b) "true" else "false"),
- .number => |n| {
- var buf: [64]u8 = undefined;
- f.write(std.fmt.bufPrint(&buf, "{d}", .{n}) catch "0");
- },
- .string => |s| {
- f.writeChar('"');
- f.write(s);
- f.writeChar('"');
- },
- .regex => |r| {
- f.writeChar('/');
- f.write(r.pattern);
- f.writeChar('/');
- f.write(r.flags);
- },
- .bigint => |s| {
- f.write(s);
- f.writeChar('n');
- },
- }
- }
-};
-
-pub const Regex = struct {
- pattern: []const u8,
- flags: []const u8,
-};
-
-pub const PropType = union(enum) {
- init: PropInit,
- get: PropGet,
- set: PropSet,
- shorthand: []const u8,
- spread: SpreadElem,
- method: FnExpr,
-};
-
-pub const Prop = struct {
- loc: Loc,
- key: PropKey,
- data: PropType,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- switch (self.data) {
- .init => |p_init| {
- self.key.fmt(f);
- f.write(": ");
- p_init.value.*.fmt(f);
- },
- .get => |g| g.fmt(f),
- .set => |s| s.fmt(f),
- .shorthand => |name| f.write(name),
- .spread => |sp| {
- f.write("...");
- sp.arg.*.fmt(f);
- },
- .method => |m| m.fmt(f),
- }
- }
-};
-
-pub const PropKey = union(enum) {
- ident: []const u8,
- string: []const u8,
- number: f64,
- computed: *const Expr,
- private: []const u8,
-};
-
-pub const PropInit = struct {
- key: PropKey,
- value: *const Expr,
-};
-
-pub const PropGet = struct {
- key: PropKey,
- body: BlockStmt,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("get ");
- self.key.fmt(f);
- f.write("() ");
- self.body.fmt(f);
- }
-};
-
-pub const PropSet = struct {
- key: PropKey,
- param: Pat,
- body: BlockStmt,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("set ");
- self.key.fmt(f);
- f.writeChar('(');
- self.param.fmt(f);
- f.write(") ");
- self.body.fmt(f);
- }
-};
-
-pub const SpreadElem = struct {
- loc: Loc,
- arg: *const Expr,
-};
-
-pub const BlockStmt = struct {
- loc: Loc,
- body: []const Stmt,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.writeChar('{');
- if (self.body.len > 0) {
- f.newline();
- f.indent_level += 1;
- for (self.body) |s| {
- f.writeIndent();
- s.fmt(f);
- f.newline();
- }
- f.indent_level -= 1;
- f.writeIndent();
- }
- f.writeChar('}');
- }
-};
-
-pub const ExprStmt = struct {
- loc: Loc,
- expr: *const Expr,
-};
-
-pub const IfStmt = struct {
- loc: Loc,
- condition: *const Expr,
- consequent: *const Stmt,
- alternate: ?*const Stmt,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("if (");
- self.condition.*.fmt(f);
- f.write(") ");
- self.consequent.*.fmt(f);
- if (self.alternate) |alt| {
- f.write(" else ");
- alt.*.fmt(f);
- }
- }
-};
-
-pub const WhileStmt = struct {
- loc: Loc,
- condition: *const Expr,
- body: *const Stmt,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("while (");
- self.condition.*.fmt(f);
- f.write(") ");
- self.body.*.fmt(f);
- }
-};
-
-pub const DoWhileStmt = struct {
- loc: Loc,
- body: *const Stmt,
- condition: *const Expr,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("do ");
- self.body.*.fmt(f);
- f.write(" while (");
- self.condition.*.fmt(f);
- f.write(");");
- }
-};
-
-pub const ForInit = union(enum) {
- expr: *const Expr,
- decl: *const Decl,
-};
-
-pub const ForStmt = struct {
- loc: Loc,
- init: ?ForInit,
- condition: ?*const Expr,
- update: ?*const Expr,
- body: *const Stmt,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("for (");
- if (self.init) |init| {
- switch (init) {
- .expr => |e| e.*.fmt(f),
- .decl => |d| d.*.fmt(f),
- }
- }
- f.writeChar(';');
- if (self.condition) |c| {
- f.writeChar(' ');
- c.*.fmt(f);
- }
- f.writeChar(';');
- if (self.update) |u| {
- f.writeChar(' ');
- u.*.fmt(f);
- }
- f.write(") ");
- self.body.*.fmt(f);
- }
-};
-
-pub const ForInStmt = struct {
- loc: Loc,
- left: ForInit,
- right: *const Expr,
- body: *const Stmt,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("for (");
- switch (self.left) {
- .expr => |e| e.*.fmt(f),
- .decl => |d| d.*.fmt(f),
- }
- f.write(" in ");
- self.right.*.fmt(f);
- f.write(") ");
- self.body.*.fmt(f);
- }
-};
-
-pub const ForOfStmt = struct {
- loc: Loc,
- await_token: bool,
- left: ForInit,
- right: *const Expr,
- body: *const Stmt,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("for");
- if (self.await_token) f.write(" await");
- f.write(" (");
- switch (self.left) {
- .expr => |e| e.*.fmt(f),
- .decl => |d| d.*.fmt(f),
- }
- f.write(" of ");
- self.right.*.fmt(f);
- f.write(") ");
- self.body.*.fmt(f);
- }
-};
-
-pub const ContinueStmt = struct {
- loc: Loc,
- label: ?[]const u8,
-};
-
-pub const BreakStmt = struct {
- loc: Loc,
- label: ?[]const u8,
-};
-
-pub const ReturnStmt = struct {
- loc: Loc,
- arg: ?*const Expr,
-};
-
-pub const SwitchStmt = struct {
- loc: Loc,
- discriminant: *const Expr,
- cases: []const SwitchCase,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("switch (");
- self.discriminant.*.fmt(f);
- f.write(") {");
- f.newline();
- f.indent_level += 1;
- for (self.cases) |c| {
- f.writeIndent();
- c.fmt(f);
- f.newline();
- }
- f.indent_level -= 1;
- f.writeIndent();
- f.writeChar('}');
- }
-};
-
-pub const SwitchCase = struct {
- loc: Loc,
- condition: ?*const Expr,
- consequent: []const Stmt,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- if (self.condition) |c| {
- f.write("case ");
- c.*.fmt(f);
- } else {
- f.write("default");
- }
- f.writeChar(':');
- for (self.consequent) |s| {
- f.writeChar(' ');
- s.fmt(f);
- }
- }
-};
-
-pub const ThrowStmt = struct {
- loc: Loc,
- arg: *const Expr,
-};
-
-pub const TryStmt = struct {
- loc: Loc,
- block: BlockStmt,
- handler: ?CatchClause,
- finalizer: ?BlockStmt,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("try ");
- self.block.fmt(f);
- if (self.handler) |h| {
- f.writeChar(' ');
- h.fmt(f);
- }
- if (self.finalizer) |fin| {
- f.write(" finally ");
- fin.fmt(f);
- }
- }
-};
-
-pub const CatchClause = struct {
- loc: Loc,
- param: ?Pat,
- body: BlockStmt,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("catch");
- if (self.param) |p| {
- f.write(" (");
- p.fmt(f);
- f.write(") ");
- } else {
- f.writeChar(' ');
- }
- self.body.fmt(f);
- }
-};
-
-pub const LabelledStmt = struct {
- loc: Loc,
- label: []const u8,
- body: *const Stmt,
-};
-
-pub const WithStmt = struct {
- loc: Loc,
- obj: *const Expr,
- body: *const Stmt,
-};
-
-pub const VarStmt = struct {
- loc: Loc,
- kind: VarKind,
- decls: []const VarDeclarator,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write(@tagName(self.kind));
- f.writeChar(' ');
- for (self.decls, 0..) |d, i| {
- if (i > 0) f.write(", ");
- d.fmt(f);
- }
- f.writeChar(';');
- }
-};
-
-pub const UsingStmt = struct {
- loc: Loc,
- decls: []const VarDeclarator,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("using ");
- for (self.decls, 0..) |d, i| {
- if (i > 0) f.write(", ");
- d.fmt(f);
- }
- }
-};
-
-pub const VarKind = enum {
- @"var",
- let,
- @"const",
-};
-
-pub const VarDecl = struct {
- loc: Loc,
- kind: VarKind,
- decls: []const VarDeclarator,
-};
-
-pub const LexicalDecl = struct {
- loc: Loc,
- kind: VarKind,
- decls: []const VarDeclarator,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write(@tagName(self.kind));
- f.writeChar(' ');
- for (self.decls, 0..) |d, i| {
- if (i > 0) f.write(", ");
- d.fmt(f);
- }
- f.writeChar(';');
- }
-};
-
-pub const UsingDecl = struct {
- loc: Loc,
- is_await: bool,
- decls: []const VarDeclarator,
-};
-
-pub const VarDeclarator = struct {
- loc: Loc,
- id: Pat,
- init: ?*const Expr,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- self.id.fmt(f);
- if (self.init) |init| {
- f.write(" = ");
- init.*.fmt(f);
- }
- }
-};
-
-pub const ObjectPat = struct {
- loc: Loc,
- props: []const PatProp,
- rest: ?*const Pat,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.writeChar('{');
- var first = true;
- for (self.props) |prop| {
- if (!first) f.write(", ");
- first = false;
- switch (prop) {
- .key_value => |kv| {
- kv.key.fmt(f);
- f.write(": ");
- kv.value.*.fmt(f);
- },
- .shorthand => |name| f.write(name),
- .rest => |r| {
- f.write("...");
- r.*.fmt(f);
- },
- }
- }
- if (self.rest) |rest| {
- if (!first) f.write(", ");
- f.write("...");
- rest.*.fmt(f);
- }
- f.writeChar('}');
- }
-};
-
-pub const PatProp = union(enum) {
- key_value: struct {
- key: PropKey,
- value: *const Pat,
- },
- shorthand: []const u8,
- rest: *const Pat,
-};
-
-pub const ArrayPat = struct {
- loc: Loc,
- elems: []const ?Pat,
- rest: ?*const Pat,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.writeChar('[');
- for (self.elems, 0..) |elem, i| {
- if (i > 0) f.write(", ");
- if (elem) |pat| pat.fmt(f);
- }
- if (self.rest) |rest| {
- if (self.elems.len > 0) f.write(", ");
- f.write("...");
- rest.*.fmt(f);
- }
- f.writeChar(']');
- }
-};
-
-pub const AssignPat = struct {
- loc: Loc,
- left: *const Pat,
- right: *const Expr,
-};
-
-pub const RestPat = struct {
- loc: Loc,
- arg: *const Pat,
-};
-
-pub const FnType = union(enum) {
- normal: void,
- generator: void,
- async: void,
- async_generator: void,
-};
-
-pub const FnDecl = struct {
- loc: Loc,
- fn_type: FnType,
- id: ?[]const u8,
- params: []const Pat,
- body: BlockStmt,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("function");
- if (self.fn_type == .generator or self.fn_type == .async_generator) {
- f.write("*");
- }
- if (self.fn_type == .async or self.fn_type == .async_generator) {
- f.write(" async");
- }
- if (self.id) |id| {
- f.writeChar(' ');
- f.write(id);
- }
- f.writeChar('(');
- for (self.params, 0..) |p, i| {
- if (i > 0) f.write(", ");
- p.fmt(f);
- }
- f.write(") ");
- self.body.fmt(f);
- }
-};
-
-pub const FnExpr = struct {
- loc: Loc,
- fn_type: FnType,
- id: ?[]const u8,
- params: []const Pat,
- body: BlockStmt,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("function");
- if (self.fn_type == .generator or self.fn_type == .async_generator) {
- f.write("*");
- }
- if (self.fn_type == .async or self.fn_type == .async_generator) {
- f.write(" async");
- }
- if (self.id) |id| {
- f.writeChar(' ');
- f.write(id);
- }
- f.writeChar('(');
- for (self.params, 0..) |p, i| {
- if (i > 0) f.write(", ");
- p.fmt(f);
- }
- f.write(") ");
- self.body.fmt(f);
- }
-};
-
-pub const ArrowFn = struct {
- loc: Loc,
- async_token: bool,
- params: []const Pat,
- body: union(enum) {
- block: BlockStmt,
- expr: *const Expr,
- },
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- if (self.async_token) f.write("async ");
- f.writeChar('(');
- for (self.params, 0..) |p, i| {
- if (i > 0) f.write(", ");
- p.fmt(f);
- }
- f.write(") => ");
- switch (self.body) {
- .block => |b| b.fmt(f),
- .expr => |e| e.*.fmt(f),
- }
- }
-};
-
-pub const ClassDecl = struct {
- loc: Loc,
- id: ?[]const u8,
- super_class: ?*const Expr,
- body: []const ClassElem,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("class");
- if (self.id) |id| {
- f.writeChar(' ');
- f.write(id);
- }
- if (self.super_class) |sc| {
- f.write(" extends ");
- sc.*.fmt(f);
- }
- f.write(" {");
- f.newline();
- f.indent_level += 1;
- for (self.body) |elem| {
- f.writeIndent();
- elem.fmt(f);
- f.newline();
- }
- f.indent_level -= 1;
- f.writeIndent();
- f.writeChar('}');
- }
-};
-
-pub const ClassExpr = struct {
- loc: Loc,
- id: ?[]const u8,
- super_class: ?*const Expr,
- body: []const ClassElem,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("class");
- if (self.id) |id| {
- f.writeChar(' ');
- f.write(id);
- }
- if (self.super_class) |sc| {
- f.write(" extends ");
- sc.*.fmt(f);
- }
- f.write(" {");
- f.newline();
- f.indent_level += 1;
- for (self.body) |elem| {
- f.writeIndent();
- elem.fmt(f);
- f.newline();
- }
- f.indent_level -= 1;
- f.writeIndent();
- f.writeChar('}');
- }
-};
-
-pub const ClassElem = union(enum) {
- method: FnExpr,
- get: PropGet,
- set: PropSet,
- field: FieldDef,
- static_block: BlockStmt,
- static_method: FnExpr,
- static_get: PropGet,
- static_set: PropSet,
- static_field: FieldDef,
-};
-
-pub const FieldDef = struct {
- loc: Loc,
- key: PropKey,
- value: ?*const Expr,
-};
-
-pub const ImportDecl = struct {
- loc: Loc,
- specifiers: []const ImportSpecifier,
- source: []const u8,
- attributes: []const ImportAttribute,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("import ");
- if (self.specifiers.len > 0) {
- for (self.specifiers, 0..) |spec, j| {
- if (j > 0) f.write(", ");
- spec.fmt(f);
- }
- f.write(" from ");
- }
- f.writeChar('"');
- f.write(self.source);
- f.writeChar('"');
- for (self.attributes) |a| {
- f.write(" /* ");
- a.fmt(f);
- f.write(" */");
- }
- f.writeChar(';');
- }
-};
-
-pub const ImportSpecifier = union(enum) {
- default: []const u8,
- namespace: []const u8,
- named: struct { imported: []const u8, local: []const u8 },
-};
-
-pub const ImportAttribute = struct {
- key: []const u8,
- value: []const u8,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write(self.key);
- f.write(": \"");
- f.write(self.value);
- f.writeChar('"');
- }
-};
-
-pub const ExportDecl = struct {
- loc: Loc,
- declaration: ?*const Decl,
- specifiers: ?[]const ExportSpecifier,
- source: ?[]const u8,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("export ");
- if (self.declaration) |d| {
- d.*.fmt(f);
- } else if (self.specifiers) |s| {
- f.writeChar('{');
- for (s, 0..) |sp, i| {
- if (i > 0) f.write(", ");
- sp.fmt(f);
- }
- f.writeChar('}');
- if (self.source) |src| {
- f.write(" from \"");
- f.write(src);
- f.writeChar('"');
- }
- f.writeChar(';');
- } else if (self.source) |src| {
- f.write("* from \"");
- f.write(src);
- f.writeChar("\";");
- }
- }
-};
-
-pub const ExportSpecifier = struct {
- loc: Loc,
- exported: []const u8,
- local: []const u8,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write(self.local);
- if (!std.mem.eql(u8, self.local, self.exported)) {
- f.write(" as ");
- f.write(self.exported);
- }
- }
-};
-
-pub const ExportDefault = struct {
- loc: Loc,
- declaration: *const Decl,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.write("export default ");
- self.declaration.*.fmt(f);
- }
-};
-
-pub const ArrayExpr = struct {
- loc: Loc,
- elems: []const ?Expr,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.writeChar('[');
- for (self.elems, 0..) |elem, i| {
- if (i > 0) f.write(", ");
- if (elem) |e| e.fmt(f);
- }
- f.writeChar(']');
- }
-};
-
-pub const ObjectExpr = struct {
- loc: Loc,
- props: []const Prop,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.writeChar('{');
- for (self.props, 0..) |p, i| {
- if (i > 0) f.write(", ");
- p.fmt(f);
- }
- f.writeChar('}');
- }
-};
-
-pub const MemberExpr = struct {
- loc: Loc,
- obj: *const Expr,
- prop: union(enum) {
- ident: []const u8,
- private: []const u8,
- },
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- self.obj.*.fmt(f);
- switch (self.prop) {
- .ident => |name| {
- f.writeChar('.');
- f.write(name);
- },
- .private => |name| {
- f.writeChar('.');
- f.write(name);
- },
- }
- }
-};
-
-pub const ComputedMember = struct {
- loc: Loc,
- obj: *const Expr,
- expr: *const Expr,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- self.obj.*.fmt(f);
- f.writeChar('[');
- self.expr.*.fmt(f);
- f.writeChar(']');
- }
-};
-
-pub const CallExpr = struct {
- loc: Loc,
- callee: *const Expr,
- args: []const Expr,
- optional: bool,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- self.callee.*.fmt(f);
- if (self.optional) {
- f.write("?.(");
- } else {
- f.writeChar('(');
- }
- for (self.args, 0..) |a, i| {
- if (i > 0) f.write(", ");
- a.fmt(f);
- }
- f.writeChar(')');
- }
-};
-
-pub const NewExpr = struct {
- loc: Loc,
- callee: *const Expr,
- args: []const Expr,
-};
-
-pub const ChainExpr = struct {
- loc: Loc,
- expr: *const Expr,
-};
-
-pub const UnaryOp = enum {
- @"-",
- @"+",
- @"!",
- @"~",
- typeof,
- void,
- delete,
-};
-
-pub const UnaryExpr = struct {
- loc: Loc,
- op: UnaryOp,
- arg: *const Expr,
- prefix: bool,
-};
-
-pub const BinaryOp = enum {
- @"==",
- @"!=",
- @"===",
- @"!==",
- @"<",
- @"<=",
- @">",
- @">=",
- @"<<",
- @">>",
- @">>>",
- @"+",
- @"-",
- @"*",
- @"/",
- @"%",
- @"**",
- @"|",
- @"^",
- @"&",
- in,
- instanceof,
- @"||",
- @"&&",
- @"??",
-};
-
-pub const BinaryExpr = struct {
- loc: Loc,
- op: BinaryOp,
- left: *const Expr,
- right: *const Expr,
-};
-
-pub const UpdateOp = enum {
- @"++",
- @"--",
-};
-
-pub const UpdateExpr = struct {
- loc: Loc,
- op: UpdateOp,
- arg: *const Expr,
- prefix: bool,
-};
-
-pub const IfExpr = struct {
- loc: Loc,
- condition: *const Expr,
- consequent: *const Expr,
- alternate: *const Expr,
-};
-
-pub const AssignOp = enum {
- @"=",
- @"+=",
- @"-=",
- @"*=",
- @"/=",
- @"%=",
- @"**=",
- @"<<=",
- @">>=",
- @">>>=",
- @"|=",
- @"^=",
- @"&=",
- @"||=",
- @"&&=",
- @"??=",
-};
-
-pub const AssignExpr = struct {
- loc: Loc,
- op: AssignOp,
- left: *const Expr,
- right: *const Expr,
-};
-
-pub const SeqExpr = struct {
- loc: Loc,
- exprs: []const Expr,
-};
-
-pub const YieldExpr = struct {
- loc: Loc,
- arg: ?*const Expr,
- delegate: bool,
-};
-
-pub const AwaitExpr = struct {
- loc: Loc,
- arg: *const Expr,
-};
-
-pub const MetaProp = struct {
- loc: Loc,
- meta: []const u8,
- prop: []const u8,
-};
-
-pub const TemplateLit = struct {
- loc: Loc,
- quasis: []const TemplateElem,
- exprs: []const Expr,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.writeChar('`');
- var ei: usize = 0;
- for (self.quasis, 0..) |q, i| {
- if (i > 0 and ei < self.exprs.len) {
- f.write("${");
- self.exprs[ei].fmt(f);
- f.writeChar('}');
- ei += 1;
- }
- f.write(q.value);
- }
- f.writeChar('`');
- }
-};
-
-pub const TemplateElem = struct {
- loc: Loc,
- value: []const u8,
- tail: bool,
-};
-
-pub const TaggedTemplate = struct {
- loc: Loc,
- tag: *const Expr,
- quasi: TemplateLit,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- self.tag.*.fmt(f);
- self.quasi.fmt(f);
- }
-};
-
-pub const Decorator = struct {
- loc: Loc,
- expr: *const Expr,
-
- pub fn fmt(self: @This(), f: *Formatter) void {
- f.writeChar('@');
- self.expr.*.fmt(f);
- }
-};
diff --git a/src/z/parser/js/ast/core.zig b/src/z/parser/js/ast/core.zig
new file mode 100644
index 0000000..a278912
--- /dev/null
+++ b/src/z/parser/js/ast/core.zig
@@ -0,0 +1,162 @@
+const std = @import("std");
+const lex = @import("../lexical_grammar.zig");
+const Token = lex.Token;
+
+pub const Loc = @import("loc.zig").Loc;
+pub const FormatOptions = @import("../formatter.zig").FormatOptions;
+pub const Formatter = @import("../formatter.zig").Formatter;
+pub const Parser = @import("../parser.zig").Parser;
+pub const ParseError = @import("../parser.zig").ParseError;
+pub const NodeArena = @import("../parser.zig").NodeArena;
+
+pub const stmt = @import("stmt.zig");
+pub const expr = @import("expr.zig");
+pub const pat = @import("pat.zig");
+pub const decl = @import("decl.zig");
+pub const lit = @import("lit.zig");
+
+pub const StmtType = stmt.StmtType;
+pub const Stmt = stmt.Stmt;
+pub const BlockStmt = stmt.BlockStmt;
+pub const ExprStmt = stmt.ExprStmt;
+pub const IfStmt = stmt.IfStmt;
+pub const WhileStmt = stmt.WhileStmt;
+pub const DoWhileStmt = stmt.DoWhileStmt;
+pub const ForStmt = stmt.ForStmt;
+pub const ForInStmt = stmt.ForInStmt;
+pub const ForOfStmt = stmt.ForOfStmt;
+pub const ContinueStmt = stmt.ContinueStmt;
+pub const BreakStmt = stmt.BreakStmt;
+pub const ReturnStmt = stmt.ReturnStmt;
+pub const SwitchStmt = stmt.SwitchStmt;
+pub const SwitchCase = stmt.SwitchCase;
+pub const ThrowStmt = stmt.ThrowStmt;
+pub const TryStmt = stmt.TryStmt;
+pub const CatchClause = stmt.CatchClause;
+pub const LabelledStmt = stmt.LabelledStmt;
+pub const WithStmt = stmt.WithStmt;
+pub const VarStmt = stmt.VarStmt;
+pub const UsingStmt = stmt.UsingStmt;
+pub const VarKind = stmt.VarKind;
+pub const VarDecl = stmt.VarDecl;
+pub const LexicalDecl = stmt.LexicalDecl;
+pub const UsingDecl = stmt.UsingDecl;
+pub const VarDeclarator = stmt.VarDeclarator;
+pub const ForInit = stmt.ForInit;
+
+pub const ExprType = expr.ExprType;
+pub const Expr = expr.Expr;
+pub const FnType = expr.FnType;
+pub const FnExpr = expr.FnExpr;
+pub const ArrowFn = expr.ArrowFn;
+pub const ClassExpr = expr.ClassExpr;
+pub const ClassElem = expr.ClassElem;
+pub const FieldDef = expr.FieldDef;
+pub const ArrayExpr = expr.ArrayExpr;
+pub const ObjectExpr = expr.ObjectExpr;
+pub const PropType = expr.PropType;
+pub const Prop = expr.Prop;
+pub const PropKey = expr.PropKey;
+pub const PropInit = expr.PropInit;
+pub const PropGet = expr.PropGet;
+pub const PropSet = expr.PropSet;
+pub const SpreadElem = expr.SpreadElem;
+pub const MemberExpr = expr.MemberExpr;
+pub const ComputedMember = expr.ComputedMember;
+pub const CallExpr = expr.CallExpr;
+pub const NewExpr = expr.NewExpr;
+pub const ChainExpr = expr.ChainExpr;
+pub const UnaryOp = expr.UnaryOp;
+pub const UnaryExpr = expr.UnaryExpr;
+pub const BinaryOp = expr.BinaryOp;
+pub const BinaryExpr = expr.BinaryExpr;
+pub const UpdateOp = expr.UpdateOp;
+pub const UpdateExpr = expr.UpdateExpr;
+pub const IfExpr = expr.IfExpr;
+pub const AssignOp = expr.AssignOp;
+pub const AssignExpr = expr.AssignExpr;
+pub const SeqExpr = expr.SeqExpr;
+pub const YieldExpr = expr.YieldExpr;
+pub const AwaitExpr = expr.AwaitExpr;
+pub const MetaProp = expr.MetaProp;
+pub const TemplateLit = expr.TemplateLit;
+pub const TemplateElem = expr.TemplateElem;
+pub const TaggedTemplate = expr.TaggedTemplate;
+pub const Decorator = expr.Decorator;
+
+pub const PatType = pat.PatType;
+pub const Pat = pat.Pat;
+pub const ObjectPat = pat.ObjectPat;
+pub const PatProp = pat.PatProp;
+pub const ArrayPat = pat.ArrayPat;
+pub const AssignPat = pat.AssignPat;
+pub const RestPat = pat.RestPat;
+
+pub const DeclType = decl.DeclType;
+pub const Decl = decl.Decl;
+pub const FnDecl = decl.FnDecl;
+pub const ClassDecl = decl.ClassDecl;
+pub const ImportDecl = decl.ImportDecl;
+pub const ImportSpecifier = decl.ImportSpecifier;
+pub const ImportAttribute = decl.ImportAttribute;
+pub const ExportDecl = decl.ExportDecl;
+pub const ExportSpecifier = decl.ExportSpecifier;
+pub const ExportDefault = decl.ExportDefault;
+
+pub const LitType = lit.LitType;
+pub const Lit = lit.Lit;
+pub const Regex = lit.Regex;
+
+pub const Node = union(enum) {
+ program: Program,
+
+ stmt: Stmt,
+ expr: Expr,
+ pat: Pat,
+
+ decl: Decl,
+ literal: Lit,
+ property: Prop,
+ template: TemplateLit,
+ spread: SpreadElem,
+ decorator: Decorator,
+
+ import_specifier: ImportSpecifier,
+ export_specifier: ExportSpecifier,
+ import_attr: ImportAttribute,
+ catch_clause: CatchClause,
+ switch_case: SwitchCase,
+};
+
+pub const Program = struct {
+ body: []const Stmt,
+ loc: Loc,
+
+ pub fn parse(comptime source: []const u8) ParseError!Program {
+ return comptime blk: {
+ var token_buf: [4096]Token = undefined;
+ const tokens = lex.Lexer.tokenizeBuf(source, &token_buf);
+ var arena: NodeArena = .{};
+ var parser = Parser.init(tokens, source, &arena);
+ 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 };
+ };
+ }
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ @setEvalBranchQuota(100000);
+ for (self.body, 0..) |s, i| {
+ if (i > 0) f.newline();
+ s.fmt(f);
+ }
+ }
+};
diff --git a/src/z/parser/js/ast/decl.zig b/src/z/parser/js/ast/decl.zig
new file mode 100644
index 0000000..a9a6117
--- /dev/null
+++ b/src/z/parser/js/ast/decl.zig
@@ -0,0 +1,432 @@
+const std = @import("std");
+const Loc = @import("loc.zig").Loc;
+const Expr = @import("expr.zig").Expr;
+const Pat = @import("pat.zig").Pat;
+const Stmt = @import("stmt.zig").Stmt;
+const BlockStmt = @import("stmt.zig").BlockStmt;
+const VarStmt = @import("stmt.zig").VarStmt;
+const VarDeclarator = @import("stmt.zig").VarDeclarator;
+const VarKind = @import("stmt.zig").VarKind;
+const FnType = @import("expr.zig").FnType;
+const ClassElem = @import("expr.zig").ClassElem;
+const Parser = @import("../parser.zig").Parser;
+const Formatter = @import("../formatter.zig").Formatter;
+const ParseError = @import("../parser.zig").ParseError;
+
+pub const DeclType = union(enum) {
+ @"fn": FnDecl,
+ class: ClassDecl,
+ @"var": VarDecl,
+ using: UsingDecl,
+ import: ImportDecl,
+ @"export": ExportDecl,
+ export_default: ExportDefault,
+};
+
+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.writeSpace();
+ 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 FnDecl = struct {
+ loc: Loc,
+ fn_type: FnType,
+ id: ?[]const u8,
+ params: []const Pat,
+ body: BlockStmt,
+
+ pub fn parse(p: *Parser, fn_type_arg: FnType, mode: enum { stmt, expr }) ParseError!?Stmt {
+ const start = p.tok().loc.start;
+ var fn_type = fn_type_arg;
+
+ if (fn_type == .async) {
+ _ = p.advanceTok();
+ } else if (fn_type == .async_generator) {
+ _ = p.advanceTok();
+ _ = p.advanceTok();
+ }
+
+ _ = p.advanceTok();
+
+ if (fn_type == .normal and p.peek() == .@"*") {
+ p.advance();
+ fn_type = .generator;
+ }
+
+ var id: ?[]const u8 = null;
+ if (p.peek().isIdentifier()) {
+ const id_tok = p.advanceTok();
+ id = p.tokenSlice(id_tok);
+ }
+
+ const params = try p.parseParams();
+ const body = try p.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;
+ }
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ f.write("function");
+ if (self.fn_type == .generator or self.fn_type == .async_generator) {
+ f.write("*");
+ }
+ if (self.fn_type == .async or self.fn_type == .async_generator) {
+ f.write(" async");
+ }
+ if (self.id) |id| {
+ f.writeSpace();
+ f.write(id);
+ }
+ f.writeChar('(');
+ for (self.params, 0..) |p, i| {
+ if (i > 0) f.write(", ");
+ p.fmt(f);
+ }
+ f.write(") ");
+ self.body.fmt(f);
+ }
+};
+
+pub const ClassDecl = struct {
+ loc: Loc,
+ id: ?[]const u8,
+ super_class: ?*const Expr,
+ body: []const ClassElem,
+
+ pub fn parse(p: *Parser, mode: enum { stmt, expr }) ParseError!?Stmt {
+ const start = p.advanceTok().loc.start;
+
+ var id: ?[]const u8 = null;
+ if (p.peek() == .identifier) {
+ const id_tok = p.advanceTok();
+ id = p.tokenSlice(id_tok);
+ }
+
+ var super_class: ?*const Expr = null;
+ if (p.matchKeyword(.extends)) {
+ super_class = try Expr.parse(p, 0);
+ }
+
+ try p.expect(.lbrace);
+ var elems: [128]ClassElem = undefined;
+ var elem_count: usize = 0;
+
+ while (p.peek() != .rbrace and p.peek() != .eof) {
+ const elem = try ClassElem.parse(p);
+ elems[elem_count] = elem;
+ elem_count += 1;
+ if (elem_count >= elems.len) return ParseError.TooManyTokens;
+ }
+
+ const end_tok = try p.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;
+ }
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ f.write("class");
+ if (self.id) |id| {
+ f.writeSpace();
+ f.write(id);
+ }
+ if (self.super_class) |sc| {
+ f.write(" extends ");
+ sc.*.fmt(f);
+ }
+ f.write(" {");
+ f.newline();
+ f.indent_level += 1;
+ for (self.body) |elem| {
+ f.writeIndent();
+ elem.fmt(f);
+ f.newline();
+ }
+ f.indent_level -= 1;
+ f.writeIndent();
+ f.writeChar('}');
+ }
+};
+
+pub const VarDecl = struct {
+ loc: Loc,
+ kind: VarKind,
+ decls: []const VarDeclarator,
+};
+
+pub const UsingDecl = struct {
+ loc: Loc,
+ is_await: bool,
+ decls: []const VarDeclarator,
+};
+
+pub const ImportDecl = struct {
+ loc: Loc,
+ specifiers: []const ImportSpecifier,
+ source: []const u8,
+ attributes: []const ImportAttribute,
+
+ pub fn parse(p: *Parser) ParseError!ImportDecl {
+ const start = p.advanceTok().loc.start;
+ var specifiers: [32]ImportSpecifier = undefined;
+ var spec_count: usize = 0;
+
+ if (p.peek() != .string and p.peek() != .lparen) {
+ if (p.peek() == .identifier or p.peek().isKeyword()) {
+ const local_tok = p.advanceTok();
+ const local = p.tokenSlice(local_tok);
+ specifiers[spec_count] = .{ .default = local };
+ spec_count += 1;
+ if (p.match(.comma)) {}
+ }
+
+ if (p.match(.@"*")) {
+ try p.expect(.identifier);
+ const ns_tok = try p.expectTok(.identifier);
+ const ns = p.tokenSlice(ns_tok);
+ specifiers[spec_count] = .{ .namespace = ns };
+ spec_count += 1;
+ } else if (p.peek() == .lbrace) {
+ p.advance();
+ if (p.peek() != .rbrace) {
+ while (true) {
+ const imported_tok = try p.expectTok(.identifier);
+ const imported = p.tokenSlice(imported_tok);
+ var local = imported;
+ if (p.match(.identifier)) {
+ local = p.tokenSlice(p.tokens[p.pos - 1]);
+ }
+ specifiers[spec_count] = .{ .named = .{ .imported = imported, .local = local } };
+ spec_count += 1;
+ if (!p.match(.comma)) break;
+ }
+ }
+ try p.expect(.rbrace);
+ }
+ }
+
+ var source: []const u8 = "";
+ var attrs: [8]ImportAttribute = undefined;
+ var attr_count: usize = 0;
+
+ if (p.matchKeyword(.from)) {
+ const str_tok = try p.expectTok(.string);
+ source = p.tokenSlice(str_tok);
+ } else if (p.peek() == .string) {
+ const str_tok = p.advanceTok();
+ source = p.tokenSlice(str_tok);
+ }
+
+ if (p.matchKeyword(.with)) {
+ try p.expect(.lbrace);
+ while (p.peek() != .rbrace and p.peek() != .eof) {
+ const key_tok = p.advanceTok();
+ const key = p.tokenSlice(key_tok);
+ try p.expect(.@":");
+ const val_tok = try p.expectTok(.string);
+ const val = p.tokenSlice(val_tok);
+ attrs[attr_count] = .{ .key = key, .value = val };
+ attr_count += 1;
+ _ = p.match(.comma);
+ }
+ try p.expect(.rbrace);
+ }
+
+ try p.expect(.semicolon);
+ return .{ .loc = .{ .start = start, .end = p.tok().loc.start }, .specifiers = specifiers[0..spec_count], .source = source, .attributes = attrs[0..attr_count] };
+ }
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ f.write("import ");
+ if (self.specifiers.len > 0) {
+ for (self.specifiers, 0..) |spec, j| {
+ if (j > 0) f.write(", ");
+ spec.fmt(f);
+ }
+ f.write(" from ");
+ }
+ f.writeChar('"');
+ f.write(self.source);
+ f.writeChar('"');
+ for (self.attributes) |a| {
+ f.write(" /* ");
+ a.fmt(f);
+ f.write(" */");
+ }
+ f.writeChar(';');
+ }
+};
+
+pub const ImportSpecifier = union(enum) {
+ default: []const u8,
+ namespace: []const u8,
+ named: struct { imported: []const u8, local: []const u8 },
+};
+
+pub const ImportAttribute = struct {
+ key: []const u8,
+ value: []const u8,
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ f.write(self.key);
+ f.write(": \"");
+ f.write(self.value);
+ f.writeChar('"');
+ }
+};
+
+pub const ExportDecl = struct {
+ loc: Loc,
+ declaration: ?*const Decl,
+ specifiers: ?[]const ExportSpecifier,
+ source: ?[]const u8,
+
+ pub fn parse(p: *Parser) ParseError!ExportDecl {
+ const start = p.advanceTok().loc.start;
+
+ 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 {
+ f.write("export ");
+ if (self.declaration) |d| {
+ d.*.fmt(f);
+ } else if (self.specifiers) |s| {
+ f.writeChar('{');
+ for (s, 0..) |sp, i| {
+ if (i > 0) f.write(", ");
+ sp.fmt(f);
+ }
+ f.writeChar('}');
+ if (self.source) |src| {
+ f.write(" from \"");
+ f.write(src);
+ f.writeChar('"');
+ }
+ f.writeChar(';');
+ } else if (self.source) |src| {
+ f.write("* from \"");
+ f.write(src);
+ f.writeChar("\";");
+ }
+ }
+};
+
+pub const ExportSpecifier = struct {
+ loc: Loc,
+ exported: []const u8,
+ local: []const u8,
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ f.write(self.local);
+ if (!std.mem.eql(u8, self.local, self.exported)) {
+ f.write(" as ");
+ f.write(self.exported);
+ }
+ }
+};
+
+pub const ExportDefault = struct {
+ loc: Loc,
+ declaration: *const Decl,
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ f.write("export default ");
+ self.declaration.*.fmt(f);
+ }
+};
diff --git a/src/z/parser/js/ast/expr.zig b/src/z/parser/js/ast/expr.zig
new file mode 100644
index 0000000..2443b80
--- /dev/null
+++ b/src/z/parser/js/ast/expr.zig
@@ -0,0 +1,1245 @@
+const std = @import("std");
+const Loc = @import("loc.zig").Loc;
+const BlockStmt = @import("stmt.zig").BlockStmt;
+const Pat = @import("pat.zig").Pat;
+const Regex = @import("lit.zig").Regex;
+const Parser = @import("../parser.zig").Parser;
+const Formatter = @import("../formatter.zig").Formatter;
+const ParseError = @import("../parser.zig").ParseError;
+const BP = @import("../parser.zig").BP;
+
+pub const ExprType = union(enum) {
+ identifier: []const u8,
+ this: void,
+ super: void,
+ null: void,
+ bool: bool,
+ number: f64,
+ string: []const u8,
+ regex: Regex,
+ array: ArrayExpr,
+ object: ObjectExpr,
+ func: FnExpr,
+ arrow: ArrowFn,
+ class: ClassExpr,
+ template: TemplateLit,
+ tagged_template: TaggedTemplate,
+ member: MemberExpr,
+ computed_member: ComputedMember,
+ call: CallExpr,
+ new: NewExpr,
+ chain: ChainExpr,
+ unary: UnaryExpr,
+ binary: BinaryExpr,
+ update: UpdateExpr,
+ conditional: IfExpr,
+ assign: AssignExpr,
+ sequence: SeqExpr,
+ spread: SpreadElem,
+ yield: YieldExpr,
+ await: AwaitExpr,
+ meta_prop: MetaProp,
+ parenthesized: *const Expr,
+ private_ident: []const u8,
+};
+
+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),
+ .this => f.write("this"),
+ .super => f.write("super"),
+ .null => f.write("null"),
+ .bool => |b| f.write(if (b) "true" else "false"),
+ .number => |n| {
+ var buf: [64]u8 = undefined;
+ f.write(std.fmt.bufPrint(&buf, "{d}", .{n}) catch "0");
+ },
+ .string => |s| {
+ f.writeChar('"');
+ f.write(s);
+ f.writeChar('"');
+ },
+ .regex => |r| {
+ f.writeChar('/');
+ f.write(r.pattern);
+ f.writeChar('/');
+ f.write(r.flags);
+ },
+ .array => |a| a.fmt(f),
+ .object => |o| o.fmt(f),
+ .func => |fn_expr| fn_expr.fmt(f),
+ .arrow => |arrow| arrow.fmt(f),
+ .class => |c| c.fmt(f),
+ .template => |t| t.fmt(f),
+ .tagged_template => |tt| tt.fmt(f),
+ .member => |m| m.fmt(f),
+ .computed_member => |cm| cm.fmt(f),
+ .call => |call| call.fmt(f),
+ .new => |n| {
+ f.write("new ");
+ n.callee.*.fmt(f);
+ f.writeChar('(');
+ for (n.args, 0..) |a, i| {
+ if (i > 0) f.write(", ");
+ a.fmt(f);
+ }
+ f.writeChar(')');
+ },
+ .chain => |c| c.expr.*.fmt(f),
+ .unary => |u| {
+ if (u.prefix) {
+ f.write(@tagName(u.op));
+ u.arg.*.fmt(f);
+ } else {
+ u.arg.*.fmt(f);
+ f.write(@tagName(u.op));
+ }
+ },
+ .binary => |b| {
+ f.writeChar('(');
+ b.left.*.fmt(f);
+ f.writeSpace();
+ f.write(@tagName(b.op));
+ f.writeSpace();
+ b.right.*.fmt(f);
+ f.writeChar(')');
+ },
+ .update => |u| {
+ const s = if (u.op == .@"++") "++" else "--";
+ if (u.prefix) {
+ f.write(s);
+ u.arg.*.fmt(f);
+ } else {
+ u.arg.*.fmt(f);
+ f.write(s);
+ }
+ },
+ .conditional => |c| {
+ c.condition.*.fmt(f);
+ f.write(" ? ");
+ c.consequent.*.fmt(f);
+ f.write(" : ");
+ c.alternate.*.fmt(f);
+ },
+ .assign => |a| {
+ a.left.*.fmt(f);
+ f.writeSpace();
+ f.write(@tagName(a.op));
+ f.writeSpace();
+ a.right.*.fmt(f);
+ },
+ .sequence => |s| {
+ for (s.exprs, 0..) |e, i| {
+ if (i > 0) f.write(", ");
+ e.fmt(f);
+ }
+ },
+ .spread => |s| {
+ f.write("...");
+ s.arg.*.fmt(f);
+ },
+ .yield => |y| {
+ f.write("yield");
+ if (y.delegate) f.write("*");
+ if (y.arg) |a| {
+ f.writeSpace();
+ a.*.fmt(f);
+ }
+ },
+ .await => |a| {
+ f.write("await ");
+ a.arg.*.fmt(f);
+ },
+ .meta_prop => |m| {
+ f.write(m.meta);
+ f.writeChar('.');
+ f.write(m.prop);
+ },
+ .parenthesized => |p| {
+ f.writeChar('(');
+ p.*.fmt(f);
+ f.writeChar(')');
+ },
+ .private_ident => |name| f.write(name),
+ }
+ }
+};
+
+pub const FnType = union(enum) {
+ normal: void,
+ generator: void,
+ async: void,
+ async_generator: void,
+};
+
+pub const FnExpr = struct {
+ loc: Loc,
+ fn_type: FnType,
+ id: ?[]const u8,
+ params: []const Pat,
+ body: BlockStmt,
+
+ pub fn parse(p: *Parser) ParseError!*const Expr {
+ const start = p.advanceTok().loc.start;
+
+ var fn_type: FnType = .normal;
+ if (p.peek() == .@"*") {
+ fn_type = .generator;
+ p.advance();
+ }
+
+ var id: ?[]const u8 = null;
+ if (p.peek() == .identifier or p.peek().isKeywordButNotLet()) {
+ const id_tok = p.advanceTok();
+ id = p.tokenSlice(id_tok);
+ }
+
+ const params = try p.parseParams();
+ const body = try p.parseFunctionBody();
+
+ return p.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 },
+ );
+ }
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ f.write("function");
+ if (self.fn_type == .generator or self.fn_type == .async_generator) {
+ f.write("*");
+ }
+ if (self.fn_type == .async or self.fn_type == .async_generator) {
+ f.write(" async");
+ }
+ if (self.id) |id| {
+ f.writeSpace();
+ f.write(id);
+ }
+ f.writeChar('(');
+ for (self.params, 0..) |p, i| {
+ if (i > 0) f.write(", ");
+ p.fmt(f);
+ }
+ f.write(") ");
+ self.body.fmt(f);
+ }
+};
+
+pub const ArrowFn = struct {
+ loc: Loc,
+ async_token: bool,
+ params: []const Pat,
+ body: union(enum) {
+ block: BlockStmt,
+ expr: *const Expr,
+ },
+
+ pub fn parse(p: *Parser, params_info: struct { loc: Loc, params: []const Pat }) ParseError!*const Expr {
+ try p.expect(.@"=>");
+ const async_token = false;
+
+ if (p.peek() == .lbrace) {
+ const body = try p.parseFunctionBody();
+ return p.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 Expr.parse(p, 0);
+ return p.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 },
+ );
+ }
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ if (self.async_token) f.write("async ");
+ f.writeChar('(');
+ for (self.params, 0..) |p, i| {
+ if (i > 0) f.write(", ");
+ p.fmt(f);
+ }
+ f.write(") => ");
+ switch (self.body) {
+ .block => |b| b.fmt(f),
+ .expr => |e| e.*.fmt(f),
+ }
+ }
+};
+
+pub const ClassExpr = struct {
+ loc: Loc,
+ id: ?[]const u8,
+ super_class: ?*const Expr,
+ body: []const ClassElem,
+
+ pub fn parse(p: *Parser) ParseError!*const Expr {
+ const start = p.advanceTok().loc.start;
+
+ var id: ?[]const u8 = null;
+ if (p.peek() == .identifier) {
+ const id_tok = p.advanceTok();
+ id = p.tokenSlice(id_tok);
+ }
+
+ var super_class: ?*const Expr = null;
+ if (p.matchKeyword(.extends)) {
+ super_class = try Expr.parse(p, 0);
+ }
+
+ try p.expect(.lbrace);
+ var elems: [128]ClassElem = undefined;
+ var elem_count: usize = 0;
+
+ while (p.peek() != .rbrace and p.peek() != .eof) {
+ const elem = try ClassElem.parse(p);
+ elems[elem_count] = elem;
+ elem_count += 1;
+ }
+
+ const end_tok = try p.expectTok(.rbrace);
+ return p.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 },
+ );
+ }
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ f.write("class");
+ if (self.id) |id| {
+ f.writeSpace();
+ f.write(id);
+ }
+ if (self.super_class) |sc| {
+ f.write(" extends ");
+ sc.*.fmt(f);
+ }
+ f.write(" {");
+ f.newline();
+ f.indent_level += 1;
+ for (self.body) |elem| {
+ f.writeIndent();
+ elem.fmt(f);
+ f.newline();
+ }
+ f.indent_level -= 1;
+ f.writeIndent();
+ f.writeChar('}');
+ }
+};
+
+pub const ClassElem = union(enum) {
+ method: FnExpr,
+ get: PropGet,
+ set: PropSet,
+ field: FieldDef,
+ static_block: BlockStmt,
+ static_method: FnExpr,
+ static_get: PropGet,
+ static_set: PropSet,
+ static_field: FieldDef,
+
+ pub fn parse(p: *Parser) ParseError!ClassElem {
+ const start = p.tok().loc.start;
+ const is_static = p.matchKeyword(.static);
+
+ if (is_static and p.peek() == .lbrace) {
+ const body = try p.parseFunctionBody();
+ return ClassElem{ .static_block = body };
+ }
+
+ if (p.matchKeyword(.get)) {
+ const key = try p.parsePropKey();
+ try p.expect(.lparen);
+ try p.expect(.rparen);
+ const body = try p.parseFunctionBody();
+ if (is_static) return ClassElem{ .static_get = .{ .key = key, .body = body } };
+ return ClassElem{ .get = .{ .key = key, .body = body } };
+ }
+
+ if (p.matchKeyword(.set)) {
+ const key = try p.parsePropKey();
+ try p.expect(.lparen);
+ const param = try Pat.parse(p);
+ try p.expect(.rparen);
+ const body = try p.parseFunctionBody();
+ if (is_static) return ClassElem{ .static_set = .{ .key = key, .param = param, .body = body } };
+ return ClassElem{ .set = .{ .key = key, .param = param, .body = body } };
+ }
+
+ if (p.match(.@"*")) {
+ _ = try p.parsePropKey();
+ try p.expect(.lparen);
+ const params = try p.parseParams();
+ const body = try p.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 p.parsePropKey();
+
+ if (p.peek() == .lparen) {
+ const params = try p.parseParams();
+ const body = try p.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 (p.match(.@"=")) {
+ value = try Expr.parse(p, 0);
+ }
+ if (p.peek() == .semicolon) p.advance();
+
+ const field = FieldDef{ .loc = .{ .start = start, .end = if (value) |v| v.loc.end else p.tok().loc.start }, .key = key, .value = value };
+ if (is_static) return ClassElem{ .static_field = field };
+ return ClassElem{ .field = field };
+ }
+};
+
+pub const FieldDef = struct {
+ loc: Loc,
+ key: PropKey,
+ value: ?*const Expr,
+};
+
+pub const ArrayExpr = struct {
+ loc: Loc,
+ elems: []const ?Expr,
+
+ pub fn parse(p: *Parser) ParseError!*const Expr {
+ const start = p.advanceTok().loc.start;
+ var elems: [256]?Expr = undefined;
+ var count: usize = 0;
+
+ while (p.peek() != .rbracket and p.peek() != .eof) {
+ if (p.match(.comma)) {
+ elems[count] = null;
+ count += 1;
+ } else if (p.peek() == .@"...") {
+ p.advance();
+ const arg = try Expr.parse(p, 0);
+ elems[count] = Expr{ .loc = arg.loc, .data = .{ .spread = .{ .loc = arg.loc, .arg = arg } } };
+ count += 1;
+ if (!p.match(.comma)) break;
+ } else {
+ const expr = try Expr.parse(p, 0);
+ elems[count] = expr.*;
+ count += 1;
+ if (!p.match(.comma)) break;
+ }
+ }
+
+ try p.expect(.rbracket);
+ return p.arena.exprPtr(.{ .array = .{ .loc = .{ .start = start, .end = p.tok().loc.start }, .elems = elems[0..count] } }, .{ .start = start, .end = p.tok().loc.start });
+ }
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ f.writeChar('[');
+ for (self.elems, 0..) |elem, i| {
+ if (i > 0) f.write(", ");
+ if (elem) |e| e.fmt(f);
+ }
+ f.writeChar(']');
+ }
+};
+
+pub const ObjectExpr = struct {
+ loc: Loc,
+ props: []const Prop,
+
+ pub fn parse(p: *Parser) ParseError!*const Expr {
+ const start = p.advanceTok().loc.start;
+ var props: [128]Prop = undefined;
+ var count: usize = 0;
+
+ while (p.peek() != .rbrace and p.peek() != .eof) {
+ if (p.peek() == .@"...") {
+ p.advance();
+ const arg = try Expr.parse(p, 0);
+ props[count] = .{ .loc = arg.loc, .key = .{ .ident = "" }, .data = .{ .spread = .{ .loc = arg.loc, .arg = arg } } };
+ count += 1;
+ } else if (p.peek() == .identifier) {
+ const name_tok = p.tok();
+ const name = p.tokenSlice(name_tok);
+ p.advance();
+ if (p.peek() == .@":") {
+ p.advance();
+ const value = try Expr.parse(p, 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 (p.peek() == .lparen) {
+ const params = try p.parseParams();
+ const body = try p.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 (p.peek() == .@"=") {
+ p.advance();
+ const value = try Expr.parse(p, 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 (p.peek() == .comma or p.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 (p.peek() == .number or p.peek() == .string) {
+ const key = try p.parsePropKey();
+ try p.expect(.@":");
+ const value = try Expr.parse(p, 0);
+ props[count] = .{ .loc = .{ .start = start, .end = value.loc.end }, .key = key, .data = .{ .init = .{ .key = key, .value = value } } };
+ count += 1;
+ } else if (p.peek() == .lbracket) {
+ const key = try p.parsePropKey();
+ try p.expect(.@":");
+ const value = try Expr.parse(p, 0);
+ props[count] = .{ .loc = .{ .start = start, .end = value.loc.end }, .key = key, .data = .{ .init = .{ .key = key, .value = value } } };
+ count += 1;
+ } else if (p.matchKeyword(.get)) {
+ const key = try p.parsePropKey();
+ try p.expect(.lparen);
+ try p.expect(.rparen);
+ const body = try p.parseFunctionBody();
+ props[count] = .{ .loc = .{ .start = start, .end = body.loc.end }, .key = key, .data = .{ .get = .{ .key = key, .body = body } } };
+ count += 1;
+ } else if (p.matchKeyword(.set)) {
+ const key = try p.parsePropKey();
+ try p.expect(.lparen);
+ const param = try Pat.parse(p);
+ try p.expect(.rparen);
+ const body = try p.parseFunctionBody();
+ props[count] = .{ .loc = .{ .start = start, .end = body.loc.end }, .key = key, .data = .{ .set = .{ .key = key, .param = param, .body = body } } };
+ count += 1;
+ } else if (p.match(.@"*")) {
+ const key = try p.parsePropKey();
+ const params = try p.parseParams();
+ const body = try p.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 (!p.match(.comma)) break;
+ }
+
+ try p.expect(.rbrace);
+ return p.arena.exprPtr(.{ .object = .{ .loc = .{ .start = start, .end = p.tok().loc.start }, .props = props[0..count] } }, .{ .start = start, .end = p.tok().loc.start });
+ }
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ f.writeChar('{');
+ for (self.props, 0..) |p, i| {
+ if (i > 0) f.write(", ");
+ p.fmt(f);
+ }
+ f.writeChar('}');
+ }
+};
+
+pub const PropType = union(enum) {
+ init: PropInit,
+ get: PropGet,
+ set: PropSet,
+ shorthand: []const u8,
+ spread: SpreadElem,
+ method: FnExpr,
+};
+
+pub const Prop = struct {
+ loc: Loc,
+ key: PropKey,
+ data: PropType,
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ switch (self.data) {
+ .init => |p_init| {
+ self.key.fmt(f);
+ f.write(": ");
+ p_init.value.*.fmt(f);
+ },
+ .get => |g| g.fmt(f),
+ .set => |s| s.fmt(f),
+ .shorthand => |name| f.write(name),
+ .spread => |sp| {
+ f.write("...");
+ sp.arg.*.fmt(f);
+ },
+ .method => |m| m.fmt(f),
+ }
+ }
+};
+
+pub const PropKey = union(enum) {
+ ident: []const u8,
+ string: []const u8,
+ number: f64,
+ computed: *const Expr,
+ private: []const u8,
+};
+
+pub const PropInit = struct {
+ key: PropKey,
+ value: *const Expr,
+};
+
+pub const PropGet = struct {
+ key: PropKey,
+ body: BlockStmt,
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ f.write("get ");
+ self.key.fmt(f);
+ f.write("() ");
+ self.body.fmt(f);
+ }
+};
+
+pub const PropSet = struct {
+ key: PropKey,
+ param: Pat,
+ body: BlockStmt,
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ f.write("set ");
+ self.key.fmt(f);
+ f.writeChar('(');
+ self.param.fmt(f);
+ f.write(") ");
+ self.body.fmt(f);
+ }
+};
+
+pub const SpreadElem = struct {
+ loc: Loc,
+ arg: *const Expr,
+};
+
+pub const MemberExpr = struct {
+ loc: Loc,
+ obj: *const Expr,
+ prop: union(enum) {
+ ident: []const u8,
+ private: []const u8,
+ },
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ self.obj.*.fmt(f);
+ switch (self.prop) {
+ .ident => |name| {
+ f.writeChar('.');
+ f.write(name);
+ },
+ .private => |name| {
+ f.writeChar('.');
+ f.write(name);
+ },
+ }
+ }
+};
+
+pub const ComputedMember = struct {
+ loc: Loc,
+ obj: *const Expr,
+ expr: *const Expr,
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ self.obj.*.fmt(f);
+ f.writeChar('[');
+ self.expr.*.fmt(f);
+ f.writeChar(']');
+ }
+};
+
+pub const CallExpr = struct {
+ loc: Loc,
+ callee: *const Expr,
+ args: []const Expr,
+ optional: bool,
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ self.callee.*.fmt(f);
+ if (self.optional) {
+ f.write("?.(");
+ } else {
+ f.writeChar('(');
+ }
+ for (self.args, 0..) |a, i| {
+ if (i > 0) f.write(", ");
+ a.fmt(f);
+ }
+ f.writeChar(')');
+ }
+};
+
+pub const NewExpr = struct {
+ loc: Loc,
+ callee: *const Expr,
+ args: []const Expr,
+};
+
+pub const ChainExpr = struct {
+ loc: Loc,
+ expr: *const Expr,
+};
+
+pub const UnaryOp = enum {
+ @"-",
+ @"+",
+ @"!",
+ @"~",
+ typeof,
+ void,
+ delete,
+};
+
+pub const UnaryExpr = struct {
+ loc: Loc,
+ op: UnaryOp,
+ arg: *const Expr,
+ prefix: bool,
+};
+
+pub const BinaryOp = enum {
+ @"==",
+ @"!=",
+ @"===",
+ @"!==",
+ @"<",
+ @"<=",
+ @">",
+ @">=",
+ @"<<",
+ @">>",
+ @">>>",
+ @"+",
+ @"-",
+ @"*",
+ @"/",
+ @"%",
+ @"**",
+ @"|",
+ @"^",
+ @"&",
+ in,
+ instanceof,
+ @"||",
+ @"&&",
+ @"??",
+};
+
+pub const BinaryExpr = struct {
+ loc: Loc,
+ op: BinaryOp,
+ left: *const Expr,
+ right: *const Expr,
+};
+
+pub const UpdateOp = enum {
+ @"++",
+ @"--",
+};
+
+pub const UpdateExpr = struct {
+ loc: Loc,
+ op: UpdateOp,
+ arg: *const Expr,
+ prefix: bool,
+};
+
+pub const IfExpr = struct {
+ loc: Loc,
+ condition: *const Expr,
+ consequent: *const Expr,
+ alternate: *const Expr,
+};
+
+pub const AssignOp = enum {
+ @"=",
+ @"+=",
+ @"-=",
+ @"*=",
+ @"/=",
+ @"%=",
+ @"**=",
+ @"<<=",
+ @">>=",
+ @">>>=",
+ @"|=",
+ @"^=",
+ @"&=",
+ @"||=",
+ @"&&=",
+ @"??=",
+};
+
+pub const AssignExpr = struct {
+ loc: Loc,
+ op: AssignOp,
+ left: *const Expr,
+ right: *const Expr,
+};
+
+pub const SeqExpr = struct {
+ loc: Loc,
+ exprs: []const Expr,
+};
+
+pub const YieldExpr = struct {
+ loc: Loc,
+ arg: ?*const Expr,
+ delegate: bool,
+};
+
+pub const AwaitExpr = struct {
+ loc: Loc,
+ arg: *const Expr,
+};
+
+pub const MetaProp = struct {
+ loc: Loc,
+ meta: []const u8,
+ prop: []const u8,
+};
+
+pub const TemplateLit = struct {
+ loc: Loc,
+ quasis: []const TemplateElem,
+ exprs: []const Expr,
+
+ pub fn parse(p: *Parser) ParseError!TemplateLit {
+ const start = p.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 (p.peek()) {
+ .no_sub_template => {
+ const t = p.advanceTok();
+ const slice = p.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 = p.advanceTok();
+ const slice = p.tokenSlice(t);
+ quasis[q_count] = .{ .loc = .{ .start = t.loc.start, .end = t.loc.end }, .value = slice, .tail = false };
+ q_count += 1;
+ const expr = try Expr.parse(p, 0);
+ exprs[e_count] = expr.*;
+ e_count += 1;
+ try p.expect(.rbrace);
+ },
+ .template_middle => {
+ const t = p.advanceTok();
+ const slice = p.tokenSlice(t);
+ quasis[q_count] = .{ .loc = .{ .start = t.loc.start, .end = t.loc.end }, .value = slice, .tail = false };
+ q_count += 1;
+ const expr = try Expr.parse(p, 0);
+ exprs[e_count] = expr.*;
+ e_count += 1;
+ try p.expect(.rbrace);
+ },
+ .template_tail => {
+ const t = p.advanceTok();
+ const slice = p.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 = p.tok().loc.start }, .quasis = quasis[0..q_count], .exprs = exprs[0..e_count] };
+ }
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ f.writeChar('`');
+ var ei: usize = 0;
+ for (self.quasis, 0..) |q, i| {
+ if (i > 0 and ei < self.exprs.len) {
+ f.write("${");
+ self.exprs[ei].fmt(f);
+ f.writeChar('}');
+ ei += 1;
+ }
+ f.write(q.value);
+ }
+ f.writeChar('`');
+ }
+};
+
+pub const TemplateElem = struct {
+ loc: Loc,
+ value: []const u8,
+ tail: bool,
+};
+
+pub const TaggedTemplate = struct {
+ loc: Loc,
+ tag: *const Expr,
+ quasi: TemplateLit,
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ self.tag.*.fmt(f);
+ self.quasi.fmt(f);
+ }
+};
+
+pub const Decorator = struct {
+ loc: Loc,
+ expr: *const Expr,
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ f.writeChar('@');
+ self.expr.*.fmt(f);
+ }
+};
diff --git a/src/z/parser/js/ast/lit.zig b/src/z/parser/js/ast/lit.zig
new file mode 100644
index 0000000..3a5149f
--- /dev/null
+++ b/src/z/parser/js/ast/lit.zig
@@ -0,0 +1,48 @@
+const std = @import("std");
+const Loc = @import("loc.zig").Loc;
+const Formatter = @import("../formatter.zig").Formatter;
+
+pub const LitType = union(enum) {
+ null: void,
+ bool: bool,
+ number: f64,
+ string: []const u8,
+ regex: Regex,
+ bigint: []const u8,
+};
+
+pub const Lit = struct {
+ loc: Loc,
+ data: LitType,
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ switch (self.data) {
+ .null => f.write("null"),
+ .bool => |b| f.write(if (b) "true" else "false"),
+ .number => |n| {
+ var buf: [64]u8 = undefined;
+ f.write(std.fmt.bufPrint(&buf, "{d}", .{n}) catch "0");
+ },
+ .string => |s| {
+ f.writeChar('"');
+ f.write(s);
+ f.writeChar('"');
+ },
+ .regex => |r| {
+ f.writeChar('/');
+ f.write(r.pattern);
+ f.writeChar('/');
+ f.write(r.flags);
+ },
+ .bigint => |s| {
+ f.write(s);
+ f.writeChar('n');
+ },
+ }
+ }
+};
+
+pub const Regex = struct {
+ pattern: []const u8,
+ flags: []const u8,
+};
diff --git a/src/z/parser/js/ast/loc.zig b/src/z/parser/js/ast/loc.zig
new file mode 100644
index 0000000..276d05c
--- /dev/null
+++ b/src/z/parser/js/ast/loc.zig
@@ -0,0 +1,4 @@
+pub const Loc = struct {
+ start: usize,
+ end: usize,
+};
diff --git a/src/z/parser/js/ast/pat.zig b/src/z/parser/js/ast/pat.zig
new file mode 100644
index 0000000..fdc8f55
--- /dev/null
+++ b/src/z/parser/js/ast/pat.zig
@@ -0,0 +1,190 @@
+const std = @import("std");
+const Loc = @import("loc.zig").Loc;
+const Expr = @import("expr.zig").Expr;
+const PropKey = @import("expr.zig").PropKey;
+const Parser = @import("../parser.zig").Parser;
+const Formatter = @import("../formatter.zig").Formatter;
+const ParseError = @import("../parser.zig").ParseError;
+
+pub const PatType = union(enum) {
+ ident: []const u8,
+ object: ObjectPat,
+ array: ArrayPat,
+ assign: AssignPat,
+ rest: RestPat,
+};
+
+pub const Pat = struct {
+ loc: Loc,
+ data: PatType,
+
+ pub fn parse(p: *Parser) ParseError!Pat {
+ const start = p.tok().loc.start;
+
+ switch (p.peek()) {
+ .identifier, .await, .yield => {
+ const t = p.advanceTok();
+ const name = p.tokenSlice(t);
+ return Pat{ .loc = .{ .start = start, .end = t.loc.end }, .data = .{ .ident = name } };
+ },
+ .lbrace => {
+ p.advance();
+ var props: [128]PatProp = undefined;
+ var count: usize = 0;
+ var rest: ?*const Pat = null;
+
+ while (p.peek() != .rbrace and p.peek() != .eof) {
+ if (p.peek() == .@"...") {
+ p.advance();
+ const pat = try Pat.parse(p);
+ rest = p.arena.patPtr(pat.data, pat.loc);
+ break;
+ }
+ if (p.peek() == .identifier) {
+ const name_tok = p.tok();
+ const name = p.tokenSlice(name_tok);
+ p.advance();
+ if (p.peek() == .@":") {
+ p.advance();
+ const val_pat = try Pat.parse(p);
+ props[count] = .{ .key_value = .{ .key = .{ .ident = name }, .value = p.arena.patPtr(val_pat.data, val_pat.loc) } };
+ count += 1;
+ } else {
+ props[count] = .{ .shorthand = name };
+ count += 1;
+ }
+ } else if (p.peek() == .lbracket) {
+ const key = try p.parsePropKey();
+ try p.expect(.@":");
+ const val_pat = try Pat.parse(p);
+ props[count] = .{ .key_value = .{ .key = key, .value = p.arena.patPtr(val_pat.data, val_pat.loc) } };
+ count += 1;
+ } else {
+ break;
+ }
+ if (!p.match(.comma)) break;
+ }
+
+ try p.expect(.rbrace);
+ return Pat{ .loc = .{ .start = start, .end = p.tok().loc.start }, .data = .{ .object = .{ .loc = .{ .start = start, .end = p.tok().loc.start }, .props = props[0..count], .rest = rest } } };
+ },
+ .lbracket => {
+ p.advance();
+ var elems: [128]?Pat = undefined;
+ var count: usize = 0;
+ var rest: ?*const Pat = null;
+
+ while (p.peek() != .rbracket and p.peek() != .eof) {
+ if (p.match(.comma)) {
+ elems[count] = null;
+ count += 1;
+ } else if (p.peek() == .@"...") {
+ p.advance();
+ const pat = try Pat.parse(p);
+ rest = p.arena.patPtr(pat.data, pat.loc);
+ break;
+ } else {
+ const pat = try Pat.parse(p);
+ elems[count] = pat;
+ count += 1;
+ if (!p.match(.comma)) break;
+ }
+ }
+
+ try p.expect(.rbracket);
+ return Pat{ .loc = .{ .start = start, .end = p.tok().loc.start }, .data = .{ .array = .{ .loc = .{ .start = start, .end = p.tok().loc.start }, .elems = elems[0..count], .rest = rest } } };
+ },
+ else => return ParseError.ExpectedParameterName,
+ }
+ }
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ switch (self.data) {
+ .ident => |name| f.write(name),
+ .object => |o| o.fmt(f),
+ .array => |a| a.fmt(f),
+ .assign => |a| {
+ a.left.*.fmt(f);
+ f.write(" = ");
+ a.right.*.fmt(f);
+ },
+ .rest => |r| {
+ f.write("...");
+ r.arg.*.fmt(f);
+ },
+ }
+ }
+};
+
+pub const ObjectPat = struct {
+ loc: Loc,
+ props: []const PatProp,
+ rest: ?*const Pat,
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ f.writeChar('{');
+ var first = true;
+ for (self.props) |prop| {
+ if (!first) f.write(", ");
+ first = false;
+ switch (prop) {
+ .key_value => |kv| {
+ kv.key.fmt(f);
+ f.write(": ");
+ kv.value.*.fmt(f);
+ },
+ .shorthand => |name| f.write(name),
+ .rest => |r| {
+ f.write("...");
+ r.*.fmt(f);
+ },
+ }
+ }
+ if (self.rest) |rest| {
+ if (!first) f.write(", ");
+ f.write("...");
+ rest.*.fmt(f);
+ }
+ f.writeChar('}');
+ }
+};
+
+pub const PatProp = union(enum) {
+ key_value: struct {
+ key: PropKey,
+ value: *const Pat,
+ },
+ shorthand: []const u8,
+ rest: *const Pat,
+};
+
+pub const ArrayPat = struct {
+ loc: Loc,
+ elems: []const ?Pat,
+ rest: ?*const Pat,
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ f.writeChar('[');
+ for (self.elems, 0..) |elem, i| {
+ if (i > 0) f.write(", ");
+ if (elem) |pat| pat.fmt(f);
+ }
+ if (self.rest) |rest| {
+ if (self.elems.len > 0) f.write(", ");
+ f.write("...");
+ rest.*.fmt(f);
+ }
+ f.writeChar(']');
+ }
+};
+
+pub const AssignPat = struct {
+ loc: Loc,
+ left: *const Pat,
+ right: *const Expr,
+};
+
+pub const RestPat = struct {
+ loc: Loc,
+ arg: *const Pat,
+};
diff --git a/src/z/parser/js/ast/stmt.zig b/src/z/parser/js/ast/stmt.zig
new file mode 100644
index 0000000..c15b1f5
--- /dev/null
+++ b/src/z/parser/js/ast/stmt.zig
@@ -0,0 +1,865 @@
+const std = @import("std");
+const Loc = @import("loc.zig").Loc;
+const Expr = @import("expr.zig").Expr;
+const Pat = @import("pat.zig").Pat;
+const Decl = @import("decl.zig").Decl;
+const FnDecl = @import("decl.zig").FnDecl;
+const ClassDecl = @import("decl.zig").ClassDecl;
+const ImportDecl = @import("decl.zig").ImportDecl;
+const ExportDecl = @import("decl.zig").ExportDecl;
+const Parser = @import("../parser.zig").Parser;
+const Formatter = @import("../formatter.zig").Formatter;
+const ParseError = @import("../parser.zig").ParseError;
+
+pub const StmtType = union(enum) {
+ block: BlockStmt,
+ empty: void,
+ debugger: void,
+ expr: ExprStmt,
+ @"if": IfStmt,
+ @"while": WhileStmt,
+ do_while: DoWhileStmt,
+ @"for": ForStmt,
+ for_in: ForInStmt,
+ for_of: ForOfStmt,
+ @"continue": ContinueStmt,
+ @"break": BreakStmt,
+ @"return": ReturnStmt,
+ @"switch": SwitchStmt,
+ throw: ThrowStmt,
+ @"try": TryStmt,
+ labelled: LabelledStmt,
+ with: WithStmt,
+ variable: VarStmt,
+ lexical: LexicalDecl,
+ function: FnDecl,
+ class: ClassDecl,
+ using: UsingStmt,
+ import: ImportDecl,
+ @"export": ExportDecl,
+};
+
+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),
+ .empty => f.writeChar(';'),
+ .debugger => f.write("debugger;"),
+ .expr => |e| {
+ e.expr.*.fmt(f);
+ f.writeChar(';');
+ },
+ .@"if" => |s| s.fmt(f),
+ .@"while" => |w| w.fmt(f),
+ .do_while => |d| d.fmt(f),
+ .@"for" => |fo| fo.fmt(f),
+ .for_in => |fi| fi.fmt(f),
+ .for_of => |fo| fo.fmt(f),
+ .@"continue" => |c| {
+ f.write("continue");
+ if (c.label) |l| {
+ f.writeSpace();
+ f.write(l);
+ }
+ f.writeChar(';');
+ },
+ .@"break" => |b| {
+ f.write("break");
+ if (b.label) |l| {
+ f.writeSpace();
+ f.write(l);
+ }
+ f.writeChar(';');
+ },
+ .@"return" => |r| {
+ f.write("return");
+ if (r.arg) |a| {
+ f.writeSpace();
+ a.*.fmt(f);
+ }
+ f.writeChar(';');
+ },
+ .@"switch" => |s| s.fmt(f),
+ .throw => |t| {
+ f.write("throw ");
+ t.arg.*.fmt(f);
+ f.writeChar(';');
+ },
+ .@"try" => |t| t.fmt(f),
+ .labelled => |l| {
+ f.write(l.label);
+ f.write(": ");
+ l.body.*.fmt(f);
+ },
+ .with => |w| {
+ f.write("with (");
+ w.obj.*.fmt(f);
+ f.write(") ");
+ w.body.*.fmt(f);
+ },
+ .variable => |v| v.fmt(f),
+ .lexical => |l| l.fmt(f),
+ .using => |u| {
+ f.write("using ");
+ for (u.decls, 0..) |d, i| {
+ if (i > 0) f.write(", ");
+ d.fmt(f);
+ }
+ f.writeChar(';');
+ },
+ .import => |i| i.fmt(f),
+ .@"export" => |e| e.fmt(f),
+ .class => |c| c.fmt(f),
+ .function => |d| d.fmt(f),
+ }
+ }
+};
+
+pub const ForInit = union(enum) {
+ expr: *const Expr,
+ decl: *const Decl,
+};
+
+pub const BlockStmt = struct {
+ loc: Loc,
+ body: []const Stmt,
+
+ pub fn parse(p: *Parser) ParseError!BlockStmt {
+ const start = try p.expectTok(.lbrace);
+ var stmts: [1024]Stmt = undefined;
+ var count: usize = 0;
+
+ while (p.peek() != .rbrace and p.peek() != .eof) {
+ const s = try Stmt.parse(p);
+ stmts[count] = s;
+ count += 1;
+ if (count >= stmts.len) return ParseError.TooManyTokens;
+ }
+
+ const end = try p.expectTok(.rbrace);
+ return .{ .loc = .{ .start = start.loc.start, .end = end.loc.end }, .body = stmts[0..count] };
+ }
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ f.writeChar('{');
+ if (self.body.len > 0) {
+ f.newline();
+ f.indent_level += 1;
+ for (self.body) |s| {
+ f.writeIndent();
+ s.fmt(f);
+ f.newline();
+ }
+ f.indent_level -= 1;
+ f.writeIndent();
+ }
+ f.writeChar('}');
+ }
+};
+
+pub const ExprStmt = struct {
+ loc: Loc,
+ expr: *const Expr,
+
+ pub fn parse(p: *Parser) ParseError!ExprStmt {
+ const expr = try Expr.parse(p, 0);
+ p.semicolon();
+ return .{ .loc = expr.loc, .expr = expr };
+ }
+};
+
+pub const IfStmt = struct {
+ loc: Loc,
+ condition: *const Expr,
+ consequent: *const Stmt,
+ alternate: ?*const Stmt,
+
+ pub fn parse(p: *Parser) ParseError!IfStmt {
+ const start = p.advanceTok().loc.start;
+ try p.expect(.lparen);
+ const condition = try Expr.parse(p, 0);
+ try p.expect(.rparen);
+ 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")) {
+ 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 };
+ }
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ f.write("if (");
+ self.condition.*.fmt(f);
+ f.write(") ");
+ self.consequent.*.fmt(f);
+ if (self.alternate) |alt| {
+ f.write(" else ");
+ alt.*.fmt(f);
+ }
+ }
+};
+
+pub const WhileStmt = struct {
+ loc: Loc,
+ condition: *const Expr,
+ body: *const Stmt,
+
+ pub fn parse(p: *Parser) ParseError!WhileStmt {
+ const start = p.advanceTok().loc.start;
+ try p.expect(.lparen);
+ const condition = try Expr.parse(p, 0);
+ try p.expect(.rparen);
+ 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 };
+ }
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ f.write("while (");
+ self.condition.*.fmt(f);
+ f.write(") ");
+ self.body.*.fmt(f);
+ }
+};
+
+pub const DoWhileStmt = struct {
+ loc: Loc,
+ body: *const Stmt,
+ condition: *const Expr,
+
+ pub fn parse(p: *Parser) ParseError!DoWhileStmt {
+ const start = p.advanceTok().loc.start;
+ 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);
+ try p.expect(.rparen);
+ p.semicolon();
+ return .{ .loc = .{ .start = start, .end = condition.loc.end }, .body = body, .condition = condition };
+ }
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ f.write("do ");
+ self.body.*.fmt(f);
+ f.write(" while (");
+ self.condition.*.fmt(f);
+ f.write(");");
+ }
+};
+
+pub const ForStmt = struct {
+ loc: Loc,
+ init: ?ForInit,
+ condition: ?*const Expr,
+ update: ?*const Expr,
+ body: *const Stmt,
+
+ pub fn parse(p: *Parser, start: usize, for_init: ?ForInit) ParseError!ForStmt {
+ var condition: ?*const Expr = null;
+ var update: ?*const Expr = null;
+
+ try p.expect(.semicolon);
+ if (p.peek() != .semicolon and p.peek() != .rparen) {
+ condition = try Expr.parse(p, 0);
+ }
+ try p.expect(.semicolon);
+ if (p.peek() != .rparen) {
+ update = try Expr.parse(p, 0);
+ }
+ try p.expect(.rparen);
+ 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 };
+ }
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ f.write("for (");
+ if (self.init) |init| {
+ switch (init) {
+ .expr => |e| e.*.fmt(f),
+ .decl => |d| d.*.fmt(f),
+ }
+ }
+ f.writeChar(';');
+ if (self.condition) |c| {
+ f.writeSpace();
+ c.*.fmt(f);
+ }
+ f.writeChar(';');
+ if (self.update) |u| {
+ f.writeSpace();
+ u.*.fmt(f);
+ }
+ f.write(") ");
+ self.body.*.fmt(f);
+ }
+};
+
+pub const ForInStmt = struct {
+ loc: Loc,
+ left: ForInit,
+ right: *const Expr,
+ body: *const Stmt,
+
+ 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 = 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 };
+ }
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ f.write("for (");
+ switch (self.left) {
+ .expr => |e| e.*.fmt(f),
+ .decl => |d| d.*.fmt(f),
+ }
+ f.write(" in ");
+ self.right.*.fmt(f);
+ f.write(") ");
+ self.body.*.fmt(f);
+ }
+};
+
+pub const ForOfStmt = struct {
+ loc: Loc,
+ await_token: bool,
+ left: ForInit,
+ right: *const Expr,
+ body: *const Stmt,
+
+ 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 = 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 };
+ }
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ f.write("for");
+ if (self.await_token) f.write(" await");
+ f.write(" (");
+ switch (self.left) {
+ .expr => |e| e.*.fmt(f),
+ .decl => |d| d.*.fmt(f),
+ }
+ f.write(" of ");
+ self.right.*.fmt(f);
+ f.write(") ");
+ self.body.*.fmt(f);
+ }
+};
+
+pub const ContinueStmt = struct {
+ loc: Loc,
+ label: ?[]const u8,
+
+ pub fn parse(p: *Parser) ParseError!ContinueStmt {
+ const start = p.advanceTok().loc.start;
+ var label: ?[]const u8 = null;
+ if (p.peek() == .identifier and !p.isLineTerminatorBeforeNext()) {
+ const t = p.advanceTok();
+ label = p.tokenSlice(t);
+ }
+ p.semicolon();
+ return .{ .loc = .{ .start = start, .end = start + 8 }, .label = label };
+ }
+};
+
+pub const BreakStmt = struct {
+ loc: Loc,
+ label: ?[]const u8,
+
+ pub fn parse(p: *Parser) ParseError!BreakStmt {
+ const start = p.advanceTok().loc.start;
+ var label: ?[]const u8 = null;
+ if (p.peek() == .identifier and !p.isLineTerminatorBeforeNext()) {
+ const t = p.advanceTok();
+ label = p.tokenSlice(t);
+ }
+ p.semicolon();
+ return .{ .loc = .{ .start = start, .end = start + 5 }, .label = label };
+ }
+};
+
+pub const ReturnStmt = struct {
+ loc: Loc,
+ arg: ?*const Expr,
+
+ pub fn parse(p: *Parser) ParseError!ReturnStmt {
+ const start = p.advanceTok().loc.start;
+ var arg: ?*const Expr = null;
+ if (p.peek() != .semicolon and p.peek() != .rbrace and p.peek() != .eof and !p.isLineTerminatorBeforeNext()) {
+ arg = try Expr.parse(p, 0);
+ }
+ p.semicolon();
+ return .{ .loc = .{ .start = start, .end = if (arg) |a| a.loc.end else start + 6 }, .arg = arg };
+ }
+};
+
+pub const SwitchStmt = struct {
+ loc: Loc,
+ discriminant: *const Expr,
+ cases: []const SwitchCase,
+
+ pub fn parse(p: *Parser) ParseError!SwitchStmt {
+ const start = p.advanceTok().loc.start;
+ try p.expect(.lparen);
+ const discriminant = try Expr.parse(p, 0);
+ try p.expect(.rparen);
+ try p.expect(.lbrace);
+
+ var cases: [256]SwitchCase = undefined;
+ var case_count: usize = 0;
+
+ while (p.peek() == .case or p.peek() == .default) {
+ const case_start = p.tok().loc.start;
+ if (p.matchKeyword(.case)) {
+ const case_test = try Expr.parse(p, 0);
+ try p.expect(.@":");
+ var consequent: [128]Stmt = undefined;
+ var cons_count: usize = 0;
+ while (p.peek() != .case and p.peek() != .default and p.peek() != .rbrace and p.peek() != .eof) {
+ const s = try Stmt.parse(p);
+ consequent[cons_count] = s;
+ cons_count += 1;
+ }
+ cases[case_count] = .{ .loc = .{ .start = case_start, .end = p.tok().loc.start }, .condition = case_test, .consequent = consequent[0..cons_count] };
+ case_count += 1;
+ } else if (p.matchKeyword(.default)) {
+ try p.expect(.@":");
+ var consequent: [128]Stmt = undefined;
+ var cons_count: usize = 0;
+ while (p.peek() != .case and p.peek() != .default and p.peek() != .rbrace and p.peek() != .eof) {
+ const s = try Stmt.parse(p);
+ consequent[cons_count] = s;
+ cons_count += 1;
+ }
+ cases[case_count] = .{ .loc = .{ .start = case_start, .end = p.tok().loc.start }, .condition = null, .consequent = consequent[0..cons_count] };
+ case_count += 1;
+ }
+ }
+
+ const end_tok = try p.expectTok(.rbrace);
+ return .{ .loc = .{ .start = start, .end = end_tok.loc.end }, .discriminant = discriminant, .cases = cases[0..case_count] };
+ }
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ f.write("switch (");
+ self.discriminant.*.fmt(f);
+ f.write(") {");
+ f.newline();
+ f.indent_level += 1;
+ for (self.cases) |c| {
+ f.writeIndent();
+ c.fmt(f);
+ f.newline();
+ }
+ f.indent_level -= 1;
+ f.writeIndent();
+ f.writeChar('}');
+ }
+};
+
+pub const SwitchCase = struct {
+ loc: Loc,
+ condition: ?*const Expr,
+ consequent: []const Stmt,
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ if (self.condition) |c| {
+ f.write("case ");
+ c.*.fmt(f);
+ } else {
+ f.write("default");
+ }
+ f.writeChar(':');
+ for (self.consequent) |s| {
+ f.writeSpace();
+ s.fmt(f);
+ }
+ }
+};
+
+pub const ThrowStmt = struct {
+ loc: Loc,
+ arg: *const Expr,
+
+ pub fn parse(p: *Parser) ParseError!ThrowStmt {
+ const start = p.advanceTok().loc.start;
+ if (p.isLineTerminatorBeforeNext()) return ParseError.ExpectedExpression;
+ const arg = try Expr.parse(p, 0);
+ p.semicolon();
+ return .{ .loc = .{ .start = start, .end = arg.loc.end }, .arg = arg };
+ }
+};
+
+pub const TryStmt = struct {
+ loc: Loc,
+ block: BlockStmt,
+ handler: ?CatchClause,
+ finalizer: ?BlockStmt,
+
+ pub fn parse(p: *Parser) ParseError!TryStmt {
+ const start = p.advanceTok().loc.start;
+ const block = try p.parseFunctionBody();
+ var handler: ?CatchClause = null;
+ var finalizer: ?BlockStmt = null;
+
+ if (p.matchKeyword(.@"catch")) {
+ var param: ?Pat = null;
+ if (p.match(.lparen)) {
+ const pat = try Pat.parse(p);
+ param = pat;
+ try p.expect(.rparen);
+ }
+ const catch_body = try p.parseFunctionBody();
+ handler = .{ .loc = .{ .start = block.loc.start, .end = catch_body.loc.end }, .param = param, .body = catch_body };
+ }
+
+ if (p.matchKeyword(.finally)) {
+ const finally_body = try p.parseFunctionBody();
+ finalizer = finally_body;
+ }
+
+ const end = if (finalizer) |f| f.loc.end else if (handler) |h| h.loc.end else block.loc.end;
+ return .{ .loc = .{ .start = start, .end = end }, .block = block, .handler = handler, .finalizer = finalizer };
+ }
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ f.write("try ");
+ self.block.fmt(f);
+ if (self.handler) |h| {
+ f.writeSpace();
+ h.fmt(f);
+ }
+ if (self.finalizer) |fin| {
+ f.write(" finally ");
+ fin.fmt(f);
+ }
+ }
+};
+
+pub const CatchClause = struct {
+ loc: Loc,
+ param: ?Pat,
+ body: BlockStmt,
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ f.write("catch");
+ if (self.param) |p| {
+ f.write(" (");
+ p.fmt(f);
+ f.write(") ");
+ } else {
+ f.writeSpace();
+ }
+ self.body.fmt(f);
+ }
+};
+
+pub const LabelledStmt = struct {
+ loc: Loc,
+ label: []const u8,
+ body: *const Stmt,
+};
+
+pub const WithStmt = struct {
+ loc: Loc,
+ obj: *const Expr,
+ body: *const Stmt,
+
+ pub fn parse(p: *Parser) ParseError!WithStmt {
+ const start = p.advanceTok().loc.start;
+ try p.expect(.lparen);
+ const obj = try Expr.parse(p, 0);
+ try p.expect(.rparen);
+ 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 };
+ }
+};
+
+pub const VarStmt = struct {
+ loc: Loc,
+ kind: VarKind,
+ decls: []const VarDeclarator,
+
+ pub fn parse(p: *Parser) ParseError!VarStmt {
+ const kind_tok = p.advanceTok();
+ var decls: [128]VarDeclarator = undefined;
+ var count: usize = 0;
+
+ while (true) {
+ const pat = try Pat.parse(p);
+ var decl_init: ?*const Expr = null;
+ if (p.match(.@"=")) {
+ decl_init = try Expr.parse(p, 0);
+ }
+ decls[count] = .{ .loc = pat.loc, .id = pat, .init = decl_init };
+ count += 1;
+ if (!p.match(.comma)) break;
+ }
+
+ p.semicolon();
+ return .{ .loc = .{ .start = kind_tok.loc.start, .end = decls[count - 1].loc.end }, .kind = .@"var", .decls = decls[0..count] };
+ }
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ f.write(@tagName(self.kind));
+ f.writeSpace();
+ for (self.decls, 0..) |d, i| {
+ if (i > 0) f.write(", ");
+ d.fmt(f);
+ }
+ f.writeChar(';');
+ }
+};
+
+pub const UsingStmt = struct {
+ loc: Loc,
+ decls: []const VarDeclarator,
+
+ pub fn parse(p: *Parser) ParseError!UsingStmt {
+ const start = p.advanceTok().loc.start;
+ var decls: [128]VarDeclarator = undefined;
+ var count: usize = 0;
+
+ while (true) {
+ const id_tok = try p.expectTok(.identifier);
+ const id = p.tokenSlice(id_tok);
+ var decl_init: ?*const Expr = null;
+ if (p.match(.@"=")) {
+ decl_init = try Expr.parse(p, 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 (!p.match(.comma)) break;
+ }
+
+ p.semicolon();
+ return .{ .loc = .{ .start = start, .end = decls[count - 1].loc.end }, .decls = decls[0..count] };
+ }
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ f.write("using ");
+ for (self.decls, 0..) |d, i| {
+ if (i > 0) f.write(", ");
+ d.fmt(f);
+ }
+ }
+};
+
+pub const VarKind = enum {
+ @"var",
+ let,
+ @"const",
+};
+
+pub const VarDecl = struct {
+ loc: Loc,
+ kind: VarKind,
+ decls: []const VarDeclarator,
+};
+
+pub const LexicalDecl = struct {
+ loc: Loc,
+ kind: VarKind,
+ decls: []const VarDeclarator,
+
+ pub fn parse(p: *Parser) ParseError!LexicalDecl {
+ const kind_tok = p.advanceTok();
+ var decls: [128]VarDeclarator = undefined;
+ var count: usize = 0;
+
+ while (true) {
+ const pat = try Pat.parse(p);
+ var decl_init: ?*const Expr = null;
+ if (kind_tok.kind == .@"const") {
+ if (!p.match(.@"=")) return ParseError.ExpectedToken;
+ decl_init = try Expr.parse(p, 0);
+ } else if (p.match(.@"=")) {
+ decl_init = try Expr.parse(p, 0);
+ }
+ decls[count] = .{ .loc = pat.loc, .id = pat, .init = decl_init };
+ count += 1;
+ if (!p.match(.comma)) break;
+ }
+
+ p.semicolon();
+ const kind: VarKind = switch (kind_tok.kind) {
+ .let => .let,
+ .@"const" => .@"const",
+ else => return ParseError.UnexpectedToken,
+ };
+ return .{ .loc = .{ .start = kind_tok.loc.start, .end = decls[count - 1].loc.end }, .kind = kind, .decls = decls[0..count] };
+ }
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ f.write(@tagName(self.kind));
+ f.writeSpace();
+ for (self.decls, 0..) |d, i| {
+ if (i > 0) f.write(", ");
+ d.fmt(f);
+ }
+ f.writeChar(';');
+ }
+};
+
+pub const UsingDecl = struct {
+ loc: Loc,
+ is_await: bool,
+ decls: []const VarDeclarator,
+};
+
+pub const VarDeclarator = struct {
+ loc: Loc,
+ id: Pat,
+ init: ?*const Expr,
+
+ pub fn fmt(self: @This(), f: *Formatter) void {
+ self.id.fmt(f);
+ if (self.init) |init| {
+ f.write(" = ");
+ init.*.fmt(f);
+ }
+ }
+};
diff --git a/src/z/parser/js/formatter.zig b/src/z/parser/js/formatter.zig
new file mode 100644
index 0000000..fbea7bc
--- /dev/null
+++ b/src/z/parser/js/formatter.zig
@@ -0,0 +1,67 @@
+const std = @import("std");
+
+pub const FormatOptions = struct {
+ indent_size: usize = 4,
+ minify: bool = false,
+};
+
+pub const Formatter = struct {
+ buf: []u8,
+ pos: usize,
+ indent_level: usize,
+ options: FormatOptions,
+ last_char: u8 = 0,
+
+ pub fn write(self: *@This(), s: []const u8) void {
+ if (self.options.minify) {
+ if (s.len == 0) return;
+ var start: usize = 0;
+ while (start < s.len and s[start] == ' ') start += 1;
+ var end = s.len;
+ while (end > start and s[end - 1] == ' ') end -= 1;
+ if (start >= end) return;
+ const trimmed = s[start..end];
+ if (self.last_char != 0 and isWordChar(self.last_char) and isWordChar(trimmed[0])) {
+ self.buf[self.pos] = ' ';
+ self.pos += 1;
+ }
+ @memcpy(self.buf[self.pos..][0..trimmed.len], trimmed);
+ self.pos += trimmed.len;
+ self.last_char = trimmed[trimmed.len - 1];
+ } else {
+ @memcpy(self.buf[self.pos..][0..s.len], s);
+ self.pos += s.len;
+ }
+ }
+
+ pub fn writeChar(self: *@This(), c: u8) void {
+ self.buf[self.pos] = c;
+ self.pos += 1;
+ self.last_char = c;
+ }
+
+ pub fn writeIndent(self: *@This()) void {
+ if (self.options.minify) return;
+ const spaces = self.indent_level * self.options.indent_size;
+ var i: usize = 0;
+ while (i < spaces) : (i += 1) {
+ self.writeChar(' ');
+ }
+ }
+
+ pub fn newline(self: *@This()) void {
+ if (self.options.minify) return;
+ self.writeChar('\n');
+ }
+
+ pub fn writeSpace(self: *@This()) void {
+ if (!self.options.minify) self.writeChar(' ');
+ }
+
+ fn isWordChar(c: u8) bool {
+ return switch (c) {
+ 'a'...'z', 'A'...'Z', '0'...'9', '_', '$' => true,
+ else => false,
+ };
+ }
+};
diff --git a/src/z/parser/js/lexical_grammar.zig b/src/z/parser/js/lexical_grammar.zig
index 2211fbe..35d1a4a 100644
--- a/src/z/parser/js/lexical_grammar.zig
+++ b/src/z/parser/js/lexical_grammar.zig
@@ -1,7 +1,6 @@
const std = @import("std");
-const ast = @import("ast.zig");
-pub const Loc = ast.Loc;
+pub const Loc = @import("ast/loc.zig").Loc;
pub const TokenType = enum {
// Keywords
diff --git a/src/z/parser/js/parser.zig b/src/z/parser/js/parser.zig
new file mode 100644
index 0000000..09118dd
--- /dev/null
+++ b/src/z/parser/js/parser.zig
@@ -0,0 +1,553 @@
+const std = @import("std");
+const lex = @import("lexical_grammar.zig");
+const Token = lex.Token;
+const TokenType = lex.TokenType;
+const ast = @import("ast/core.zig");
+
+pub const ParseError = error{
+ UnexpectedToken,
+ ExpectedToken,
+ ExpectedExpression,
+ ExpectedStatement,
+ ExpectedIdentifier,
+ UnclosedString,
+ InvalidNumber,
+ InvalidEscape,
+ UnterminatedComment,
+ ExpectedParameterName,
+ ExpectedPropertyName,
+ UnterminatedTemplate,
+ InvalidAssignmentTarget,
+ TooManyTokens,
+};
+
+pub const BP = struct {
+ pub const comma = 1;
+ pub const assign = 2;
+ pub const yield = 3;
+ pub const conditional = 4;
+ pub const coalesce = 5;
+ pub const log_or = 6;
+ pub const log_and = 7;
+ pub const bit_or = 8;
+ pub const bit_xor = 9;
+ pub const bit_and = 10;
+ pub const equality = 11;
+ pub const relational = 12;
+ pub const shift = 13;
+ pub const additive = 14;
+ pub const multiplicative = 15;
+ pub const exponentiation = 16;
+ pub const unary = 17;
+ pub const update = 18;
+ pub const call = 19;
+ pub const member = 20;
+};
+
+pub const NodeArena = struct {
+ exprs: [8192]ast.Expr = undefined,
+ expr_count: usize = 0,
+ stmts: [8192]ast.Stmt = undefined,
+ stmt_count: usize = 0,
+ decls: [1024]ast.Decl = undefined,
+ decl_count: usize = 0,
+ pats: [1024]ast.Pat = undefined,
+ pat_count: usize = 0,
+ lits: [1024]ast.Lit = undefined,
+ lit_count: usize = 0,
+ string_buf: [65536]u8 = undefined,
+ string_pos: usize = 0,
+
+ pub fn exprPtr(self: *NodeArena, data: ast.ExprType, loc: ast.Loc) *const ast.Expr {
+ const idx = self.expr_count;
+ self.exprs[idx] = .{ .loc = loc, .data = data };
+ self.expr_count += 1;
+ return &self.exprs[idx];
+ }
+
+ pub fn stmtPtr(self: *NodeArena, data: ast.StmtType, loc: ast.Loc) *const ast.Stmt {
+ const idx = self.stmt_count;
+ self.stmts[idx] = .{ .loc = loc, .data = data };
+ self.stmt_count += 1;
+ return &self.stmts[idx];
+ }
+
+ pub fn declPtr(self: *NodeArena, data: ast.DeclType, loc: ast.Loc) *const ast.Decl {
+ const idx = self.decl_count;
+ self.decls[idx] = .{ .loc = loc, .data = data };
+ self.decl_count += 1;
+ return &self.decls[idx];
+ }
+
+ pub fn patPtr(self: *NodeArena, data: ast.PatType, loc: ast.Loc) *const ast.Pat {
+ const idx = self.pat_count;
+ self.pats[idx] = .{ .loc = loc, .data = data };
+ self.pat_count += 1;
+ return &self.pats[idx];
+ }
+
+ pub fn litPtr(self: *NodeArena, data: ast.LitType, loc: ast.Loc) *const ast.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 };
+ }
+
+ pub fn tok(self: *@This()) Token {
+ return self.tokens[self.pos];
+ }
+
+ pub fn peek(self: *@This()) TokenType {
+ return self.tokens[self.pos].kind;
+ }
+
+ pub fn peekAt(self: *@This(), offset: usize) TokenType {
+ const idx = self.pos + offset;
+ if (idx >= self.tokens.len) return .eof;
+ return self.tokens[idx].kind;
+ }
+
+ pub fn advance(self: *@This()) void {
+ self.pos += 1;
+ }
+
+ pub fn advanceTok(self: *@This()) Token {
+ const t = self.tokens[self.pos];
+ self.pos += 1;
+ return t;
+ }
+
+ pub fn expect(self: *@This(), kind: TokenType) ParseError!void {
+ if (self.peek() != kind) return ParseError.ExpectedToken;
+ self.advance();
+ }
+
+ pub fn expectTok(self: *@This(), kind: TokenType) ParseError!Token {
+ if (self.peek() != kind) return ParseError.ExpectedToken;
+ return self.advanceTok();
+ }
+
+ pub fn match(self: *@This(), kind: TokenType) bool {
+ if (self.peek() == kind) {
+ _ = self.advanceTok();
+ return true;
+ }
+ return false;
+ }
+
+ pub fn matchKeyword(self: *@This(), kw: TokenType) bool {
+ if (self.peek() == kw) {
+ _ = self.advanceTok();
+ return true;
+ }
+ return false;
+ }
+
+ pub 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];
+ 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' => return true,
+ else => {},
+ }
+ }
+ return false;
+ }
+
+ pub fn as_identifier(self: *@This(), t: Token) []const u8 {
+ return self.source[t.loc.start..t.loc.end];
+ }
+
+ pub fn tokenSlice(self: *@This(), t: Token) []const u8 {
+ return self.source[t.loc.start..t.loc.end];
+ }
+
+ pub fn parseForDeclaration(self: *@This()) ParseError!*const ast.Decl {
+ const kind_tok = self.advanceTok();
+ var decls: [128]ast.VarDeclarator = undefined;
+ var count: usize = 0;
+
+ while (true) {
+ const pat = try ast.Pat.parse(self);
+ var decl_init: ?*const ast.Expr = null;
+ if (self.match(.@"=")) {
+ decl_init = try ast.Expr.parse(self, 0);
+ }
+ decls[count] = .{ .loc = pat.loc, .id = pat, .init = decl_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 });
+ }
+
+ pub fn parseParams(self: *@This()) ParseError![]const ast.Pat {
+ try self.expect(.lparen);
+ var params: [128]ast.Pat = undefined;
+ var count: usize = 0;
+
+ if (self.peek() != .rparen) {
+ while (true) {
+ if (self.peek() == .@"...") {
+ self.advance();
+ const pat = try ast.Pat.parse(self);
+ params[count] = ast.Pat{ .loc = pat.loc, .data = .{ .rest = .{ .loc = pat.loc, .arg = self.arena.patPtr(pat.data, pat.loc) } } };
+ count += 1;
+ break;
+ }
+ const pat = try ast.Pat.parse(self);
+ if (self.match(.@"=")) {
+ const expr = try ast.Expr.parse(self, 0);
+ params[count] = ast.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 ast.Pat.parse(self);
+ params[count] = ast.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];
+ }
+
+ pub fn parseFunctionBody(self: *@This()) ParseError!ast.BlockStmt {
+ return ast.BlockStmt.parse(self);
+ }
+
+ pub fn parsePrimaryExpr(self: *@This()) ParseError!*const ast.Expr {
+ const start = self.tok().loc.start;
+
+ switch (self.peek()) {
+ .this => {
+ const t = self.advanceTok();
+ return self.arena.exprPtr(.{ .this = {} }, .{ .start = start, .end = t.loc.end });
+ },
+ .super => {
+ const t = self.advanceTok();
+ return self.arena.exprPtr(.{ .super = {} }, .{ .start = start, .end = t.loc.end });
+ },
+ .null => {
+ const t = self.advanceTok();
+ return self.arena.exprPtr(.{ .null = {} }, .{ .start = start, .end = t.loc.end });
+ },
+ .true => {
+ const t = self.advanceTok();
+ return self.arena.exprPtr(.{ .bool = true }, .{ .start = start, .end = t.loc.end });
+ },
+ .false => {
+ const t = self.advanceTok();
+ return self.arena.exprPtr(.{ .bool = false }, .{ .start = start, .end = t.loc.end });
+ },
+ .number => {
+ const t = self.advanceTok();
+ 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.advanceTok();
+ return self.arena.exprPtr(.{ .string = self.tokenSlice(t) }, .{ .start = start, .end = t.loc.end });
+ },
+ .bigint => {
+ const t = self.advanceTok();
+ return self.arena.exprPtr(.{ .number = 0 }, .{ .start = start, .end = t.loc.end });
+ },
+ .regex => {
+ const t = self.advanceTok();
+ 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.advanceTok();
+ const name = self.tokenSlice(t);
+ return self.arena.exprPtr(.{ .identifier = name }, .{ .start = start, .end = t.loc.end });
+ },
+ .private_identifier => {
+ const t = self.advanceTok();
+ const name = self.tokenSlice(t);
+ return self.arena.exprPtr(.{ .private_ident = name }, .{ .start = start, .end = t.loc.end });
+ },
+ .lbrace => return ast.ObjectExpr.parse(self),
+ .lbracket => return ast.ArrayExpr.parse(self),
+ .lparen => {
+ self.advance();
+ if (self.peek() == .rparen) {
+ self.advance();
+ if (self.peek() == .@"=>") {
+ return ast.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 ast.Expr.parse(self, 0);
+ try self.expect(.rparen);
+ if (self.peek() == .@"=>") {
+ return ast.ArrowFn.parse(self, .{ .loc = .{ .start = start, .end = arg.loc.end }, .params = &.{} });
+ }
+ const loc: ast.Loc = .{ .start = start, .end = self.tok().loc.start };
+ return self.arena.exprPtr(.{ .spread = .{ .loc = loc, .arg = arg } }, loc);
+ }
+ const expr = try ast.Expr.parse(self, 0);
+ if (self.peek() == .comma) {
+ var params: [128]ast.Pat = undefined;
+ var count: usize = 0;
+ params[count] = ast.Pat{ .loc = expr.loc, .data = .{ .ident = expr.data.identifier } };
+ count += 1;
+ while (self.match(.comma)) {
+ const p = try ast.Pat.parse(self);
+ params[count] = p;
+ count += 1;
+ }
+ try self.expect(.rparen);
+ if (self.peek() == .@"=>") {
+ return ast.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 = ast.Pat{ .loc = expr.loc, .data = .{ .ident = expr.data.identifier } };
+ return ast.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 ast.FnExpr.parse(self),
+ .class => return ast.ClassExpr.parse(self),
+ .async => {
+ self.advance();
+ if (self.peek() == .function) {
+ return ast.FnExpr.parse(self);
+ }
+ const id_tok = try self.expectTok(.identifier);
+ const name = self.tokenSlice(id_tok);
+ if (self.peek() == .@"=>") {
+ const pat = ast.Pat{ .loc = .{ .start = start, .end = id_tok.loc.end }, .data = .{ .ident = name } };
+ return ast.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 ast.Expr.parse(self, BP.member);
+ var args: [256]ast.Expr = undefined;
+ var arg_count: usize = 0;
+ if (self.peek() == .lparen) {
+ self.advance();
+ if (self.peek() != .rparen) {
+ while (true) {
+ const arg = try ast.Expr.parse(self, 0);
+ args[arg_count] = arg.*;
+ arg_count += 1;
+ if (!self.match(.comma)) break;
+ }
+ }
+ try self.expect(.rparen);
+ }
+ const loc: ast.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 ast.Expr.parse(self, BP.unary);
+ const loc: ast.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 ast.Expr.parse(self, BP.update);
+ const loc: ast.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 ast.Expr.parse(self, BP.update);
+ const loc: ast.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 ast.Expr.parse(self, BP.unary);
+ const loc: ast.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 ast.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 ast.Expr.parse(self, 0);
+ }
+ }
+ const loc: ast.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 ast.TemplateLit.parse(self) }, .{ .start = start, .end = self.tok().loc.start }),
+ .import => {
+ self.advance();
+ try self.expect(.lparen);
+ _ = try ast.Expr.parse(self, 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.expectTok(.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.advanceTok();
+ const name = self.tokenSlice(t);
+ if (self.peek() == .@"=>") {
+ const pat = ast.Pat{ .loc = .{ .start = t.loc.start, .end = t.loc.end }, .data = .{ .ident = name } };
+ return ast.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 });
+ }
+ return ParseError.ExpectedExpression;
+ },
+ }
+ }
+
+ pub fn parsePropKey(self: *@This()) ParseError!ast.PropKey {
+ switch (self.peek()) {
+ .identifier, .let, .static, .get, .set, .async => {
+ const t = self.advanceTok();
+ return ast.PropKey{ .ident = self.tokenSlice(t) };
+ },
+ .string => {
+ const t = self.advanceTok();
+ return ast.PropKey{ .string = self.tokenSlice(t) };
+ },
+ .number => {
+ const t = self.advanceTok();
+ const val = std.fmt.parseFloat(f64, self.tokenSlice(t)) catch 0.0;
+ return ast.PropKey{ .number = val };
+ },
+ .lbracket => {
+ self.advance();
+ const expr = try ast.Expr.parse(self, 0);
+ try self.expect(.rbracket);
+ return ast.PropKey{ .computed = expr };
+ },
+ .private_identifier => {
+ const t = self.advanceTok();
+ return ast.PropKey{ .private = self.tokenSlice(t) };
+ },
+ else => return ParseError.ExpectedPropertyName,
+ }
+ }
+
+ pub fn bp(_: *@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,
+ .lbracket, .@".", .@"?." => BP.member,
+ .@"++", .@"--" => BP.update,
+ .template_head, .no_sub_template => BP.member,
+ else => 0,
+ };
+ }
+
+ pub fn unaryOpFromToken(self: *@This()) ast.UnaryOp {
+ return switch (self.tok().kind) {
+ .@"+" => .@"+",
+ .@"-" => .@"-",
+ .@"!" => .@"!",
+ .@"~" => .@"~",
+ .typeof => .typeof,
+ .void => .void,
+ .delete => .delete,
+ else => .@"+",
+ };
+ }
+
+ pub fn assignOpFromToken(self: *@This()) ast.AssignOp {
+ return switch (self.tokens[self.pos - 1].kind) {
+ .@"=" => .@"=",
+ .@"+=" => .@"+=",
+ .@"-=" => .@"-=",
+ .@"*=" => .@"*=",
+ .@"/=" => .@"/=",
+ .@"%=" => .@"%=",
+ .@"**=" => .@"**=",
+ .@"<<=" => .@"<<=",
+ .@">>=" => .@">>=",
+ .@">>>=" => .@">>>=",
+ .@"&=" => .@"&=",
+ .@"|=" => .@"|=",
+ .@"^=" => .@"^=",
+ .@"&&=" => .@"&&=",
+ .@"||=" => .@"||=",
+ .@"??=" => .@"??=",
+ else => .@"=",
+ };
+ }
+
+ pub fn semicolon(self: *@This()) void {
+ if (self.peek() == .semicolon) {
+ self.advance();
+ }
+ }
+
+ pub fn statementPointer(self: *@This()) ParseError!*const ast.Stmt {
+ const s = try ast.Stmt.parse(self);
+ return self.arena.stmtPtr(s.data, s.loc);
+ }
+};
diff --git a/src/z/parser/js/root.zig b/src/z/parser/js/root.zig
index 565d572..6b2d5c7 100644
--- a/src/z/parser/js/root.zig
+++ b/src/z/parser/js/root.zig
@@ -1,7 +1,7 @@
const std = @import("std");
pub const lexical_grammar = @import("lexical_grammar.zig");
-pub const ast = @import("ast.zig");
+pub const ast = @import("ast/core.zig");
test "simple literal" {
comptime {
@@ -80,6 +80,18 @@ test "format fixture" {
}
}
+test "minify" {
+ comptime {
+ var buf: [65536]u8 = undefined;
+ const ast1 = try ast.Program.parse("function foo() { var x = 1; return x; }");
+ var f = ast.Formatter{ .buf = buf[0..], .pos = 0, .indent_level = 0, .options = .{ .minify = true } };
+ ast1.fmt(&f);
+ const minified = buf[0..f.pos];
+ try std.testing.expect(std.mem.indexOf(u8, minified, "\n") == null);
+ try std.testing.expect(minified.len < 50);
+ }
+}
+
test {
_ = std.testing.refAllDecls(@This());
}