From 28623dd2f71e775241aa8e42275263939bdba2db Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Thu, 7 Aug 2025 01:49:58 -0700 Subject: [PATCH] Split up parseprefix --- src/ast/parsePrefix.zig | 1345 +++++++++++++++++----------------- src/ast/parseSuffix.zig | 1518 +++++++++++++++++++-------------------- 2 files changed, 1456 insertions(+), 1407 deletions(-) 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");