diff --git a/src/ast/parsePrefix.zig b/src/ast/parsePrefix.zig
index f9c282d3f7..22acda032d 100644
--- a/src/ast/parsePrefix.zig
+++ b/src/ast/parsePrefix.zig
@@ -8,664 +8,717 @@ pub fn ParsePrefix(
const is_jsx_enabled = P.is_jsx_enabled;
const is_typescript_enabled = P.is_typescript_enabled;
- pub fn parsePrefix(noalias p: *P, level: Level, noalias errors: ?*DeferredErrors, flags: Expr.EFlags) anyerror!Expr {
+ fn t_super(noalias p: *P, level: Level) anyerror!Expr {
const loc = p.lexer.loc();
const l = @intFromEnum(level);
- // Output.print("Parse Prefix {s}:{s} @{s} ", .{ p.lexer.token, p.lexer.raw(), @tagName(level) });
+ const superRange = p.lexer.range();
+ try p.lexer.next();
switch (p.lexer.token) {
- .t_super => {
- const superRange = p.lexer.range();
- try p.lexer.next();
-
- switch (p.lexer.token) {
- .t_open_paren => {
- if (l < @intFromEnum(Level.call) and p.fn_or_arrow_data_parse.allow_super_call) {
- return p.newExpr(E.Super{}, loc);
- }
- },
- .t_dot, .t_open_bracket => {
- if (p.fn_or_arrow_data_parse.allow_super_property) {
- return p.newExpr(E.Super{}, loc);
- }
- },
- else => {},
- }
-
- p.log.addRangeError(p.source, superRange, "Unexpected \"super\"") catch unreachable;
- return p.newExpr(E.Super{}, loc);
- },
.t_open_paren => {
- try p.lexer.next();
-
- // Arrow functions aren't allowed in the middle of expressions
- if (level.gt(.assign)) {
- // Allow "in" inside parentheses
- const oldAllowIn = p.allow_in;
- p.allow_in = true;
-
- var value = try p.parseExpr(Level.lowest);
- p.markExprAsParenthesized(&value);
- try p.lexer.expect(.t_close_paren);
-
- p.allow_in = oldAllowIn;
- return value;
+ if (l < @intFromEnum(Level.call) and p.fn_or_arrow_data_parse.allow_super_call) {
+ return p.newExpr(E.Super{}, loc);
}
-
- return p.parseParenExpr(loc, level, ParenExprOpts{});
},
- .t_false => {
- try p.lexer.next();
- return p.newExpr(E.Boolean{ .value = false }, loc);
- },
- .t_true => {
- try p.lexer.next();
- return p.newExpr(E.Boolean{ .value = true }, loc);
- },
- .t_null => {
- try p.lexer.next();
- return p.newExpr(E.Null{}, loc);
- },
- .t_this => {
- if (p.fn_or_arrow_data_parse.is_this_disallowed) {
- p.log.addRangeError(p.source, p.lexer.range(), "Cannot use \"this\" here") catch unreachable;
+ .t_dot, .t_open_bracket => {
+ if (p.fn_or_arrow_data_parse.allow_super_property) {
+ return p.newExpr(E.Super{}, loc);
}
- try p.lexer.next();
- return Expr{ .data = Prefill.Data.This, .loc = loc };
- },
- .t_private_identifier => {
- if (!p.allow_private_identifiers or !p.allow_in or level.gte(.compare)) {
- try p.lexer.unexpected();
- return error.SyntaxError;
- }
-
- const name = p.lexer.identifier;
- try p.lexer.next();
-
- // Check for "#foo in bar"
- if (p.lexer.token != .t_in) {
- try p.lexer.expected(.t_in);
- }
-
- return p.newExpr(E.PrivateIdentifier{ .ref = try p.storeNameInRef(name) }, loc);
- },
- .t_identifier => {
- const name = p.lexer.identifier;
- const name_range = p.lexer.range();
- const raw = p.lexer.raw();
-
- try p.lexer.next();
-
- // Handle async and await expressions
- switch (AsyncPrefixExpression.find(name)) {
- .is_async => {
- if ((raw.ptr == name.ptr and raw.len == name.len) or AsyncPrefixExpression.find(raw) == .is_async) {
- return try p.parseAsyncPrefixExpr(name_range, level);
- }
- },
-
- .is_await => {
- switch (p.fn_or_arrow_data_parse.allow_await) {
- .forbid_all => {
- p.log.addRangeError(p.source, name_range, "The keyword \"await\" cannot be used here") catch unreachable;
- },
- .allow_expr => {
- if (AsyncPrefixExpression.find(raw) != .is_await) {
- p.log.addRangeError(p.source, name_range, "The keyword \"await\" cannot be escaped") catch unreachable;
- } else {
- if (p.fn_or_arrow_data_parse.is_top_level) {
- p.top_level_await_keyword = name_range;
- }
-
- if (p.fn_or_arrow_data_parse.track_arrow_arg_errors) {
- p.fn_or_arrow_data_parse.arrow_arg_errors.invalid_expr_await = name_range;
- }
-
- const value = try p.parseExpr(.prefix);
- if (p.lexer.token == T.t_asterisk_asterisk) {
- try p.lexer.unexpected();
- return error.SyntaxError;
- }
-
- return p.newExpr(E.Await{ .value = value }, loc);
- }
- },
- .allow_ident => {
- p.lexer.prev_token_was_await_keyword = true;
- p.lexer.await_keyword_loc = name_range.loc;
- p.lexer.fn_or_arrow_start_loc = p.fn_or_arrow_data_parse.needs_async_loc;
- },
- }
- },
-
- .is_yield => {
- switch (p.fn_or_arrow_data_parse.allow_yield) {
- .forbid_all => {
- p.log.addRangeError(p.source, name_range, "The keyword \"yield\" cannot be used here") catch unreachable;
- },
- .allow_expr => {
- if (AsyncPrefixExpression.find(raw) != .is_yield) {
- p.log.addRangeError(p.source, name_range, "The keyword \"yield\" cannot be escaped") catch unreachable;
- } else {
- if (level.gt(.assign)) {
- p.log.addRangeError(p.source, name_range, "Cannot use a \"yield\" here without parentheses") catch unreachable;
- }
-
- if (p.fn_or_arrow_data_parse.track_arrow_arg_errors) {
- p.fn_or_arrow_data_parse.arrow_arg_errors.invalid_expr_yield = name_range;
- }
-
- return p.parseYieldExpr(loc);
- }
- },
- // .allow_ident => {
-
- // },
- else => {
- // Try to gracefully recover if "yield" is used in the wrong place
- if (!p.lexer.has_newline_before) {
- switch (p.lexer.token) {
- .t_null, .t_identifier, .t_false, .t_true, .t_numeric_literal, .t_big_integer_literal, .t_string_literal => {
- p.log.addRangeError(p.source, name_range, "Cannot use \"yield\" outside a generator function") catch unreachable;
- },
- else => {},
- }
- }
- },
- }
- },
- .none => {},
- }
-
- // Handle the start of an arrow expression
- if (p.lexer.token == .t_equals_greater_than and level.lte(.assign)) {
- const ref = p.storeNameInRef(name) catch unreachable;
- var args = p.allocator.alloc(Arg, 1) catch unreachable;
- args[0] = Arg{ .binding = p.b(B.Identifier{
- .ref = ref,
- }, loc) };
-
- _ = p.pushScopeForParsePass(.function_args, loc) catch unreachable;
- defer p.popScope();
-
- var fn_or_arrow_data = FnOrArrowDataParse{
- .needs_async_loc = loc,
- };
- return p.newExpr(try p.parseArrowBody(args, &fn_or_arrow_data), loc);
- }
-
- const ref = p.storeNameInRef(name) catch unreachable;
-
- return Expr.initIdentifier(ref, loc);
- },
- .t_string_literal, .t_no_substitution_template_literal => {
- return try p.parseStringLiteral();
- },
- .t_template_head => {
- const head = try p.lexer.toEString();
-
- const parts = try p.parseTemplateParts(false);
-
- // Check if TemplateLiteral is unsupported. We don't care for this product.`
- // if ()
-
- return p.newExpr(E.Template{
- .head = .{ .cooked = head },
- .parts = parts,
- }, loc);
- },
- .t_numeric_literal => {
- const value = p.newExpr(E.Number{ .value = p.lexer.number }, loc);
- // p.checkForLegacyOctalLiteral()
- try p.lexer.next();
- return value;
- },
- .t_big_integer_literal => {
- const value = p.lexer.identifier;
- // markSyntaxFeature bigInt
- try p.lexer.next();
- return p.newExpr(E.BigInt{ .value = value }, loc);
- },
- .t_slash, .t_slash_equals => {
- try p.lexer.scanRegExp();
- // always set regex_flags_start to null to make sure we don't accidentally use the wrong value later
- defer p.lexer.regex_flags_start = null;
- const value = p.lexer.raw();
- try p.lexer.next();
-
- return p.newExpr(E.RegExp{ .value = value, .flags_offset = p.lexer.regex_flags_start }, loc);
- },
- .t_void => {
- try p.lexer.next();
- const value = try p.parseExpr(.prefix);
- if (p.lexer.token == .t_asterisk_asterisk) {
- try p.lexer.unexpected();
- return error.SyntaxError;
- }
-
- return p.newExpr(E.Unary{
- .op = .un_void,
- .value = value,
- }, loc);
- },
- .t_typeof => {
- try p.lexer.next();
- const value = try p.parseExpr(.prefix);
- if (p.lexer.token == .t_asterisk_asterisk) {
- try p.lexer.unexpected();
- return error.SyntaxError;
- }
-
- return p.newExpr(E.Unary{ .op = .un_typeof, .value = value }, loc);
- },
- .t_delete => {
- try p.lexer.next();
- const value = try p.parseExpr(.prefix);
- if (p.lexer.token == .t_asterisk_asterisk) {
- try p.lexer.unexpected();
- return error.SyntaxError;
- }
- if (value.data == .e_index) {
- if (value.data.e_index.index.data == .e_private_identifier) {
- const private = value.data.e_index.index.data.e_private_identifier;
- const name = p.loadNameFromRef(private.ref);
- const range = logger.Range{ .loc = value.loc, .len = @as(i32, @intCast(name.len)) };
- p.log.addRangeErrorFmt(p.source, range, p.allocator, "Deleting the private name \"{s}\" is forbidden", .{name}) catch unreachable;
- }
- }
-
- return p.newExpr(E.Unary{ .op = .un_delete, .value = value }, loc);
- },
- .t_plus => {
- try p.lexer.next();
- const value = try p.parseExpr(.prefix);
- if (p.lexer.token == .t_asterisk_asterisk) {
- try p.lexer.unexpected();
- return error.SyntaxError;
- }
-
- return p.newExpr(E.Unary{ .op = .un_pos, .value = value }, loc);
- },
- .t_minus => {
- try p.lexer.next();
- const value = try p.parseExpr(.prefix);
- if (p.lexer.token == .t_asterisk_asterisk) {
- try p.lexer.unexpected();
- return error.SyntaxError;
- }
-
- return p.newExpr(E.Unary{ .op = .un_neg, .value = value }, loc);
- },
- .t_tilde => {
- try p.lexer.next();
- const value = try p.parseExpr(.prefix);
- if (p.lexer.token == .t_asterisk_asterisk) {
- try p.lexer.unexpected();
- return error.SyntaxError;
- }
-
- return p.newExpr(E.Unary{ .op = .un_cpl, .value = value }, loc);
- },
- .t_exclamation => {
- try p.lexer.next();
- const value = try p.parseExpr(.prefix);
- if (p.lexer.token == .t_asterisk_asterisk) {
- try p.lexer.unexpected();
- return error.SyntaxError;
- }
-
- return p.newExpr(E.Unary{ .op = .un_not, .value = value }, loc);
- },
- .t_minus_minus => {
- try p.lexer.next();
- return p.newExpr(E.Unary{ .op = .un_pre_dec, .value = try p.parseExpr(.prefix) }, loc);
- },
- .t_plus_plus => {
- try p.lexer.next();
- return p.newExpr(E.Unary{ .op = .un_pre_inc, .value = try p.parseExpr(.prefix) }, loc);
- },
- .t_function => {
- return try p.parseFnExpr(loc, false, logger.Range.None);
- },
- .t_class => {
- const classKeyword = p.lexer.range();
- // markSyntaxFEatuer class
- try p.lexer.next();
- var name: ?js_ast.LocRef = null;
-
- _ = p.pushScopeForParsePass(.class_name, loc) catch unreachable;
-
- // Parse an optional class name
- if (p.lexer.token == .t_identifier) {
- const name_text = p.lexer.identifier;
- if (!is_typescript_enabled or !strings.eqlComptime(name_text, "implements")) {
- if (p.fn_or_arrow_data_parse.allow_await != .allow_ident and strings.eqlComptime(name_text, "await")) {
- p.log.addRangeError(p.source, p.lexer.range(), "Cannot use \"await\" as an identifier here") catch unreachable;
- }
-
- name = js_ast.LocRef{
- .loc = p.lexer.loc(),
- .ref = p.newSymbol(
- .other,
- name_text,
- ) catch unreachable,
- };
- try p.lexer.next();
- }
- }
-
- // Even anonymous classes can have TypeScript type parameters
- if (is_typescript_enabled) {
- _ = try p.skipTypeScriptTypeParameters(.{ .allow_in_out_variance_annotations = true, .allow_const_modifier = true });
- }
-
- const class = try p.parseClass(classKeyword, name, ParseClassOptions{});
- p.popScope();
-
- return p.newExpr(class, loc);
- },
- .t_new => {
- try p.lexer.next();
-
- // Special-case the weird "new.target" expression here
- if (p.lexer.token == .t_dot) {
- try p.lexer.next();
-
- if (p.lexer.token != .t_identifier or !strings.eqlComptime(p.lexer.raw(), "target")) {
- try p.lexer.unexpected();
- return error.SyntaxError;
- }
- const range = logger.Range{ .loc = loc, .len = p.lexer.range().end().start - loc.start };
-
- try p.lexer.next();
- return p.newExpr(E.NewTarget{ .range = range }, loc);
- }
-
- // 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
- if (p.lexer.token == .t_less_than) {
- _ = p.trySkipTypeScriptTypeArgumentsWithBacktracking();
- }
- }
-
- if (p.lexer.token == .t_open_paren) {
- const call_args = try p.parseCallArgs();
- 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 new;
- },
- .t_open_bracket => {
- try p.lexer.next();
- var is_single_line = !p.lexer.has_newline_before;
- var items = ListManaged(Expr).init(p.allocator);
- var self_errors = DeferredErrors{};
- var comma_after_spread = logger.Loc{};
-
- // Allow "in" inside arrays
- const old_allow_in = p.allow_in;
- p.allow_in = true;
-
- while (p.lexer.token != .t_close_bracket) {
- switch (p.lexer.token) {
- .t_comma => {
- items.append(Expr{ .data = Prefill.Data.EMissing, .loc = p.lexer.loc() }) catch unreachable;
- },
- .t_dot_dot_dot => {
- if (errors != null)
- errors.?.array_spread_feature = p.lexer.range();
-
- const dots_loc = p.lexer.loc();
- try p.lexer.next();
- 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) {
- comma_after_spread = p.lexer.loc();
- }
- },
- else => {
- try items.ensureUnusedCapacity(1);
- const item: *Expr = &items.unusedCapacitySlice()[0];
- try p.parseExprOrBindings(.comma, &self_errors, item);
- items.items.len += 1;
- },
- }
-
- if (p.lexer.token != .t_comma) {
- break;
- }
-
- if (p.lexer.has_newline_before) {
- is_single_line = false;
- }
-
- try p.lexer.next();
-
- if (p.lexer.has_newline_before) {
- is_single_line = false;
- }
- }
-
- if (p.lexer.has_newline_before) {
- is_single_line = false;
- }
-
- const close_bracket_loc = p.lexer.loc();
- try p.lexer.expect(.t_close_bracket);
- p.allow_in = old_allow_in;
-
- // Is this a binding pattern?
- if (p.willNeedBindingPattern()) {
- // noop
- } else if (errors == null) {
- // Is this an expression?
- p.logExprErrors(&self_errors);
- } else {
- // In this case, we can't distinguish between the two yet
- self_errors.mergeInto(errors.?);
- }
- return p.newExpr(E.Array{
- .items = ExprNodeList.fromList(items),
- .comma_after_spread = comma_after_spread.toNullable(),
- .is_single_line = is_single_line,
- .close_bracket_loc = close_bracket_loc,
- }, loc);
- },
- .t_open_brace => {
- try p.lexer.next();
- var is_single_line = !p.lexer.has_newline_before;
- var properties = ListManaged(G.Property).init(p.allocator);
- var self_errors = DeferredErrors{};
- var comma_after_spread: logger.Loc = logger.Loc{};
-
- // Allow "in" inside object literals
- const old_allow_in = p.allow_in;
- p.allow_in = true;
-
- while (p.lexer.token != .t_close_brace) {
- if (p.lexer.token == .t_dot_dot_dot) {
- try p.lexer.next();
- 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) {
- comma_after_spread = p.lexer.loc();
- }
- } else {
- // This property may turn out to be a type in TypeScript, which should be ignored
- var propertyOpts = PropertyOpts{};
- if (try p.parseProperty(.normal, &propertyOpts, &self_errors)) |prop| {
- if (comptime Environment.allow_assert) {
- assert(prop.key != null or prop.value != null);
- }
- properties.append(prop) catch unreachable;
- }
- }
-
- if (p.lexer.token != .t_comma) {
- break;
- }
-
- if (p.lexer.has_newline_before) {
- is_single_line = false;
- }
-
- try p.lexer.next();
-
- if (p.lexer.has_newline_before) {
- is_single_line = false;
- }
- }
-
- if (p.lexer.has_newline_before) {
- is_single_line = false;
- }
-
- const close_brace_loc = p.lexer.loc();
- try p.lexer.expect(.t_close_brace);
- p.allow_in = old_allow_in;
-
- if (p.willNeedBindingPattern()) {
- // Is this a binding pattern?
- } else if (errors == null) {
- // Is this an expression?
- p.logExprErrors(&self_errors);
- } else {
- // In this case, we can't distinguish between the two yet
- self_errors.mergeInto(errors.?);
- }
-
- return p.newExpr(E.Object{
- .properties = G.Property.List.fromList(properties),
- .comma_after_spread = if (comma_after_spread.start > 0)
- comma_after_spread
- else
- null,
- .is_single_line = is_single_line,
- .close_brace_loc = close_brace_loc,
- }, loc);
- },
- .t_less_than => {
- // This is a very complicated and highly ambiguous area of TypeScript
- // syntax. Many similar-looking things are overloaded.
- //
- // TS:
- //
- // A type cast:
- // (x)
- // <[]>(x)
- // (x)
- //
- // An arrow function with type parameters:
- // (x) => {}
- // (x) => {}
- // (x) => {}
- // (x) => {}
- //
- // TSX:
- //
- // A JSX element:
- // (x) => {}
- // (x) => {}
- // (x) => {}
- //
- // An arrow function with type parameters:
- // (x) => {}
- // (x) => {}
- //
- // A syntax error:
- // <[]>(x)
- // (x)
- // (x) => {}
- // (x) => {}
- if (comptime is_typescript_enabled and is_jsx_enabled) {
- if (try TypeScript.isTSArrowFnJSX(p)) {
- _ = try p.skipTypeScriptTypeParameters(TypeParameterFlag{
- .allow_const_modifier = true,
- });
- try p.lexer.expect(.t_open_paren);
- return try p.parseParenExpr(loc, level, ParenExprOpts{ .force_arrow_fn = true });
- }
- }
-
- if (is_jsx_enabled) {
- // Use NextInsideJSXElement() instead of Next() so we parse "<<" as "<"
- try p.lexer.nextInsideJSXElement();
- const element = try p.parseJSXElement(loc);
-
- // The call to parseJSXElement() above doesn't consume the last
- // TGreaterThan because the caller knows what Next() function to call.
- // Use Next() instead of NextInsideJSXElement() here since the next
- // token is an expression.
- try p.lexer.next();
- return element;
- }
-
- if (is_typescript_enabled) {
- // This is either an old-style type cast or a generic lambda function
-
- // "(x)"
- // "(x) => {}"
- switch (p.trySkipTypeScriptTypeParametersThenOpenParenWithBacktracking()) {
- .did_not_skip_anything => {},
- else => |result| {
- try p.lexer.expect(.t_open_paren);
- return p.parseParenExpr(loc, level, ParenExprOpts{
- .force_arrow_fn = result == .definitely_type_parameters,
- });
- },
- }
-
- // "x"
- try p.lexer.next();
- try p.skipTypeScriptType(.lowest);
- try p.lexer.expectGreaterThan(false);
- return p.parsePrefix(level, errors, flags);
- }
-
- try p.lexer.unexpected();
- return error.SyntaxError;
- },
- .t_import => {
- try p.lexer.next();
- return p.parseImportExpr(loc, level);
- },
- else => {
- try p.lexer.unexpected();
- return error.SyntaxError;
},
+ else => {},
}
+
+ p.log.addRangeError(p.source, superRange, "Unexpected \"super\"") catch unreachable;
+ return p.newExpr(E.Super{}, loc);
+ }
+ fn t_open_paren(noalias p: *P, level: Level) anyerror!Expr {
+ const loc = p.lexer.loc();
+ try p.lexer.next();
+
+ // Arrow functions aren't allowed in the middle of expressions
+ if (level.gt(.assign)) {
+ // Allow "in" inside parentheses
+ const oldAllowIn = p.allow_in;
+ p.allow_in = true;
+
+ var value = try p.parseExpr(Level.lowest);
+ p.markExprAsParenthesized(&value);
+ try p.lexer.expect(.t_close_paren);
+
+ p.allow_in = oldAllowIn;
+ return value;
+ }
+
+ return p.parseParenExpr(loc, level, ParenExprOpts{});
+ }
+ fn t_false(noalias p: *P) anyerror!Expr {
+ const loc = p.lexer.loc();
+ try p.lexer.next();
+ return p.newExpr(E.Boolean{ .value = false }, loc);
+ }
+ fn t_true(noalias p: *P) anyerror!Expr {
+ const loc = p.lexer.loc();
+ try p.lexer.next();
+ return p.newExpr(E.Boolean{ .value = true }, loc);
+ }
+ fn t_null(noalias p: *P) anyerror!Expr {
+ const loc = p.lexer.loc();
+ try p.lexer.next();
+ return p.newExpr(E.Null{}, loc);
+ }
+ fn t_this(noalias p: *P) anyerror!Expr {
+ const loc = p.lexer.loc();
+ if (p.fn_or_arrow_data_parse.is_this_disallowed) {
+ p.log.addRangeError(p.source, p.lexer.range(), "Cannot use \"this\" here") catch unreachable;
+ }
+ try p.lexer.next();
+ return Expr{ .data = Prefill.Data.This, .loc = loc };
+ }
+ fn t_private_identifier(noalias p: *P, level: Level) anyerror!Expr {
+ const loc = p.lexer.loc();
+ if (!p.allow_private_identifiers or !p.allow_in or level.gte(.compare)) {
+ try p.lexer.unexpected();
+ return error.SyntaxError;
+ }
+
+ const name = p.lexer.identifier;
+ try p.lexer.next();
+
+ // Check for "#foo in bar"
+ if (p.lexer.token != .t_in) {
+ try p.lexer.expected(.t_in);
+ }
+
+ return p.newExpr(E.PrivateIdentifier{ .ref = try p.storeNameInRef(name) }, loc);
+ }
+ fn t_identifier(noalias p: *P, level: Level) anyerror!Expr {
+ const loc = p.lexer.loc();
+ const name = p.lexer.identifier;
+ const name_range = p.lexer.range();
+ const raw = p.lexer.raw();
+
+ try p.lexer.next();
+
+ // Handle async and await expressions
+ switch (AsyncPrefixExpression.find(name)) {
+ .is_async => {
+ if ((raw.ptr == name.ptr and raw.len == name.len) or AsyncPrefixExpression.find(raw) == .is_async) {
+ return try p.parseAsyncPrefixExpr(name_range, level);
+ }
+ },
+
+ .is_await => {
+ switch (p.fn_or_arrow_data_parse.allow_await) {
+ .forbid_all => {
+ p.log.addRangeError(p.source, name_range, "The keyword \"await\" cannot be used here") catch unreachable;
+ },
+ .allow_expr => {
+ if (AsyncPrefixExpression.find(raw) != .is_await) {
+ p.log.addRangeError(p.source, name_range, "The keyword \"await\" cannot be escaped") catch unreachable;
+ } else {
+ if (p.fn_or_arrow_data_parse.is_top_level) {
+ p.top_level_await_keyword = name_range;
+ }
+
+ if (p.fn_or_arrow_data_parse.track_arrow_arg_errors) {
+ p.fn_or_arrow_data_parse.arrow_arg_errors.invalid_expr_await = name_range;
+ }
+
+ const value = try p.parseExpr(.prefix);
+ if (p.lexer.token == T.t_asterisk_asterisk) {
+ try p.lexer.unexpected();
+ return error.SyntaxError;
+ }
+
+ return p.newExpr(E.Await{ .value = value }, loc);
+ }
+ },
+ .allow_ident => {
+ p.lexer.prev_token_was_await_keyword = true;
+ p.lexer.await_keyword_loc = name_range.loc;
+ p.lexer.fn_or_arrow_start_loc = p.fn_or_arrow_data_parse.needs_async_loc;
+ },
+ }
+ },
+
+ .is_yield => {
+ switch (p.fn_or_arrow_data_parse.allow_yield) {
+ .forbid_all => {
+ p.log.addRangeError(p.source, name_range, "The keyword \"yield\" cannot be used here") catch unreachable;
+ },
+ .allow_expr => {
+ if (AsyncPrefixExpression.find(raw) != .is_yield) {
+ p.log.addRangeError(p.source, name_range, "The keyword \"yield\" cannot be escaped") catch unreachable;
+ } else {
+ if (level.gt(.assign)) {
+ p.log.addRangeError(p.source, name_range, "Cannot use a \"yield\" here without parentheses") catch unreachable;
+ }
+
+ if (p.fn_or_arrow_data_parse.track_arrow_arg_errors) {
+ p.fn_or_arrow_data_parse.arrow_arg_errors.invalid_expr_yield = name_range;
+ }
+
+ return p.parseYieldExpr(loc);
+ }
+ },
+ // .allow_ident => {
+
+ // },
+ else => {
+ // Try to gracefully recover if "yield" is used in the wrong place
+ if (!p.lexer.has_newline_before) {
+ switch (p.lexer.token) {
+ .t_null, .t_identifier, .t_false, .t_true, .t_numeric_literal, .t_big_integer_literal, .t_string_literal => {
+ p.log.addRangeError(p.source, name_range, "Cannot use \"yield\" outside a generator function") catch unreachable;
+ },
+ else => {},
+ }
+ }
+ },
+ }
+ },
+ .none => {},
+ }
+
+ // Handle the start of an arrow expression
+ if (p.lexer.token == .t_equals_greater_than and level.lte(.assign)) {
+ const ref = p.storeNameInRef(name) catch unreachable;
+ var args = p.allocator.alloc(Arg, 1) catch unreachable;
+ args[0] = Arg{ .binding = p.b(B.Identifier{
+ .ref = ref,
+ }, loc) };
+
+ _ = p.pushScopeForParsePass(.function_args, loc) catch unreachable;
+ defer p.popScope();
+
+ var fn_or_arrow_data = FnOrArrowDataParse{
+ .needs_async_loc = loc,
+ };
+ return p.newExpr(try p.parseArrowBody(args, &fn_or_arrow_data), loc);
+ }
+
+ const ref = p.storeNameInRef(name) catch unreachable;
+
+ return Expr.initIdentifier(ref, loc);
+ }
+ fn t_template_head(noalias p: *P) anyerror!Expr {
+ const loc = p.lexer.loc();
+ const head = try p.lexer.toEString();
+
+ const parts = try p.parseTemplateParts(false);
+
+ // Check if TemplateLiteral is unsupported. We don't care for this product.`
+ // if ()
+
+ return p.newExpr(E.Template{
+ .head = .{ .cooked = head },
+ .parts = parts,
+ }, loc);
+ }
+ fn t_numeric_literal(noalias p: *P) anyerror!Expr {
+ const loc = p.lexer.loc();
+ const value = p.newExpr(E.Number{ .value = p.lexer.number }, loc);
+ // p.checkForLegacyOctalLiteral()
+ try p.lexer.next();
+ return value;
+ }
+ fn t_big_integer_literal(noalias p: *P) anyerror!Expr {
+ const loc = p.lexer.loc();
+ const value = p.lexer.identifier;
+ // markSyntaxFeature bigInt
+ try p.lexer.next();
+ return p.newExpr(E.BigInt{ .value = value }, loc);
+ }
+ fn t_slash(noalias p: *P) anyerror!Expr {
+ const loc = p.lexer.loc();
+ try p.lexer.scanRegExp();
+ // always set regex_flags_start to null to make sure we don't accidentally use the wrong value later
+ defer p.lexer.regex_flags_start = null;
+ const value = p.lexer.raw();
+ try p.lexer.next();
+
+ return p.newExpr(E.RegExp{ .value = value, .flags_offset = p.lexer.regex_flags_start }, loc);
+ }
+ fn t_void(noalias p: *P) anyerror!Expr {
+ const loc = p.lexer.loc();
+ try p.lexer.next();
+ const value = try p.parseExpr(.prefix);
+ if (p.lexer.token == .t_asterisk_asterisk) {
+ try p.lexer.unexpected();
+ return error.SyntaxError;
+ }
+
+ return p.newExpr(E.Unary{
+ .op = .un_void,
+ .value = value,
+ }, loc);
+ }
+ fn t_typeof(noalias p: *P) anyerror!Expr {
+ const loc = p.lexer.loc();
+ try p.lexer.next();
+ const value = try p.parseExpr(.prefix);
+ if (p.lexer.token == .t_asterisk_asterisk) {
+ try p.lexer.unexpected();
+ return error.SyntaxError;
+ }
+
+ return p.newExpr(E.Unary{ .op = .un_typeof, .value = value }, loc);
+ }
+ fn t_delete(noalias p: *P) anyerror!Expr {
+ const loc = p.lexer.loc();
+ try p.lexer.next();
+ const value = try p.parseExpr(.prefix);
+ if (p.lexer.token == .t_asterisk_asterisk) {
+ try p.lexer.unexpected();
+ return error.SyntaxError;
+ }
+ if (value.data == .e_index) {
+ if (value.data.e_index.index.data == .e_private_identifier) {
+ const private = value.data.e_index.index.data.e_private_identifier;
+ const name = p.loadNameFromRef(private.ref);
+ const range = logger.Range{ .loc = value.loc, .len = @as(i32, @intCast(name.len)) };
+ p.log.addRangeErrorFmt(p.source, range, p.allocator, "Deleting the private name \"{s}\" is forbidden", .{name}) catch unreachable;
+ }
+ }
+
+ return p.newExpr(E.Unary{ .op = .un_delete, .value = value }, loc);
+ }
+ fn t_plus(noalias p: *P) anyerror!Expr {
+ const loc = p.lexer.loc();
+ try p.lexer.next();
+ const value = try p.parseExpr(.prefix);
+ if (p.lexer.token == .t_asterisk_asterisk) {
+ try p.lexer.unexpected();
+ return error.SyntaxError;
+ }
+
+ return p.newExpr(E.Unary{ .op = .un_pos, .value = value }, loc);
+ }
+ fn t_minus(noalias p: *P) anyerror!Expr {
+ const loc = p.lexer.loc();
+ try p.lexer.next();
+ const value = try p.parseExpr(.prefix);
+ if (p.lexer.token == .t_asterisk_asterisk) {
+ try p.lexer.unexpected();
+ return error.SyntaxError;
+ }
+
+ return p.newExpr(E.Unary{ .op = .un_neg, .value = value }, loc);
+ }
+ fn t_tilde(noalias p: *P) anyerror!Expr {
+ const loc = p.lexer.loc();
+ try p.lexer.next();
+ const value = try p.parseExpr(.prefix);
+ if (p.lexer.token == .t_asterisk_asterisk) {
+ try p.lexer.unexpected();
+ return error.SyntaxError;
+ }
+
+ return p.newExpr(E.Unary{ .op = .un_cpl, .value = value }, loc);
+ }
+ fn t_exclamation(noalias p: *P) anyerror!Expr {
+ const loc = p.lexer.loc();
+ try p.lexer.next();
+ const value = try p.parseExpr(.prefix);
+ if (p.lexer.token == .t_asterisk_asterisk) {
+ try p.lexer.unexpected();
+ return error.SyntaxError;
+ }
+
+ return p.newExpr(E.Unary{ .op = .un_not, .value = value }, loc);
+ }
+ fn t_minus_minus(noalias p: *P) anyerror!Expr {
+ const loc = p.lexer.loc();
+ try p.lexer.next();
+ return p.newExpr(E.Unary{ .op = .un_pre_dec, .value = try p.parseExpr(.prefix) }, loc);
+ }
+ fn t_plus_plus(noalias p: *P) anyerror!Expr {
+ const loc = p.lexer.loc();
+ try p.lexer.next();
+ return p.newExpr(E.Unary{ .op = .un_pre_inc, .value = try p.parseExpr(.prefix) }, loc);
+ }
+ fn t_function(noalias p: *P) anyerror!Expr {
+ const loc = p.lexer.loc();
+ return try p.parseFnExpr(loc, false, logger.Range.None);
+ }
+ fn t_class(noalias p: *P) anyerror!Expr {
+ const loc = p.lexer.loc();
+ const classKeyword = p.lexer.range();
+ // markSyntaxFEatuer class
+ try p.lexer.next();
+ var name: ?js_ast.LocRef = null;
+
+ _ = p.pushScopeForParsePass(.class_name, loc) catch unreachable;
+
+ // Parse an optional class name
+ if (p.lexer.token == .t_identifier) {
+ const name_text = p.lexer.identifier;
+ if (!is_typescript_enabled or !strings.eqlComptime(name_text, "implements")) {
+ if (p.fn_or_arrow_data_parse.allow_await != .allow_ident and strings.eqlComptime(name_text, "await")) {
+ p.log.addRangeError(p.source, p.lexer.range(), "Cannot use \"await\" as an identifier here") catch unreachable;
+ }
+
+ name = js_ast.LocRef{
+ .loc = p.lexer.loc(),
+ .ref = p.newSymbol(
+ .other,
+ name_text,
+ ) catch unreachable,
+ };
+ try p.lexer.next();
+ }
+ }
+
+ // Even anonymous classes can have TypeScript type parameters
+ if (is_typescript_enabled) {
+ _ = try p.skipTypeScriptTypeParameters(.{ .allow_in_out_variance_annotations = true, .allow_const_modifier = true });
+ }
+
+ const class = try p.parseClass(classKeyword, name, ParseClassOptions{});
+ p.popScope();
+
+ return p.newExpr(class, loc);
+ }
+ fn t_new(noalias p: *P, flags: Expr.EFlags) anyerror!Expr {
+ const loc = p.lexer.loc();
+ try p.lexer.next();
+
+ // Special-case the weird "new.target" expression here
+ if (p.lexer.token == .t_dot) {
+ try p.lexer.next();
+
+ if (p.lexer.token != .t_identifier or !strings.eqlComptime(p.lexer.raw(), "target")) {
+ try p.lexer.unexpected();
+ return error.SyntaxError;
+ }
+ const range = logger.Range{ .loc = loc, .len = p.lexer.range().end().start - loc.start };
+
+ try p.lexer.next();
+ return p.newExpr(E.NewTarget{ .range = range }, loc);
+ }
+
+ // 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
+ if (p.lexer.token == .t_less_than) {
+ _ = p.trySkipTypeScriptTypeArgumentsWithBacktracking();
+ }
+ }
+
+ if (p.lexer.token == .t_open_paren) {
+ const call_args = try p.parseCallArgs();
+ 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 new;
+ }
+ fn t_open_bracket(noalias p: *P, noalias errors: ?*DeferredErrors) anyerror!Expr {
+ const loc = p.lexer.loc();
+ try p.lexer.next();
+ var is_single_line = !p.lexer.has_newline_before;
+ var items = ListManaged(Expr).init(p.allocator);
+ var self_errors = DeferredErrors{};
+ var comma_after_spread = logger.Loc{};
+
+ // Allow "in" inside arrays
+ const old_allow_in = p.allow_in;
+ p.allow_in = true;
+
+ while (p.lexer.token != .t_close_bracket) {
+ switch (p.lexer.token) {
+ .t_comma => {
+ items.append(Expr{ .data = Prefill.Data.EMissing, .loc = p.lexer.loc() }) catch unreachable;
+ },
+ .t_dot_dot_dot => {
+ if (errors != null)
+ errors.?.array_spread_feature = p.lexer.range();
+
+ const dots_loc = p.lexer.loc();
+ try p.lexer.next();
+ 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) {
+ comma_after_spread = p.lexer.loc();
+ }
+ },
+ else => {
+ try items.ensureUnusedCapacity(1);
+ const item: *Expr = &items.unusedCapacitySlice()[0];
+ try p.parseExprOrBindings(.comma, &self_errors, item);
+ items.items.len += 1;
+ },
+ }
+
+ if (p.lexer.token != .t_comma) {
+ break;
+ }
+
+ if (p.lexer.has_newline_before) {
+ is_single_line = false;
+ }
+
+ try p.lexer.next();
+
+ if (p.lexer.has_newline_before) {
+ is_single_line = false;
+ }
+ }
+
+ if (p.lexer.has_newline_before) {
+ is_single_line = false;
+ }
+
+ const close_bracket_loc = p.lexer.loc();
+ try p.lexer.expect(.t_close_bracket);
+ p.allow_in = old_allow_in;
+
+ // Is this a binding pattern?
+ if (p.willNeedBindingPattern()) {
+ // noop
+ } else if (errors == null) {
+ // Is this an expression?
+ p.logExprErrors(&self_errors);
+ } else {
+ // In this case, we can't distinguish between the two yet
+ self_errors.mergeInto(errors.?);
+ }
+ return p.newExpr(E.Array{
+ .items = ExprNodeList.fromList(items),
+ .comma_after_spread = comma_after_spread.toNullable(),
+ .is_single_line = is_single_line,
+ .close_bracket_loc = close_bracket_loc,
+ }, loc);
+ }
+ fn t_open_brace(noalias p: *P, noalias errors: ?*DeferredErrors) anyerror!Expr {
+ const loc = p.lexer.loc();
+ try p.lexer.next();
+ var is_single_line = !p.lexer.has_newline_before;
+ var properties = ListManaged(G.Property).init(p.allocator);
+ var self_errors = DeferredErrors{};
+ var comma_after_spread: logger.Loc = logger.Loc{};
+
+ // Allow "in" inside object literals
+ const old_allow_in = p.allow_in;
+ p.allow_in = true;
+
+ while (p.lexer.token != .t_close_brace) {
+ if (p.lexer.token == .t_dot_dot_dot) {
+ try p.lexer.next();
+ 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) {
+ comma_after_spread = p.lexer.loc();
+ }
+ } else {
+ // This property may turn out to be a type in TypeScript, which should be ignored
+ var propertyOpts = PropertyOpts{};
+ if (try p.parseProperty(.normal, &propertyOpts, &self_errors)) |prop| {
+ if (comptime Environment.allow_assert) {
+ assert(prop.key != null or prop.value != null);
+ }
+ properties.append(prop) catch unreachable;
+ }
+ }
+
+ if (p.lexer.token != .t_comma) {
+ break;
+ }
+
+ if (p.lexer.has_newline_before) {
+ is_single_line = false;
+ }
+
+ try p.lexer.next();
+
+ if (p.lexer.has_newline_before) {
+ is_single_line = false;
+ }
+ }
+
+ if (p.lexer.has_newline_before) {
+ is_single_line = false;
+ }
+
+ const close_brace_loc = p.lexer.loc();
+ try p.lexer.expect(.t_close_brace);
+ p.allow_in = old_allow_in;
+
+ if (p.willNeedBindingPattern()) {
+ // Is this a binding pattern?
+ } else if (errors == null) {
+ // Is this an expression?
+ p.logExprErrors(&self_errors);
+ } else {
+ // In this case, we can't distinguish between the two yet
+ self_errors.mergeInto(errors.?);
+ }
+
+ return p.newExpr(E.Object{
+ .properties = G.Property.List.fromList(properties),
+ .comma_after_spread = if (comma_after_spread.start > 0)
+ comma_after_spread
+ else
+ null,
+ .is_single_line = is_single_line,
+ .close_brace_loc = close_brace_loc,
+ }, loc);
+ }
+ fn t_less_than(noalias p: *P, level: Level, noalias errors: ?*DeferredErrors, flags: Expr.EFlags) anyerror!Expr {
+ const loc = p.lexer.loc();
+ // This is a very complicated and highly ambiguous area of TypeScript
+ // syntax. Many similar-looking things are overloaded.
+ //
+ // TS:
+ //
+ // A type cast:
+ // (x)
+ // <[]>(x)
+ // (x)
+ //
+ // An arrow function with type parameters:
+ // (x) => {}
+ // (x) => {}
+ // (x) => {}
+ // (x) => {}
+ //
+ // TSX:
+ //
+ // A JSX element:
+ // (x) => {}
+ // (x) => {}
+ // (x) => {}
+ //
+ // An arrow function with type parameters:
+ // (x) => {}
+ // (x) => {}
+ //
+ // A syntax error:
+ // <[]>(x)
+ // (x)
+ // (x) => {}
+ // (x) => {}
+ if (comptime is_typescript_enabled and is_jsx_enabled) {
+ if (try TypeScript.isTSArrowFnJSX(p)) {
+ _ = try p.skipTypeScriptTypeParameters(TypeParameterFlag{
+ .allow_const_modifier = true,
+ });
+ try p.lexer.expect(.t_open_paren);
+ return try p.parseParenExpr(loc, level, ParenExprOpts{ .force_arrow_fn = true });
+ }
+ }
+
+ if (is_jsx_enabled) {
+ // Use NextInsideJSXElement() instead of Next() so we parse "<<" as "<"
+ try p.lexer.nextInsideJSXElement();
+ const element = try p.parseJSXElement(loc);
+
+ // The call to parseJSXElement() above doesn't consume the last
+ // TGreaterThan because the caller knows what Next() function to call.
+ // Use Next() instead of NextInsideJSXElement() here since the next
+ // token is an expression.
+ try p.lexer.next();
+ return element;
+ }
+
+ if (is_typescript_enabled) {
+ // This is either an old-style type cast or a generic lambda function
+
+ // "(x)"
+ // "(x) => {}"
+ switch (p.trySkipTypeScriptTypeParametersThenOpenParenWithBacktracking()) {
+ .did_not_skip_anything => {},
+ else => |result| {
+ try p.lexer.expect(.t_open_paren);
+ return p.parseParenExpr(loc, level, ParenExprOpts{
+ .force_arrow_fn = result == .definitely_type_parameters,
+ });
+ },
+ }
+
+ // "x"
+ try p.lexer.next();
+ try p.skipTypeScriptType(.lowest);
+ try p.lexer.expectGreaterThan(false);
+ return p.parsePrefix(level, errors, flags);
+ }
+
+ try p.lexer.unexpected();
return error.SyntaxError;
}
+ fn t_import(noalias p: *P, level: Level) anyerror!Expr {
+ const loc = p.lexer.loc();
+ try p.lexer.next();
+ return p.parseImportExpr(loc, level);
+ }
+
+ // Before splitting this up, this used 3 KB of stack space per call.
+ pub fn parsePrefix(noalias p: *P, level: Level, noalias errors: ?*DeferredErrors, flags: Expr.EFlags) anyerror!Expr {
+ return switch (p.lexer.token) {
+ .t_open_bracket => t_open_bracket(p, errors),
+ .t_open_brace => t_open_brace(p, errors),
+ .t_less_than => t_less_than(p, level, errors, flags),
+ .t_import => t_import(p, level),
+ .t_open_paren => t_open_paren(p, level),
+ .t_private_identifier => t_private_identifier(p, level),
+ .t_identifier => t_identifier(p, level),
+ .t_false => t_false(p),
+ .t_true => t_true(p),
+ .t_null => t_null(p),
+ .t_this => t_this(p),
+ .t_template_head => t_template_head(p),
+ .t_numeric_literal => t_numeric_literal(p),
+ .t_big_integer_literal => t_big_integer_literal(p),
+ .t_string_literal, .t_no_substitution_template_literal => p.parseStringLiteral(),
+ .t_slash => t_slash(p),
+ .t_void => t_void(p),
+ .t_typeof => t_typeof(p),
+ .t_delete => t_delete(p),
+ .t_plus => t_plus(p),
+ .t_minus => t_minus(p),
+ .t_tilde => t_tilde(p),
+ .t_exclamation => t_exclamation(p),
+ .t_minus_minus => t_minus_minus(p),
+ .t_plus_plus => t_plus_plus(p),
+ .t_function => t_function(p),
+ .t_class => t_class(p),
+ .t_new => t_new(p, flags),
+ .t_super => t_super(p, level),
+ else => brk: {
+ @branchHint(.cold);
+ try p.lexer.unexpected();
+ break :brk error.SyntaxError;
+ },
+ };
+ }
};
}
diff --git a/src/ast/parseSuffix.zig b/src/ast/parseSuffix.zig
index 89c6645696..f79167ac8d 100644
--- a/src/ast/parseSuffix.zig
+++ b/src/ast/parseSuffix.zig
@@ -7,783 +7,802 @@ pub fn ParseSuffix(
const P = js_parser.NewParser_(parser_feature__typescript, parser_feature__jsx, parser_feature__scan_only);
const is_typescript_enabled = P.is_typescript_enabled;
- const parse_suffix_fns = struct {
- const Continuation = enum { next, done };
+ fn handleTypescriptAs(p: *P, level: Level) anyerror!Continuation {
+ if (is_typescript_enabled and level.lt(.compare) and !p.lexer.has_newline_before and (p.lexer.isContextualKeyword("as") or p.lexer.isContextualKeyword("satisfies"))) {
+ try p.lexer.next();
+ try p.skipTypeScriptType(.lowest);
- pub fn handleTypescriptAs(p: *P, level: Level) anyerror!Continuation {
- if (is_typescript_enabled and level.lt(.compare) and !p.lexer.has_newline_before and (p.lexer.isContextualKeyword("as") or p.lexer.isContextualKeyword("satisfies"))) {
- try p.lexer.next();
- try p.skipTypeScriptType(.lowest);
-
- // These tokens are not allowed to follow a cast expression. This isn't
- // an outright error because it may be on a new line, in which case it's
- // the start of a new expression when it's after a cast:
- //
- // x = y as z
- // (something);
- //
- switch (p.lexer.token) {
- .t_plus_plus,
- .t_minus_minus,
- .t_no_substitution_template_literal,
- .t_template_head,
- .t_open_paren,
- .t_open_bracket,
- .t_question_dot,
- => {
- p.forbid_suffix_after_as_loc = p.lexer.loc();
- return .done;
- },
- else => {},
- }
-
- if (p.lexer.token.isAssign()) {
+ // These tokens are not allowed to follow a cast expression. This isn't
+ // an outright error because it may be on a new line, in which case it's
+ // the start of a new expression when it's after a cast:
+ //
+ // x = y as z
+ // (something);
+ //
+ switch (p.lexer.token) {
+ .t_plus_plus,
+ .t_minus_minus,
+ .t_no_substitution_template_literal,
+ .t_template_head,
+ .t_open_paren,
+ .t_open_bracket,
+ .t_question_dot,
+ => {
p.forbid_suffix_after_as_loc = p.lexer.loc();
return .done;
- }
- return .next;
- }
- return .done;
- }
-
- pub fn t_dot(p: *P, optional_chain: *?OptionalChain, old_optional_chain: ?OptionalChain, left: *Expr) anyerror!Continuation {
- try p.lexer.next();
- const target = left.*;
-
- if (p.lexer.token == .t_private_identifier and p.allow_private_identifiers) {
- // "a.#b"
- // "a?.b.#c"
- switch (left.data) {
- .e_super => {
- try p.lexer.expected(.t_identifier);
- },
- else => {},
- }
-
- const name = p.lexer.identifier;
- const name_loc = p.lexer.loc();
- try p.lexer.next();
- const ref = p.storeNameInRef(name) catch unreachable;
- left.* = p.newExpr(E.Index{
- .target = target,
- .index = p.newExpr(
- E.PrivateIdentifier{
- .ref = ref,
- },
- name_loc,
- ),
- .optional_chain = old_optional_chain,
- }, left.loc);
- } else {
- // "a.b"
- // "a?.b.c"
- if (!p.lexer.isIdentifierOrKeyword()) {
- try p.lexer.expect(.t_identifier);
- }
-
- const name = p.lexer.identifier;
- const name_loc = p.lexer.loc();
- try p.lexer.next();
-
- left.* = p.newExpr(
- E.Dot{
- .target = target,
- .name = name,
- .name_loc = name_loc,
- .optional_chain = old_optional_chain,
- },
- left.loc,
- );
- }
- optional_chain.* = old_optional_chain;
- return .next;
- }
- pub fn t_question_dot(p: *P, level: Level, optional_chain: *?OptionalChain, left: *Expr) anyerror!Continuation {
- try p.lexer.next();
- var optional_start: ?OptionalChain = OptionalChain.start;
-
- // Remove unnecessary optional chains
- if (p.options.features.minify_syntax) {
- const result = SideEffects.toNullOrUndefined(p, left.data);
- if (result.ok and !result.value) {
- optional_start = null;
- }
- }
-
- switch (p.lexer.token) {
- .t_open_bracket => {
- // "a?.[b]"
- try p.lexer.next();
-
- // allow "in" inside the brackets;
- const old_allow_in = p.allow_in;
- p.allow_in = true;
-
- const index = try p.parseExpr(.lowest);
-
- p.allow_in = old_allow_in;
-
- try p.lexer.expect(.t_close_bracket);
- left.* = p.newExpr(
- E.Index{ .target = left.*, .index = index, .optional_chain = optional_start },
- left.loc,
- );
- },
-
- .t_open_paren => {
- // "a?.()"
- if (level.gte(.call)) {
- return .done;
- }
-
- const list_loc = try p.parseCallArgs();
- left.* = p.newExpr(E.Call{
- .target = left.*,
- .args = list_loc.list,
- .close_paren_loc = list_loc.loc,
- .optional_chain = optional_start,
- }, left.loc);
- },
- .t_less_than, .t_less_than_less_than => {
- // "a?.()"
- if (comptime !is_typescript_enabled) {
- try p.lexer.expected(.t_identifier);
- return error.SyntaxError;
- }
-
- _ = try p.skipTypeScriptTypeArguments(false);
- if (p.lexer.token != .t_open_paren) {
- try p.lexer.expected(.t_open_paren);
- }
-
- if (level.gte(.call)) {
- return .done;
- }
-
- const list_loc = try p.parseCallArgs();
- left.* = p.newExpr(E.Call{
- .target = left.*,
- .args = list_loc.list,
- .close_paren_loc = list_loc.loc,
- .optional_chain = optional_start,
- }, left.loc);
- },
- else => {
- if (p.lexer.token == .t_private_identifier and p.allow_private_identifiers) {
- // "a?.#b"
- const name = p.lexer.identifier;
- const name_loc = p.lexer.loc();
- try p.lexer.next();
- const ref = p.storeNameInRef(name) catch unreachable;
- left.* = p.newExpr(E.Index{
- .target = left.*,
- .index = p.newExpr(
- E.PrivateIdentifier{
- .ref = ref,
- },
- name_loc,
- ),
- .optional_chain = optional_start,
- }, left.loc);
- } else {
- // "a?.b"
- if (!p.lexer.isIdentifierOrKeyword()) {
- try p.lexer.expect(.t_identifier);
- }
- const name = p.lexer.identifier;
- const name_loc = p.lexer.loc();
- try p.lexer.next();
-
- left.* = p.newExpr(E.Dot{
- .target = left.*,
- .name = name,
- .name_loc = name_loc,
- .optional_chain = optional_start,
- }, left.loc);
- }
},
+ else => {},
}
- // Only continue if we have started
- if ((optional_start orelse .continuation) == .start) {
- optional_chain.* = .continuation;
- }
-
- return .next;
- }
- pub fn t_no_substitution_template_literal(p: *P, _: Level, _: *?OptionalChain, old_optional_chain: ?OptionalChain, left: *Expr) anyerror!Continuation {
- if (old_optional_chain != null) {
- p.log.addRangeError(p.source, p.lexer.range(), "Template literals cannot have an optional chain as a tag") catch unreachable;
- }
- // p.markSyntaxFeature(compat.TemplateLiteral, p.lexer.Range());
- const head = p.lexer.rawTemplateContents();
- try p.lexer.next();
-
- left.* = p.newExpr(E.Template{
- .tag = left.*,
- .head = .{ .raw = head },
- }, left.loc);
- return .next;
- }
- pub fn t_template_head(p: *P, _: Level, _: *?OptionalChain, old_optional_chain: ?OptionalChain, left: *Expr) anyerror!Continuation {
- if (old_optional_chain != null) {
- p.log.addRangeError(p.source, p.lexer.range(), "Template literals cannot have an optional chain as a tag") catch unreachable;
- }
- // p.markSyntaxFeature(compat.TemplateLiteral, p.lexer.Range());
- const head = p.lexer.rawTemplateContents();
- const partsGroup = try p.parseTemplateParts(true);
- const tag = left.*;
- left.* = p.newExpr(E.Template{
- .tag = tag,
- .head = .{ .raw = head },
- .parts = partsGroup,
- }, left.loc);
- return .next;
- }
- pub fn t_open_bracket(p: *P, optional_chain: *?OptionalChain, old_optional_chain: ?OptionalChain, left: *Expr, flags: Expr.EFlags) anyerror!Continuation {
- // When parsing a decorator, ignore EIndex expressions since they may be
- // part of a computed property:
- //
- // class Foo {
- // @foo ['computed']() {}
- // }
- //
- // This matches the behavior of the TypeScript compiler.
- if (flags == .ts_decorator) {
+ if (p.lexer.token.isAssign()) {
+ p.forbid_suffix_after_as_loc = p.lexer.loc();
return .done;
}
+ return .next;
+ }
+ return .done;
+ }
+ fn t_dot(p: *P, optional_chain: *?OptionalChain, old_optional_chain: ?OptionalChain, left: *Expr) anyerror!Continuation {
+ try p.lexer.next();
+ const target = left.*;
+
+ if (p.lexer.token == .t_private_identifier and p.allow_private_identifiers) {
+ // "a.#b"
+ // "a?.b.#c"
+ switch (left.data) {
+ .e_super => {
+ try p.lexer.expected(.t_identifier);
+ },
+ else => {},
+ }
+
+ const name = p.lexer.identifier;
+ const name_loc = p.lexer.loc();
try p.lexer.next();
-
- // Allow "in" inside the brackets
- const old_allow_in = p.allow_in;
- p.allow_in = true;
-
- const index = try p.parseExpr(.lowest);
-
- p.allow_in = old_allow_in;
-
- try p.lexer.expect(.t_close_bracket);
-
+ const ref = p.storeNameInRef(name) catch unreachable;
left.* = p.newExpr(E.Index{
- .target = left.*,
- .index = index,
+ .target = target,
+ .index = p.newExpr(
+ E.PrivateIdentifier{
+ .ref = ref,
+ },
+ name_loc,
+ ),
.optional_chain = old_optional_chain,
}, left.loc);
- optional_chain.* = old_optional_chain;
- return .next;
- }
- pub fn t_open_paren(p: *P, level: Level, optional_chain: *?OptionalChain, old_optional_chain: ?OptionalChain, left: *Expr) anyerror!Continuation {
- if (level.gte(.call)) {
- return .done;
+ } else {
+ // "a.b"
+ // "a?.b.c"
+ if (!p.lexer.isIdentifierOrKeyword()) {
+ try p.lexer.expect(.t_identifier);
}
- const list_loc = try p.parseCallArgs();
+ const name = p.lexer.identifier;
+ const name_loc = p.lexer.loc();
+ try p.lexer.next();
+
left.* = p.newExpr(
- E.Call{
- .target = left.*,
- .args = list_loc.list,
- .close_paren_loc = list_loc.loc,
+ E.Dot{
+ .target = target,
+ .name = name,
+ .name_loc = name_loc,
.optional_chain = old_optional_chain,
},
left.loc,
);
+ }
+ optional_chain.* = old_optional_chain;
+ return .next;
+ }
+ fn t_question_dot(p: *P, level: Level, optional_chain: *?OptionalChain, left: *Expr) anyerror!Continuation {
+ try p.lexer.next();
+ var optional_start: ?OptionalChain = OptionalChain.start;
+
+ // Remove unnecessary optional chains
+ if (p.options.features.minify_syntax) {
+ const result = SideEffects.toNullOrUndefined(p, left.data);
+ if (result.ok and !result.value) {
+ optional_start = null;
+ }
+ }
+
+ switch (p.lexer.token) {
+ .t_open_bracket => {
+ // "a?.[b]"
+ try p.lexer.next();
+
+ // allow "in" inside the brackets;
+ const old_allow_in = p.allow_in;
+ p.allow_in = true;
+
+ const index = try p.parseExpr(.lowest);
+
+ p.allow_in = old_allow_in;
+
+ try p.lexer.expect(.t_close_bracket);
+ left.* = p.newExpr(
+ E.Index{ .target = left.*, .index = index, .optional_chain = optional_start },
+ left.loc,
+ );
+ },
+
+ .t_open_paren => {
+ // "a?.()"
+ if (level.gte(.call)) {
+ return .done;
+ }
+
+ const list_loc = try p.parseCallArgs();
+ left.* = p.newExpr(E.Call{
+ .target = left.*,
+ .args = list_loc.list,
+ .close_paren_loc = list_loc.loc,
+ .optional_chain = optional_start,
+ }, left.loc);
+ },
+ .t_less_than, .t_less_than_less_than => {
+ // "a?.()"
+ if (comptime !is_typescript_enabled) {
+ try p.lexer.expected(.t_identifier);
+ return error.SyntaxError;
+ }
+
+ _ = try p.skipTypeScriptTypeArguments(false);
+ if (p.lexer.token != .t_open_paren) {
+ try p.lexer.expected(.t_open_paren);
+ }
+
+ if (level.gte(.call)) {
+ return .done;
+ }
+
+ const list_loc = try p.parseCallArgs();
+ left.* = p.newExpr(E.Call{
+ .target = left.*,
+ .args = list_loc.list,
+ .close_paren_loc = list_loc.loc,
+ .optional_chain = optional_start,
+ }, left.loc);
+ },
+ else => {
+ if (p.lexer.token == .t_private_identifier and p.allow_private_identifiers) {
+ // "a?.#b"
+ const name = p.lexer.identifier;
+ const name_loc = p.lexer.loc();
+ try p.lexer.next();
+ const ref = p.storeNameInRef(name) catch unreachable;
+ left.* = p.newExpr(E.Index{
+ .target = left.*,
+ .index = p.newExpr(
+ E.PrivateIdentifier{
+ .ref = ref,
+ },
+ name_loc,
+ ),
+ .optional_chain = optional_start,
+ }, left.loc);
+ } else {
+ // "a?.b"
+ if (!p.lexer.isIdentifierOrKeyword()) {
+ try p.lexer.expect(.t_identifier);
+ }
+ const name = p.lexer.identifier;
+ const name_loc = p.lexer.loc();
+ try p.lexer.next();
+
+ left.* = p.newExpr(E.Dot{
+ .target = left.*,
+ .name = name,
+ .name_loc = name_loc,
+ .optional_chain = optional_start,
+ }, left.loc);
+ }
+ },
+ }
+
+ // Only continue if we have started
+ if ((optional_start orelse .continuation) == .start) {
+ optional_chain.* = .continuation;
+ }
+
+ return .next;
+ }
+ fn t_no_substitution_template_literal(p: *P, _: Level, _: *?OptionalChain, old_optional_chain: ?OptionalChain, left: *Expr) anyerror!Continuation {
+ if (old_optional_chain != null) {
+ p.log.addRangeError(p.source, p.lexer.range(), "Template literals cannot have an optional chain as a tag") catch unreachable;
+ }
+ // p.markSyntaxFeature(compat.TemplateLiteral, p.lexer.Range());
+ const head = p.lexer.rawTemplateContents();
+ try p.lexer.next();
+
+ left.* = p.newExpr(E.Template{
+ .tag = left.*,
+ .head = .{ .raw = head },
+ }, left.loc);
+ return .next;
+ }
+ fn t_template_head(p: *P, _: Level, _: *?OptionalChain, old_optional_chain: ?OptionalChain, left: *Expr) anyerror!Continuation {
+ if (old_optional_chain != null) {
+ p.log.addRangeError(p.source, p.lexer.range(), "Template literals cannot have an optional chain as a tag") catch unreachable;
+ }
+ // p.markSyntaxFeature(compat.TemplateLiteral, p.lexer.Range());
+ const head = p.lexer.rawTemplateContents();
+ const partsGroup = try p.parseTemplateParts(true);
+ const tag = left.*;
+ left.* = p.newExpr(E.Template{
+ .tag = tag,
+ .head = .{ .raw = head },
+ .parts = partsGroup,
+ }, left.loc);
+ return .next;
+ }
+ fn t_open_bracket(p: *P, optional_chain: *?OptionalChain, old_optional_chain: ?OptionalChain, left: *Expr, flags: Expr.EFlags) anyerror!Continuation {
+ // When parsing a decorator, ignore EIndex expressions since they may be
+ // part of a computed property:
+ //
+ // class Foo {
+ // @foo ['computed']() {}
+ // }
+ //
+ // This matches the behavior of the TypeScript compiler.
+ if (flags == .ts_decorator) {
+ return .done;
+ }
+
+ try p.lexer.next();
+
+ // Allow "in" inside the brackets
+ const old_allow_in = p.allow_in;
+ p.allow_in = true;
+
+ const index = try p.parseExpr(.lowest);
+
+ p.allow_in = old_allow_in;
+
+ try p.lexer.expect(.t_close_bracket);
+
+ left.* = p.newExpr(E.Index{
+ .target = left.*,
+ .index = index,
+ .optional_chain = old_optional_chain,
+ }, left.loc);
+ optional_chain.* = old_optional_chain;
+ return .next;
+ }
+ fn t_open_paren(p: *P, level: Level, optional_chain: *?OptionalChain, old_optional_chain: ?OptionalChain, left: *Expr) anyerror!Continuation {
+ if (level.gte(.call)) {
+ return .done;
+ }
+
+ const list_loc = try p.parseCallArgs();
+ left.* = p.newExpr(
+ E.Call{
+ .target = left.*,
+ .args = list_loc.list,
+ .close_paren_loc = list_loc.loc,
+ .optional_chain = old_optional_chain,
+ },
+ left.loc,
+ );
+ optional_chain.* = old_optional_chain;
+ return .next;
+ }
+ fn t_question(p: *P, level: Level, noalias errors: ?*DeferredErrors, left: *Expr) anyerror!Continuation {
+ if (level.gte(.conditional)) {
+ return .done;
+ }
+ try p.lexer.next();
+
+ // Stop now if we're parsing one of these:
+ // "(a?) => {}"
+ // "(a?: b) => {}"
+ // "(a?, b?) => {}"
+ if (is_typescript_enabled and left.loc.start == p.latest_arrow_arg_loc.start and (p.lexer.token == .t_colon or
+ p.lexer.token == .t_close_paren or p.lexer.token == .t_comma))
+ {
+ if (errors == null) {
+ try p.lexer.unexpected();
+ return error.SyntaxError;
+ }
+ errors.?.invalid_expr_after_question = p.lexer.range();
+ return .done;
+ }
+
+ const ternary = p.newExpr(E.If{
+ .test_ = left.*,
+ .yes = undefined,
+ .no = undefined,
+ }, left.loc);
+
+ // Allow "in" in between "?" and ":"
+ const old_allow_in = p.allow_in;
+ p.allow_in = true;
+
+ // condition ? yes : no
+ // ^
+ try p.parseExprWithFlags(.comma, .none, &ternary.data.e_if.yes);
+
+ p.allow_in = old_allow_in;
+
+ // condition ? yes : no
+ // ^
+ try p.lexer.expect(.t_colon);
+
+ // condition ? yes : no
+ // ^
+ try p.parseExprWithFlags(.comma, .none, &ternary.data.e_if.no);
+
+ // condition ? yes : no
+ // ^
+
+ left.* = ternary;
+ return .next;
+ }
+ fn t_exclamation(p: *P, optional_chain: *?OptionalChain, old_optional_chain: ?OptionalChain) anyerror!Continuation {
+ // Skip over TypeScript non-null assertions
+ if (p.lexer.has_newline_before) {
+ return .done;
+ }
+
+ if (!is_typescript_enabled) {
+ try p.lexer.unexpected();
+ return error.SyntaxError;
+ }
+
+ try p.lexer.next();
+ optional_chain.* = old_optional_chain;
+
+ return .next;
+ }
+ fn t_minus_minus(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (p.lexer.has_newline_before or level.gte(.postfix)) {
+ return .done;
+ }
+
+ try p.lexer.next();
+ left.* = p.newExpr(E.Unary{ .op = .un_post_dec, .value = left.* }, left.loc);
+ return .next;
+ }
+ fn t_plus_plus(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (p.lexer.has_newline_before or level.gte(.postfix)) {
+ return .done;
+ }
+
+ try p.lexer.next();
+ left.* = p.newExpr(E.Unary{ .op = .un_post_inc, .value = left.* }, left.loc);
+ return .next;
+ }
+ fn t_comma(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.comma)) {
+ return .done;
+ }
+
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_comma, .left = left.*, .right = try p.parseExpr(.comma) }, left.loc);
+ return .next;
+ }
+ fn t_plus(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.add)) {
+ return .done;
+ }
+
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_add, .left = left.*, .right = try p.parseExpr(.add) }, left.loc);
+ return .next;
+ }
+ fn t_plus_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.assign)) {
+ return .done;
+ }
+
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_add_assign, .left = left.*, .right = try p.parseExpr(@as(Op.Level, @enumFromInt(@intFromEnum(Op.Level.assign) - 1))) }, left.loc);
+ return .next;
+ }
+ fn t_minus(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.add)) {
+ return .done;
+ }
+
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_sub, .left = left.*, .right = try p.parseExpr(.add) }, left.loc);
+ return .next;
+ }
+ fn t_minus_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.assign)) {
+ return .done;
+ }
+
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_sub_assign, .left = left.*, .right = try p.parseExpr(Op.Level.sub(Op.Level.assign, 1)) }, left.loc);
+ return .next;
+ }
+ fn t_asterisk(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.multiply)) {
+ return .done;
+ }
+
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_mul, .left = left.*, .right = try p.parseExpr(.multiply) }, left.loc);
+ return .next;
+ }
+ fn t_asterisk_asterisk(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.exponentiation)) {
+ return .done;
+ }
+
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_pow, .left = left.*, .right = try p.parseExpr(Op.Level.exponentiation.sub(1)) }, left.loc);
+ return .next;
+ }
+ fn t_asterisk_asterisk_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.assign)) {
+ return .done;
+ }
+
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_pow_assign, .left = left.*, .right = try p.parseExpr(Op.Level.assign.sub(1)) }, left.loc);
+ return .next;
+ }
+ fn t_asterisk_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.assign)) {
+ return .done;
+ }
+
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_mul_assign, .left = left.*, .right = try p.parseExpr(Op.Level.assign.sub(1)) }, left.loc);
+ return .next;
+ }
+ fn t_percent(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.multiply)) {
+ return .done;
+ }
+
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_rem, .left = left.*, .right = try p.parseExpr(Op.Level.multiply) }, left.loc);
+ return .next;
+ }
+ fn t_percent_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.assign)) {
+ return .done;
+ }
+
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_rem_assign, .left = left.*, .right = try p.parseExpr(Level.assign.sub(1)) }, left.loc);
+ return .next;
+ }
+ fn t_slash(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.multiply)) {
+ return .done;
+ }
+
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_div, .left = left.*, .right = try p.parseExpr(Level.multiply) }, left.loc);
+ return .next;
+ }
+ fn t_slash_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.assign)) {
+ return .done;
+ }
+
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_div_assign, .left = left.*, .right = try p.parseExpr(Level.assign.sub(1)) }, left.loc);
+ return .next;
+ }
+ fn t_equals_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.equals)) {
+ return .done;
+ }
+
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_loose_eq, .left = left.*, .right = try p.parseExpr(Level.equals) }, left.loc);
+ return .next;
+ }
+ fn t_exclamation_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.equals)) {
+ return .done;
+ }
+
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_loose_ne, .left = left.*, .right = try p.parseExpr(Level.equals) }, left.loc);
+ return .next;
+ }
+ fn t_equals_equals_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.equals)) {
+ return .done;
+ }
+
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_strict_eq, .left = left.*, .right = try p.parseExpr(Level.equals) }, left.loc);
+ return .next;
+ }
+ fn t_exclamation_equals_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.equals)) {
+ return .done;
+ }
+
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_strict_ne, .left = left.*, .right = try p.parseExpr(Level.equals) }, left.loc);
+ return .next;
+ }
+ fn t_less_than(p: *P, level: Level, optional_chain: *?OptionalChain, old_optional_chain: ?OptionalChain, left: *Expr) anyerror!Continuation {
+ // TypeScript allows type arguments to be specified with angle brackets
+ // inside an expression. Unlike in other languages, this unfortunately
+ // appears to require backtracking to parse.
+ if (is_typescript_enabled and p.trySkipTypeScriptTypeArgumentsWithBacktracking()) {
optional_chain.* = old_optional_chain;
return .next;
}
- pub fn t_question(p: *P, level: Level, noalias errors: ?*DeferredErrors, left: *Expr) anyerror!Continuation {
- if (level.gte(.conditional)) {
- return .done;
- }
- try p.lexer.next();
- // Stop now if we're parsing one of these:
- // "(a?) => {}"
- // "(a?: b) => {}"
- // "(a?, b?) => {}"
- if (is_typescript_enabled and left.loc.start == p.latest_arrow_arg_loc.start and (p.lexer.token == .t_colon or
- p.lexer.token == .t_close_paren or p.lexer.token == .t_comma))
- {
- if (errors == null) {
- try p.lexer.unexpected();
- return error.SyntaxError;
- }
- errors.?.invalid_expr_after_question = p.lexer.range();
- return .done;
- }
-
- const ternary = p.newExpr(E.If{
- .test_ = left.*,
- .yes = undefined,
- .no = undefined,
- }, left.loc);
-
- // Allow "in" in between "?" and ":"
- const old_allow_in = p.allow_in;
- p.allow_in = true;
-
- // condition ? yes : no
- // ^
- try p.parseExprWithFlags(.comma, .none, &ternary.data.e_if.yes);
-
- p.allow_in = old_allow_in;
-
- // condition ? yes : no
- // ^
- try p.lexer.expect(.t_colon);
-
- // condition ? yes : no
- // ^
- try p.parseExprWithFlags(.comma, .none, &ternary.data.e_if.no);
-
- // condition ? yes : no
- // ^
-
- left.* = ternary;
- return .next;
+ if (level.gte(.compare)) {
+ return .done;
}
- pub fn t_exclamation(p: *P, optional_chain: *?OptionalChain, old_optional_chain: ?OptionalChain) anyerror!Continuation {
- // Skip over TypeScript non-null assertions
- if (p.lexer.has_newline_before) {
- return .done;
- }
-
- if (!is_typescript_enabled) {
- try p.lexer.unexpected();
- return error.SyntaxError;
- }
-
- try p.lexer.next();
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_lt, .left = left.*, .right = try p.parseExpr(.compare) }, left.loc);
+ return .next;
+ }
+ fn t_less_than_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.compare)) {
+ return .done;
+ }
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_le, .left = left.*, .right = try p.parseExpr(.compare) }, left.loc);
+ return .next;
+ }
+ fn t_greater_than(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.compare)) {
+ return .done;
+ }
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_gt, .left = left.*, .right = try p.parseExpr(.compare) }, left.loc);
+ return .next;
+ }
+ fn t_greater_than_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.compare)) {
+ return .done;
+ }
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_ge, .left = left.*, .right = try p.parseExpr(.compare) }, left.loc);
+ return .next;
+ }
+ fn t_less_than_less_than(p: *P, level: Level, optional_chain: *?OptionalChain, old_optional_chain: ?OptionalChain, left: *Expr) anyerror!Continuation {
+ // TypeScript allows type arguments to be specified with angle brackets
+ // inside an expression. Unlike in other languages, this unfortunately
+ // appears to require backtracking to parse.
+ if (is_typescript_enabled and p.trySkipTypeScriptTypeArgumentsWithBacktracking()) {
optional_chain.* = old_optional_chain;
+ return .next;
+ }
- return .next;
+ if (level.gte(.shift)) {
+ return .done;
+ }
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_shl, .left = left.*, .right = try p.parseExpr(.shift) }, left.loc);
+ return .next;
+ }
+ fn t_less_than_less_than_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.assign)) {
+ return .done;
}
- pub fn t_minus_minus(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (p.lexer.has_newline_before or level.gte(.postfix)) {
- return .done;
- }
- try p.lexer.next();
- left.* = p.newExpr(E.Unary{ .op = .un_post_dec, .value = left.* }, left.loc);
- return .next;
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_shl_assign, .left = left.*, .right = try p.parseExpr(Level.assign.sub(1)) }, left.loc);
+ return .next;
+ }
+ fn t_greater_than_greater_than(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.shift)) {
+ return .done;
+ }
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_shr, .left = left.*, .right = try p.parseExpr(.shift) }, left.loc);
+ return .next;
+ }
+ fn t_greater_than_greater_than_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.assign)) {
+ return .done;
}
- pub fn t_plus_plus(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (p.lexer.has_newline_before or level.gte(.postfix)) {
- return .done;
- }
- try p.lexer.next();
- left.* = p.newExpr(E.Unary{ .op = .un_post_inc, .value = left.* }, left.loc);
- return .next;
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_shr_assign, .left = left.*, .right = try p.parseExpr(Level.assign.sub(1)) }, left.loc);
+ return .next;
+ }
+ fn t_greater_than_greater_than_greater_than(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.shift)) {
+ return .done;
+ }
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_u_shr, .left = left.*, .right = try p.parseExpr(.shift) }, left.loc);
+ return .next;
+ }
+ fn t_greater_than_greater_than_greater_than_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.assign)) {
+ return .done;
}
- pub fn t_comma(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.comma)) {
- return .done;
- }
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_comma, .left = left.*, .right = try p.parseExpr(.comma) }, left.loc);
- return .next;
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_u_shr_assign, .left = left.*, .right = try p.parseExpr(Level.assign.sub(1)) }, left.loc);
+ return .next;
+ }
+ fn t_question_question(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.nullish_coalescing)) {
+ return .done;
+ }
+ try p.lexer.next();
+ const prev = left.*;
+ left.* = p.newExpr(E.Binary{ .op = .bin_nullish_coalescing, .left = prev, .right = try p.parseExpr(.nullish_coalescing) }, left.loc);
+ return .next;
+ }
+ fn t_question_question_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.assign)) {
+ return .done;
}
- pub fn t_plus(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.add)) {
- return .done;
- }
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_add, .left = left.*, .right = try p.parseExpr(.add) }, left.loc);
- return .next;
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_nullish_coalescing_assign, .left = left.*, .right = try p.parseExpr(Level.assign.sub(1)) }, left.loc);
+ return .next;
+ }
+ fn t_bar_bar(p: *P, level: Level, left: *Expr, flags: Expr.EFlags) anyerror!Continuation {
+ if (level.gte(.logical_or)) {
+ return .done;
}
- pub fn t_plus_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.assign)) {
- return .done;
- }
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_add_assign, .left = left.*, .right = try p.parseExpr(@as(Op.Level, @enumFromInt(@intFromEnum(Op.Level.assign) - 1))) }, left.loc);
- return .next;
+ // Prevent "||" inside "??" from the right
+ if (level.eql(.nullish_coalescing)) {
+ try p.lexer.unexpected();
+ return error.SyntaxError;
}
- pub fn t_minus(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.add)) {
- return .done;
- }
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_sub, .left = left.*, .right = try p.parseExpr(.add) }, left.loc);
- return .next;
- }
- pub fn t_minus_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.assign)) {
- return .done;
- }
+ try p.lexer.next();
+ const right = try p.parseExpr(.logical_or);
+ left.* = p.newExpr(E.Binary{ .op = Op.Code.bin_logical_or, .left = left.*, .right = right }, left.loc);
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_sub_assign, .left = left.*, .right = try p.parseExpr(Op.Level.sub(Op.Level.assign, 1)) }, left.loc);
- return .next;
- }
- pub fn t_asterisk(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.multiply)) {
- return .done;
- }
+ if (level.lt(.nullish_coalescing)) {
+ try p.parseSuffix(left, Level.nullish_coalescing.addF(1), null, flags);
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_mul, .left = left.*, .right = try p.parseExpr(.multiply) }, left.loc);
- return .next;
- }
- pub fn t_asterisk_asterisk(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.exponentiation)) {
- return .done;
- }
-
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_pow, .left = left.*, .right = try p.parseExpr(Op.Level.exponentiation.sub(1)) }, left.loc);
- return .next;
- }
- pub fn t_asterisk_asterisk_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.assign)) {
- return .done;
- }
-
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_pow_assign, .left = left.*, .right = try p.parseExpr(Op.Level.assign.sub(1)) }, left.loc);
- return .next;
- }
- pub fn t_asterisk_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.assign)) {
- return .done;
- }
-
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_mul_assign, .left = left.*, .right = try p.parseExpr(Op.Level.assign.sub(1)) }, left.loc);
- return .next;
- }
- pub fn t_percent(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.multiply)) {
- return .done;
- }
-
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_rem, .left = left.*, .right = try p.parseExpr(Op.Level.multiply) }, left.loc);
- return .next;
- }
- pub fn t_percent_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.assign)) {
- return .done;
- }
-
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_rem_assign, .left = left.*, .right = try p.parseExpr(Level.assign.sub(1)) }, left.loc);
- return .next;
- }
- pub fn t_slash(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.multiply)) {
- return .done;
- }
-
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_div, .left = left.*, .right = try p.parseExpr(Level.multiply) }, left.loc);
- return .next;
- }
- pub fn t_slash_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.assign)) {
- return .done;
- }
-
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_div_assign, .left = left.*, .right = try p.parseExpr(Level.assign.sub(1)) }, left.loc);
- return .next;
- }
- pub fn t_equals_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.equals)) {
- return .done;
- }
-
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_loose_eq, .left = left.*, .right = try p.parseExpr(Level.equals) }, left.loc);
- return .next;
- }
- pub fn t_exclamation_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.equals)) {
- return .done;
- }
-
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_loose_ne, .left = left.*, .right = try p.parseExpr(Level.equals) }, left.loc);
- return .next;
- }
- pub fn t_equals_equals_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.equals)) {
- return .done;
- }
-
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_strict_eq, .left = left.*, .right = try p.parseExpr(Level.equals) }, left.loc);
- return .next;
- }
- pub fn t_exclamation_equals_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.equals)) {
- return .done;
- }
-
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_strict_ne, .left = left.*, .right = try p.parseExpr(Level.equals) }, left.loc);
- return .next;
- }
- pub fn t_less_than(p: *P, level: Level, optional_chain: *?OptionalChain, old_optional_chain: ?OptionalChain, left: *Expr) anyerror!Continuation {
- // TypeScript allows type arguments to be specified with angle brackets
- // inside an expression. Unlike in other languages, this unfortunately
- // appears to require backtracking to parse.
- if (is_typescript_enabled and p.trySkipTypeScriptTypeArgumentsWithBacktracking()) {
- optional_chain.* = old_optional_chain;
- return .next;
- }
-
- if (level.gte(.compare)) {
- return .done;
- }
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_lt, .left = left.*, .right = try p.parseExpr(.compare) }, left.loc);
- return .next;
- }
- pub fn t_less_than_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.compare)) {
- return .done;
- }
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_le, .left = left.*, .right = try p.parseExpr(.compare) }, left.loc);
- return .next;
- }
- pub fn t_greater_than(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.compare)) {
- return .done;
- }
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_gt, .left = left.*, .right = try p.parseExpr(.compare) }, left.loc);
- return .next;
- }
- pub fn t_greater_than_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.compare)) {
- return .done;
- }
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_ge, .left = left.*, .right = try p.parseExpr(.compare) }, left.loc);
- return .next;
- }
- pub fn t_less_than_less_than(p: *P, level: Level, optional_chain: *?OptionalChain, old_optional_chain: ?OptionalChain, left: *Expr) anyerror!Continuation {
- // TypeScript allows type arguments to be specified with angle brackets
- // inside an expression. Unlike in other languages, this unfortunately
- // appears to require backtracking to parse.
- if (is_typescript_enabled and p.trySkipTypeScriptTypeArgumentsWithBacktracking()) {
- optional_chain.* = old_optional_chain;
- return .next;
- }
-
- if (level.gte(.shift)) {
- return .done;
- }
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_shl, .left = left.*, .right = try p.parseExpr(.shift) }, left.loc);
- return .next;
- }
- pub fn t_less_than_less_than_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.assign)) {
- return .done;
- }
-
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_shl_assign, .left = left.*, .right = try p.parseExpr(Level.assign.sub(1)) }, left.loc);
- return .next;
- }
- pub fn t_greater_than_greater_than(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.shift)) {
- return .done;
- }
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_shr, .left = left.*, .right = try p.parseExpr(.shift) }, left.loc);
- return .next;
- }
- pub fn t_greater_than_greater_than_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.assign)) {
- return .done;
- }
-
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_shr_assign, .left = left.*, .right = try p.parseExpr(Level.assign.sub(1)) }, left.loc);
- return .next;
- }
- pub fn t_greater_than_greater_than_greater_than(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.shift)) {
- return .done;
- }
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_u_shr, .left = left.*, .right = try p.parseExpr(.shift) }, left.loc);
- return .next;
- }
- pub fn t_greater_than_greater_than_greater_than_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.assign)) {
- return .done;
- }
-
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_u_shr_assign, .left = left.*, .right = try p.parseExpr(Level.assign.sub(1)) }, left.loc);
- return .next;
- }
- pub fn t_question_question(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.nullish_coalescing)) {
- return .done;
- }
- try p.lexer.next();
- const prev = left.*;
- left.* = p.newExpr(E.Binary{ .op = .bin_nullish_coalescing, .left = prev, .right = try p.parseExpr(.nullish_coalescing) }, left.loc);
- return .next;
- }
- pub fn t_question_question_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.assign)) {
- return .done;
- }
-
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_nullish_coalescing_assign, .left = left.*, .right = try p.parseExpr(Level.assign.sub(1)) }, left.loc);
- return .next;
- }
- pub fn t_bar_bar(p: *P, level: Level, left: *Expr, flags: Expr.EFlags) anyerror!Continuation {
- if (level.gte(.logical_or)) {
- return .done;
- }
-
- // Prevent "||" inside "??" from the right
- if (level.eql(.nullish_coalescing)) {
+ if (p.lexer.token == .t_question_question) {
try p.lexer.unexpected();
return error.SyntaxError;
}
-
- try p.lexer.next();
- const right = try p.parseExpr(.logical_or);
- left.* = p.newExpr(E.Binary{ .op = Op.Code.bin_logical_or, .left = left.*, .right = right }, left.loc);
-
- if (level.lt(.nullish_coalescing)) {
- try p.parseSuffix(left, Level.nullish_coalescing.addF(1), null, flags);
-
- if (p.lexer.token == .t_question_question) {
- try p.lexer.unexpected();
- return error.SyntaxError;
- }
- }
- return .next;
}
- pub fn t_bar_bar_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.assign)) {
- return .done;
- }
-
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_logical_or_assign, .left = left.*, .right = try p.parseExpr(Level.assign.sub(1)) }, left.loc);
- return .next;
+ return .next;
+ }
+ fn t_bar_bar_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.assign)) {
+ return .done;
}
- pub fn t_ampersand_ampersand(p: *P, level: Level, left: *Expr, flags: Expr.EFlags) anyerror!Continuation {
- if (level.gte(.logical_and)) {
- return .done;
- }
- // Prevent "&&" inside "??" from the right
- if (level.eql(.nullish_coalescing)) {
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_logical_or_assign, .left = left.*, .right = try p.parseExpr(Level.assign.sub(1)) }, left.loc);
+ return .next;
+ }
+ fn t_ampersand_ampersand(p: *P, level: Level, left: *Expr, flags: Expr.EFlags) anyerror!Continuation {
+ if (level.gte(.logical_and)) {
+ return .done;
+ }
+
+ // Prevent "&&" inside "??" from the right
+ if (level.eql(.nullish_coalescing)) {
+ try p.lexer.unexpected();
+ return error.SyntaxError;
+ }
+
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_logical_and, .left = left.*, .right = try p.parseExpr(.logical_and) }, left.loc);
+
+ // Prevent "&&" inside "??" from the left
+ if (level.lt(.nullish_coalescing)) {
+ try p.parseSuffix(left, Level.nullish_coalescing.addF(1), null, flags);
+
+ if (p.lexer.token == .t_question_question) {
try p.lexer.unexpected();
return error.SyntaxError;
}
+ }
+ return .next;
+ }
+ fn t_ampersand_ampersand_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.assign)) {
+ return .done;
+ }
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_logical_and, .left = left.*, .right = try p.parseExpr(.logical_and) }, left.loc);
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_logical_and_assign, .left = left.*, .right = try p.parseExpr(Level.assign.sub(1)) }, left.loc);
+ return .next;
+ }
+ fn t_bar(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.bitwise_or)) {
+ return .done;
+ }
- // Prevent "&&" inside "??" from the left
- if (level.lt(.nullish_coalescing)) {
- try p.parseSuffix(left, Level.nullish_coalescing.addF(1), null, flags);
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_bitwise_or, .left = left.*, .right = try p.parseExpr(.bitwise_or) }, left.loc);
+ return .next;
+ }
+ fn t_bar_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.assign)) {
+ return .done;
+ }
- if (p.lexer.token == .t_question_question) {
- try p.lexer.unexpected();
- return error.SyntaxError;
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_bitwise_or_assign, .left = left.*, .right = try p.parseExpr(Level.assign.sub(1)) }, left.loc);
+ return .next;
+ }
+ fn t_ampersand(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.bitwise_and)) {
+ return .done;
+ }
+
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_bitwise_and, .left = left.*, .right = try p.parseExpr(.bitwise_and) }, left.loc);
+ return .next;
+ }
+ fn t_ampersand_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.assign)) {
+ return .done;
+ }
+
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_bitwise_and_assign, .left = left.*, .right = try p.parseExpr(Level.assign.sub(1)) }, left.loc);
+ return .next;
+ }
+ fn t_caret(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.bitwise_xor)) {
+ return .done;
+ }
+
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_bitwise_xor, .left = left.*, .right = try p.parseExpr(.bitwise_xor) }, left.loc);
+ return .next;
+ }
+ fn t_caret_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.assign)) {
+ return .done;
+ }
+
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_bitwise_xor_assign, .left = left.*, .right = try p.parseExpr(Level.assign.sub(1)) }, left.loc);
+ return .next;
+ }
+ fn t_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.assign)) {
+ return .done;
+ }
+
+ try p.lexer.next();
+
+ left.* = p.newExpr(E.Binary{ .op = .bin_assign, .left = left.*, .right = try p.parseExpr(Level.assign.sub(1)) }, left.loc);
+ return .next;
+ }
+ fn t_in(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.compare) or !p.allow_in) {
+ return .done;
+ }
+
+ // Warn about "!a in b" instead of "!(a in b)"
+ switch (left.data) {
+ .e_unary => |unary| {
+ if (unary.op == .un_not) {
+ // TODO:
+ // p.log.addRangeWarning(source: ?Source, r: Range, text: string)
}
- }
- return .next;
+ },
+ else => {},
}
- pub fn t_ampersand_ampersand_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.assign)) {
- return .done;
- }
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_logical_and_assign, .left = left.*, .right = try p.parseExpr(Level.assign.sub(1)) }, left.loc);
- return .next;
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_in, .left = left.*, .right = try p.parseExpr(.compare) }, left.loc);
+ return .next;
+ }
+ fn t_instanceof(p: *P, level: Level, left: *Expr) anyerror!Continuation {
+ if (level.gte(.compare)) {
+ return .done;
}
- pub fn t_bar(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.bitwise_or)) {
- return .done;
- }
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_bitwise_or, .left = left.*, .right = try p.parseExpr(.bitwise_or) }, left.loc);
- return .next;
- }
- pub fn t_bar_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.assign)) {
- return .done;
- }
-
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_bitwise_or_assign, .left = left.*, .right = try p.parseExpr(Level.assign.sub(1)) }, left.loc);
- return .next;
- }
- pub fn t_ampersand(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.bitwise_and)) {
- return .done;
- }
-
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_bitwise_and, .left = left.*, .right = try p.parseExpr(.bitwise_and) }, left.loc);
- return .next;
- }
- pub fn t_ampersand_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.assign)) {
- return .done;
- }
-
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_bitwise_and_assign, .left = left.*, .right = try p.parseExpr(Level.assign.sub(1)) }, left.loc);
- return .next;
- }
- pub fn t_caret(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.bitwise_xor)) {
- return .done;
- }
-
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_bitwise_xor, .left = left.*, .right = try p.parseExpr(.bitwise_xor) }, left.loc);
- return .next;
- }
- pub fn t_caret_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.assign)) {
- return .done;
- }
-
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_bitwise_xor_assign, .left = left.*, .right = try p.parseExpr(Level.assign.sub(1)) }, left.loc);
- return .next;
- }
- pub fn t_equals(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.assign)) {
- return .done;
- }
-
- try p.lexer.next();
-
- left.* = p.newExpr(E.Binary{ .op = .bin_assign, .left = left.*, .right = try p.parseExpr(Level.assign.sub(1)) }, left.loc);
- return .next;
- }
- pub fn t_in(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.compare) or !p.allow_in) {
- return .done;
- }
-
- // Warn about "!a in b" instead of "!(a in b)"
+ // Warn about "!a instanceof b" instead of "!(a instanceof b)". Here's an
+ // example of code with this problem: https://github.com/mrdoob/three.js/pull/11182.
+ if (!p.options.suppress_warnings_about_weird_code) {
switch (left.data) {
.e_unary => |unary| {
if (unary.op == .un_not) {
@@ -793,34 +812,11 @@ pub fn ParseSuffix(
},
else => {},
}
-
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_in, .left = left.*, .right = try p.parseExpr(.compare) }, left.loc);
- return .next;
}
- pub fn t_instanceof(p: *P, level: Level, left: *Expr) anyerror!Continuation {
- if (level.gte(.compare)) {
- return .done;
- }
-
- // Warn about "!a instanceof b" instead of "!(a instanceof b)". Here's an
- // example of code with this problem: https://github.com/mrdoob/three.js/pull/11182.
- if (!p.options.suppress_warnings_about_weird_code) {
- switch (left.data) {
- .e_unary => |unary| {
- if (unary.op == .un_not) {
- // TODO:
- // p.log.addRangeWarning(source: ?Source, r: Range, text: string)
- }
- },
- else => {},
- }
- }
- try p.lexer.next();
- left.* = p.newExpr(E.Binary{ .op = .bin_instanceof, .left = left.*, .right = try p.parseExpr(.compare) }, left.loc);
- return .next;
- }
- };
+ try p.lexer.next();
+ left.* = p.newExpr(E.Binary{ .op = .bin_instanceof, .left = left.*, .right = try p.parseExpr(.compare) }, left.loc);
+ return .next;
+ }
pub fn parseSuffix(p: *P, left_and_out: *Expr, level: Level, noalias errors: ?*DeferredErrors, flags: Expr.EFlags) anyerror!void {
var left_value = left_and_out.*;
@@ -905,20 +901,20 @@ pub fn ParseSuffix(
.t_question_question_equals,
.t_slash,
.t_slash_equals,
- => |tag| @field(parse_suffix_fns, @tagName(tag))(p, level, left),
- .t_exclamation => parse_suffix_fns.t_exclamation(p, optional_chain, old_optional_chain),
- .t_bar_bar => parse_suffix_fns.t_bar_bar(p, level, left, flags),
- .t_ampersand_ampersand => parse_suffix_fns.t_ampersand_ampersand(p, level, left, flags),
- .t_question => parse_suffix_fns.t_question(p, level, errors, left),
- .t_question_dot => parse_suffix_fns.t_question_dot(p, level, optional_chain, left),
- .t_template_head => parse_suffix_fns.t_template_head(p, level, optional_chain, old_optional_chain, left),
- .t_less_than => parse_suffix_fns.t_less_than(p, level, optional_chain, old_optional_chain, left),
- .t_open_paren => parse_suffix_fns.t_open_paren(p, level, optional_chain, old_optional_chain, left),
- .t_no_substitution_template_literal => parse_suffix_fns.t_no_substitution_template_literal(p, level, optional_chain, old_optional_chain, left),
- .t_open_bracket => parse_suffix_fns.t_open_bracket(p, optional_chain, old_optional_chain, left, flags),
- .t_dot => parse_suffix_fns.t_dot(p, optional_chain, old_optional_chain, left),
- .t_less_than_less_than => parse_suffix_fns.t_less_than_less_than(p, level, optional_chain, old_optional_chain, left),
- else => parse_suffix_fns.handleTypescriptAs(p, level),
+ => |tag| @field(@This(), @tagName(tag))(p, level, left),
+ .t_exclamation => t_exclamation(p, optional_chain, old_optional_chain),
+ .t_bar_bar => t_bar_bar(p, level, left, flags),
+ .t_ampersand_ampersand => t_ampersand_ampersand(p, level, left, flags),
+ .t_question => t_question(p, level, errors, left),
+ .t_question_dot => t_question_dot(p, level, optional_chain, left),
+ .t_template_head => t_template_head(p, level, optional_chain, old_optional_chain, left),
+ .t_less_than => t_less_than(p, level, optional_chain, old_optional_chain, left),
+ .t_open_paren => t_open_paren(p, level, optional_chain, old_optional_chain, left),
+ .t_no_substitution_template_literal => t_no_substitution_template_literal(p, level, optional_chain, old_optional_chain, left),
+ .t_open_bracket => t_open_bracket(p, optional_chain, old_optional_chain, left, flags),
+ .t_dot => t_dot(p, optional_chain, old_optional_chain, left),
+ .t_less_than_less_than => t_less_than_less_than(p, level, optional_chain, old_optional_chain, left),
+ else => handleTypescriptAs(p, level),
};
switch (try continuation) {
@@ -931,7 +927,7 @@ pub fn ParseSuffix(
}
};
}
-
+const Continuation = enum { next, done };
const string = []const u8;
const bun = @import("bun");