This commit is contained in:
Jarred Sumner
2025-08-06 22:24:19 -07:00
parent 4deeadd53a
commit f9d3ed55f4
6 changed files with 957 additions and 782 deletions

View File

@@ -26,25 +26,27 @@ pub fn Parse(
pub const parseTypeScriptImportEqualsStmt = @import("./parseTypescript.zig").ParseTypescript(parser_feature__typescript, parser_feature__jsx, parser_feature__scan_only).parseTypeScriptImportEqualsStmt;
pub const parseTypescriptEnumStmt = @import("./parseTypescript.zig").ParseTypescript(parser_feature__typescript, parser_feature__jsx, parser_feature__scan_only).parseTypescriptEnumStmt;
pub inline fn parseExprOrBindings(p: *P, level: Level, errors: ?*DeferredErrors) anyerror!Expr {
return try p.parseExprCommon(level, errors, Expr.EFlags.none);
pub inline fn parseExprOrBindings(p: *P, level: Level, errors: ?*DeferredErrors, expr: *Expr) anyerror!void {
return p.parseExprCommon(level, errors, Expr.EFlags.none, expr);
}
pub inline fn parseExpr(p: *P, level: Level) anyerror!Expr {
return try p.parseExprCommon(level, null, Expr.EFlags.none);
var expr: Expr = undefined;
try p.parseExprCommon(level, null, Expr.EFlags.none, &expr);
return expr;
}
pub inline fn parseExprWithFlags(p: *P, level: Level, flags: Expr.EFlags) anyerror!Expr {
return try p.parseExprCommon(level, null, flags);
pub inline fn parseExprWithFlags(p: *P, level: Level, flags: Expr.EFlags, expr: *Expr) anyerror!void {
return p.parseExprCommon(level, null, flags, expr);
}
pub fn parseExprCommon(p: *P, level: Level, errors: ?*DeferredErrors, flags: Expr.EFlags) anyerror!Expr {
pub fn parseExprCommon(p: *P, level: Level, errors: ?*DeferredErrors, flags: Expr.EFlags, expr: *Expr) anyerror!void {
if (!p.stack_check.isSafeToRecurse()) {
try bun.throwStackOverflow();
}
const had_pure_comment_before = p.lexer.has_pure_comment_before and !p.options.ignore_dce_annotations;
var expr = try p.parsePrefix(level, errors, flags);
expr.* = try p.parsePrefix(level, errors, flags);
// There is no formal spec for "__PURE__" comments but from reverse-
// engineering, it looks like they apply to the next CallExpression or
@@ -52,7 +54,7 @@ pub fn Parse(
// to the expression "a().b()".
if (had_pure_comment_before and level.lt(.call)) {
expr = try p.parseSuffix(expr, @as(Level, @enumFromInt(@intFromEnum(Level.call) - 1)), errors, flags);
try p.parseSuffix(expr, @as(Level, @enumFromInt(@intFromEnum(Level.call) - 1)), errors, flags);
switch (expr.data) {
.e_call => |ex| {
ex.can_be_unwrapped_if_unused = .if_unused;
@@ -64,7 +66,7 @@ pub fn Parse(
}
}
return try p.parseSuffix(expr, level, errors, flags);
try p.parseSuffix(expr, level, errors, flags);
}
pub fn parseYieldExpr(p: *P, loc: logger.Loc) !ExprNodeIndex {
@@ -343,10 +345,13 @@ pub fn Parse(
// We don't know yet whether these are arguments or expressions, so parse
p.latest_arrow_arg_loc = p.lexer.loc();
var item = try p.parseExprOrBindings(.comma, &errors);
try items_list.ensureUnusedCapacity(1);
const item: *Expr = &items_list.unusedCapacitySlice()[0];
try p.parseExprOrBindings(.comma, &errors, item);
items_list.items.len += 1;
if (is_spread) {
item = p.newExpr(E.Spread{ .value = item }, loc);
item.* = p.newExpr(E.Spread{ .value = item.* }, loc);
}
// Skip over types
@@ -359,11 +364,9 @@ pub fn Parse(
// There may be a "=" after the type (but not after an "as" cast)
if (is_typescript_enabled and p.lexer.token == .t_equals and !p.forbid_suffix_after_as_loc.eql(p.lexer.loc())) {
try p.lexer.next();
item = Expr.assign(item, try p.parseExpr(.comma));
item.* = Expr.assign(item.*, try p.parseExpr(.comma));
}
items_list.append(item) catch unreachable;
if (p.lexer.token != .t_comma) {
break;
}
@@ -675,7 +678,7 @@ pub fn Parse(
try p.lexer.next();
const raw2 = p.lexer.raw();
const value = if (p.lexer.token == .t_identifier and strings.eqlComptime(raw2, "using")) value: {
var value = if (p.lexer.token == .t_identifier and strings.eqlComptime(raw2, "using")) value: {
// const using_loc = p.saveExprCommentsHere();
const using_range = p.lexer.range();
try p.lexer.next();
@@ -711,13 +714,15 @@ pub fn Parse(
if (p.lexer.token == .t_asterisk_asterisk) {
try p.lexer.unexpected();
}
const expr = p.newExpr(
E.Await{ .value = try p.parseSuffix(value, .prefix, null, .none) },
try p.parseSuffix(&value, .prefix, null, .none);
var expr = p.newExpr(
E.Await{ .value = value },
token_range.loc,
);
try p.parseSuffix(&expr, .lowest, null, .none);
return ExprOrLetStmt{
.stmt_or_expr = js_ast.StmtOrExpr{
.expr = try p.parseSuffix(expr, .lowest, null, .none),
.expr = expr,
},
};
} else {
@@ -730,12 +735,13 @@ pub fn Parse(
// Parse the remainder of this expression that starts with an identifier
const ref = try p.storeNameInRef(raw);
const expr = p.newExpr(E.Identifier{ .ref = ref }, token_range.loc);
return ExprOrLetStmt{
var result = ExprOrLetStmt{
.stmt_or_expr = js_ast.StmtOrExpr{
.expr = try p.parseSuffix(expr, .lowest, null, .none),
.expr = p.newExpr(E.Identifier{ .ref = ref }, token_range.loc),
},
};
try p.parseSuffix(&result.stmt_or_expr.expr, .lowest, null, .none);
return result;
}
pub fn parseBinding(p: *P, comptime opts: ParseBindingOptions) anyerror!Binding {

View File

@@ -381,8 +381,14 @@ pub fn ParsePrefix(
return p.newExpr(E.NewTarget{ .range = range }, loc);
}
const target = try p.parseExprWithFlags(.member, flags);
var args = ExprNodeList{};
// This wil become the new expr
var new = p.newExpr(E.New{
.target = undefined,
.args = undefined,
.close_parens_loc = undefined,
}, loc);
try p.parseExprWithFlags(.member, flags, &new.data.e_new.target);
if (comptime is_typescript_enabled) {
// Skip over TypeScript type arguments here if there are any
@@ -391,18 +397,15 @@ pub fn ParsePrefix(
}
}
var close_parens_loc = logger.Loc.Empty;
if (p.lexer.token == .t_open_paren) {
const call_args = try p.parseCallArgs();
args = call_args.list;
close_parens_loc = call_args.loc;
new.data.e_new.args = call_args.list;
new.data.e_new.close_parens_loc = call_args.loc;
} else {
new.data.e_new.close_parens_loc = .Empty;
}
return p.newExpr(E.New{
.target = target,
.args = args,
.close_parens_loc = close_parens_loc,
}, loc);
return new;
},
.t_open_bracket => {
try p.lexer.next();
@@ -426,9 +429,11 @@ pub fn ParsePrefix(
const dots_loc = p.lexer.loc();
try p.lexer.next();
items.append(
p.newExpr(E.Spread{ .value = try p.parseExprOrBindings(.comma, &self_errors) }, dots_loc),
) catch unreachable;
try items.ensureUnusedCapacity(1);
const spread_expr: *Expr = &items.unusedCapacitySlice()[0];
spread_expr.* = p.newExpr(E.Spread{ .value = undefined }, dots_loc);
try p.parseExprOrBindings(.comma, &self_errors, &spread_expr.data.e_spread.value);
items.items.len += 1;
// Commas are not allowed here when destructuring
if (p.lexer.token == .t_comma) {
@@ -436,9 +441,10 @@ pub fn ParsePrefix(
}
},
else => {
items.append(
try p.parseExprOrBindings(.comma, &self_errors),
) catch unreachable;
try items.ensureUnusedCapacity(1);
const item: *Expr = &items.unusedCapacitySlice()[0];
try p.parseExprOrBindings(.comma, &self_errors, item);
items.items.len += 1;
},
}
@@ -496,7 +502,19 @@ pub fn ParsePrefix(
while (p.lexer.token != .t_close_brace) {
if (p.lexer.token == .t_dot_dot_dot) {
try p.lexer.next();
properties.append(G.Property{ .kind = .spread, .value = try p.parseExpr(.comma) }) catch unreachable;
try properties.ensureUnusedCapacity(1);
const property: *G.Property = &properties.unusedCapacitySlice()[0];
property.* = .{
.kind = .spread,
.value = Expr.empty,
};
try p.parseExprOrBindings(
.comma,
&self_errors,
&(property.value.?),
);
properties.items.len += 1;
// Commas are not allowed here when destructuring
if (p.lexer.token == .t_comma) {

View File

@@ -508,16 +508,17 @@ pub fn ParseProperty(
// Parse an object key/value pair
try p.lexer.expect(.t_colon);
const value = try p.parseExprOrBindings(.comma, errors);
return G.Property{
var property: G.Property = .{
.kind = kind,
.flags = Flags.Property.init(.{
.is_computed = is_computed,
}),
.key = key,
.value = value,
.value = Expr{ .data = .e_missing, .loc = .{} },
};
try p.parseExprOrBindings(.comma, errors, &property.value.?);
return property;
}
};
}

View File

@@ -185,8 +185,8 @@ pub fn ParseStmt(
const defaultName = try createDefaultName(p, loc);
const prefix_expr = try p.parseAsyncPrefixExpr(async_range, Level.comma);
const expr = try p.parseSuffix(prefix_expr, Level.comma, null, Expr.EFlags.none);
var expr = try p.parseAsyncPrefixExpr(async_range, Level.comma);
try p.parseSuffix(&expr, Level.comma, null, Expr.EFlags.none);
try p.lexer.expectOrInsertSemicolon();
const value = js_ast.StmtOrExpr{ .expr = expr };
p.has_export_default = true;
@@ -926,7 +926,8 @@ pub fn ParseStmt(
// "import.meta"
.t_open_paren, .t_dot => {
p.esm_import_keyword = previous_import_keyword; // this wasn't an esm import statement after all
const expr = try p.parseSuffix(try p.parseImportExpr(loc, .lowest), .lowest, null, Expr.EFlags.none);
var expr = try p.parseImportExpr(loc, .lowest);
try p.parseSuffix(&expr, .lowest, null, Expr.EFlags.none);
try p.lexer.expectOrInsertSemicolon();
return p.s(S.SExpr{
.value = expr,
@@ -1164,7 +1165,8 @@ pub fn ParseStmt(
return try p.parseFnStmt(async_range.loc, opts, async_range);
}
expr = try p.parseSuffix(try p.parseAsyncPrefixExpr(async_range, .lowest), .lowest, null, Expr.EFlags.none);
expr = try p.parseAsyncPrefixExpr(async_range, .lowest);
try p.parseSuffix(&expr, .lowest, null, Expr.EFlags.none);
} else {
const exprOrLet = try p.parseExprOrLetStmt(opts);
switch (exprOrLet.stmt_or_expr) {

File diff suppressed because it is too large Load Diff

View File

@@ -24,7 +24,9 @@ pub fn ParseTypescript(
// }
//
// This matches the behavior of the TypeScript compiler.
try decorators.append(try p.parseExprWithFlags(.new, Expr.EFlags.ts_decorator));
try decorators.ensureUnusedCapacity(1);
try p.parseExprWithFlags(.new, Expr.EFlags.ts_decorator, &decorators.unusedCapacitySlice()[0]);
decorators.items.len += 1;
}
return decorators.items;