This commit is contained in:
Jarred Sumner
2021-04-22 22:50:18 -07:00
parent 8bcc729477
commit 9414d52ffb
3 changed files with 1131 additions and 12 deletions

View File

@@ -627,6 +627,7 @@ pub const E = struct {
pub const Dot = struct {
// target is Node
target: ExprNodeIndex,
name: string,
name_loc: logger.Loc,
optional_chain: ?OptionalChain = null,
@@ -649,6 +650,7 @@ pub const E = struct {
pub const Index = struct {
index: ExprNodeIndex,
target: ExprNodeIndex,
optional_chain: ?OptionalChain = null,
pub fn hasSameFlagsAs(a: *Index, b: *Index) bool {
@@ -759,8 +761,13 @@ pub const E = struct {
tail_raw: string,
};
pub const Template = struct { tag: ?ExprNodeIndex = null, head: JavascriptString, head_raw: string, // This is only filled out for tagged template literals
parts: ?[]TemplatePart = null, legacy_octal_loc: logger.Loc };
pub const Template = struct {
tag: ?ExprNodeIndex = null,
head: JavascriptString,
head_raw: string, // This is only filled out for tagged template literals
parts: ?[]TemplatePart = null,
legacy_octal_loc: logger.Loc = logger.Loc.Empty,
};
pub const RegExp = struct {
value: string,
@@ -1587,6 +1594,357 @@ pub const Expr = struct {
e_import,
e_this,
e_class,
pub fn isArray(self: Tag) bool {
switch (self) {
.e_array => {
return true;
},
else => {
return false;
},
}
}
pub fn isUnary(self: Tag) bool {
switch (self) {
.e_unary => {
return true;
},
else => {
return false;
},
}
}
pub fn isBinary(self: Tag) bool {
switch (self) {
.e_binary => {
return true;
},
else => {
return false;
},
}
}
pub fn isThis(self: Tag) bool {
switch (self) {
.e_this => {
return true;
},
else => {
return false;
},
}
}
pub fn isClass(self: Tag) bool {
switch (self) {
.e_class => {
return true;
},
else => {
return false;
},
}
}
pub fn isBoolean(self: Tag) bool {
switch (self) {
.e_boolean => {
return true;
},
else => {
return false;
},
}
}
pub fn isSuper(self: Tag) bool {
switch (self) {
.e_super => {
return true;
},
else => {
return false;
},
}
}
pub fn isNull(self: Tag) bool {
switch (self) {
.e_null => {
return true;
},
else => {
return false;
},
}
}
pub fn isUndefined(self: Tag) bool {
switch (self) {
.e_undefined => {
return true;
},
else => {
return false;
},
}
}
pub fn isNew(self: Tag) bool {
switch (self) {
.e_new => {
return true;
},
else => {
return false;
},
}
}
pub fn isNewTarget(self: Tag) bool {
switch (self) {
.e_new_target => {
return true;
},
else => {
return false;
},
}
}
pub fn isFunction(self: Tag) bool {
switch (self) {
.e_function => {
return true;
},
else => {
return false;
},
}
}
pub fn isImportMeta(self: Tag) bool {
switch (self) {
.e_import_meta => {
return true;
},
else => {
return false;
},
}
}
pub fn isCall(self: Tag) bool {
switch (self) {
.e_call => {
return true;
},
else => {
return false;
},
}
}
pub fn isDot(self: Tag) bool {
switch (self) {
.e_dot => {
return true;
},
else => {
return false;
},
}
}
pub fn isIndex(self: Tag) bool {
switch (self) {
.e_index => {
return true;
},
else => {
return false;
},
}
}
pub fn isArrow(self: Tag) bool {
switch (self) {
.e_arrow => {
return true;
},
else => {
return false;
},
}
}
pub fn isIdentifier(self: Tag) bool {
switch (self) {
.e_identifier => {
return true;
},
else => {
return false;
},
}
}
pub fn isImportIdentifier(self: Tag) bool {
switch (self) {
.e_import_identifier => {
return true;
},
else => {
return false;
},
}
}
pub fn isPrivateIdentifier(self: Tag) bool {
switch (self) {
.e_private_identifier => {
return true;
},
else => {
return false;
},
}
}
pub fn isJsxElement(self: Tag) bool {
switch (self) {
.e_jsx_element => {
return true;
},
else => {
return false;
},
}
}
pub fn isMissing(self: Tag) bool {
switch (self) {
.e_missing => {
return true;
},
else => {
return false;
},
}
}
pub fn isNumber(self: Tag) bool {
switch (self) {
.e_number => {
return true;
},
else => {
return false;
},
}
}
pub fn isBigInt(self: Tag) bool {
switch (self) {
.e_big_int => {
return true;
},
else => {
return false;
},
}
}
pub fn isObject(self: Tag) bool {
switch (self) {
.e_object => {
return true;
},
else => {
return false;
},
}
}
pub fn isSpread(self: Tag) bool {
switch (self) {
.e_spread => {
return true;
},
else => {
return false;
},
}
}
pub fn isString(self: Tag) bool {
switch (self) {
.e_string => {
return true;
},
else => {
return false;
},
}
}
pub fn isTemplatePart(self: Tag) bool {
switch (self) {
.e_template_part => {
return true;
},
else => {
return false;
},
}
}
pub fn isTemplate(self: Tag) bool {
switch (self) {
.e_template => {
return true;
},
else => {
return false;
},
}
}
pub fn isRegExp(self: Tag) bool {
switch (self) {
.e_reg_exp => {
return true;
},
else => {
return false;
},
}
}
pub fn isAwait(self: Tag) bool {
switch (self) {
.e_await => {
return true;
},
else => {
return false;
},
}
}
pub fn isYield(self: Tag) bool {
switch (self) {
.e_yield => {
return true;
},
else => {
return false;
},
}
}
pub fn isIf(self: Tag) bool {
switch (self) {
.e_if => {
return true;
},
else => {
return false;
},
}
}
pub fn isRequireOrRequireResolve(self: Tag) bool {
switch (self) {
.e_require_or_require_resolve => {
return true;
},
else => {
return false;
},
}
}
pub fn isImport(self: Tag) bool {
switch (self) {
.e_import => {
return true;
},
else => {
return false;
},
}
}
};
pub fn assign(a: *Expr, b: *Expr, allocator: *std.mem.Allocator) Expr {
@@ -2034,6 +2392,14 @@ pub const Op = struct {
pub fn eql(self: Level, b: Level) callconv(.Inline) bool {
return @enumToInt(self) == @enumToInt(b);
}
pub fn sub(self: Level, comptime i: anytype) callconv(.Inline) Level {
return @intToEnum(Level, @enumToInt(self) - i);
}
pub fn add(self: Level, comptime i: anytype) callconv(.Inline) Level {
return @intToEnum(Level, @enumToInt(self) + i);
}
};
text: string,

View File

@@ -130,12 +130,12 @@ pub const T = enum(u8) {
t_while,
t_with,
pub fn isAssign() bool {
return self >= T.t_ampersand_ampersand_equals and self <= T.t_slash_equals;
pub fn isAssign(self: T) bool {
return @enumToInt(self) >= @enumToInt(T.t_ampersand_ampersand_equals) and @enumToInt(self) <= @enumToInt(T.t_slash_equals);
}
pub fn isReservedWord() bool {
return self >= T.t_break and self <= T.t_with;
pub fn isReservedWord(self: T) bool {
return @enumToInt(self) >= @enumToInt(T.t_break) and @enumToInt(self) <= @enumToInt(T.t_with);
}
pub fn isString(self: T) bool {

View File

@@ -1056,7 +1056,7 @@ const P = struct {
}
pub fn parseFn(p: *P, name: ?js_ast.LocRef, opts: FnOrArrowDataParse) G.Fn {
// if data.allowAwait && data.allowYield {
// if data.allowAwait and data.allowYield {
// p.markSyntaxFeature(compat.AsyncGenerator, data.asyncRange)
// }
@@ -2507,13 +2507,765 @@ const P = struct {
pub fn parseSuffix(p: *P, left: Expr, level: Level, errors: ?*DeferredErrors, flags: Expr.EFlags) Expr {
return _parseSuffix(p, left, level, errors orelse &DeferredErrors.None, flags);
}
pub fn _parseSuffix(p: *P, left: Expr, level: Level, errors: *DeferredErrors, flags: Expr.EFlags) callconv(.Inline) Expr {
pub fn _parseSuffix(p: *P, _left: Expr, level: Level, errors: *DeferredErrors, flags: Expr.EFlags) Expr {
var expr: Expr = undefined;
var left = _left;
var loc = p.lexer.loc();
var optional_chain: ?js_ast.OptionalChain = null;
return expr;
while (true) {
if (p.lexer.loc().start == p.after_arrow_body_loc.start) {
while (true) {
switch (p.lexer.token) {
.t_comma => {
if (level.gte(.comma)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{
.op = .bin_comma,
.left = left,
.right = p.parseExpr(.comma),
}, left.loc);
},
else => {
return left;
},
}
}
}
// Stop now if this token is forbidden to follow a TypeScript "as" cast
if (p.lexer.loc().start == p.forbid_suffix_after_as_loc.start) {
return left;
}
// Reset the optional chain flag by default. That way we won't accidentally
// treat "c.d" as OptionalChainContinue in "a?.b + c.d".
var old_optional_chain = optional_chain;
optional_chain = null;
switch (p.lexer.token) {
.t_dot => {
p.lexer.next();
if (p.lexer.token == .t_private_identifier and p.allow_private_identifiers) {
// "a.#b"
// "a?.b.#c"
switch (left.data) {
.e_super => {
p.lexer.expected(.t_identifier);
},
else => {},
}
var name = p.lexer.identifier;
var name_loc = p.lexer.loc();
p.lexer.next();
const ref = p.storeNameInRef(name) catch unreachable;
left = p.e(E.Index{
.target = left,
.index = p.e(
E.PrivateIdentifier{
.ref = ref,
},
name_loc,
),
.optional_chain = old_optional_chain,
}, left.loc);
} else {
// "a.b"
// "a?.b.c"
if (!p.lexer.isIdentifierOrKeyword()) {
p.lexer.expect(.t_identifier);
}
var name = p.lexer.identifier;
var name_loc = p.lexer.loc();
p.lexer.next();
left = p.e(E.Dot{ .target = left, .name = name, .name_loc = name_loc, .optional_chain = old_optional_chain }, left.loc);
}
optional_chain = old_optional_chain;
},
.t_question_dot => {
p.lexer.next();
var optional_start = js_ast.OptionalChain.start;
// TODO: Remove unnecessary optional chains
// if p.options.mangleSyntax {
// if isNullOrUndefined, _, ok := toNullOrUndefinedWithSideEffects(left.Data); ok and !isNullOrUndefined {
// optionalStart = js_ast.OptionalChainNone
// }
// }
switch (p.lexer.token) {
.t_open_bracket => {
// "a?.[b]"
p.lexer.next();
// allow "in" inside the brackets;
const old_allow_in = p.allow_in;
p.allow_in = true;
const index = p.parseExpr(.lowest);
p.allow_in = old_allow_in;
p.lexer.expect(.t_close_bracket);
left = p.e(
E.Index{ .target = left, .index = index, .optional_chain = optional_start },
left.loc,
);
},
.t_open_paren => {
// "a?.()"
if (level.gte(.call)) {
return left;
}
left = p.e(E.Call{
.target = left,
.args = p.parseCallArgs(),
.optional_chain = optional_start,
}, left.loc);
},
.t_less_than => {
// "a?.<T>()"
if (!p.options.ts) {
p.lexer.expected(.t_identifier);
}
p.skipTypeScriptTypeArguments(false);
if (p.lexer.token != .t_open_paren) {
p.lexer.expected(.t_open_paren);
}
if (level.gte(.call)) {
return left;
}
left = p.e(
E.Call{ .target = left, .args = p.parseCallArgs(), .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();
p.lexer.next();
const ref = p.storeNameInRef(name) catch unreachable;
left = p.e(E.Index{
.target = left,
.index = p.e(
E.PrivateIdentifier{
.ref = ref,
},
name_loc,
),
.optional_chain = optional_start,
}, left.loc);
} else {
// "a?.b"
if (!p.lexer.isIdentifierOrKeyword()) {
p.lexer.expect(.t_identifier);
}
const name = p.lexer.identifier;
const name_loc = p.lexer.loc();
p.lexer.next();
left = p.e(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 == .start) {
optional_start = .ccontinue;
}
},
.t_no_substitution_template_literal => {
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.string_literal;
const head_raw = p.lexer.rawTemplateContents();
p.lexer.next();
left = p.e(E.Template{
.tag = left,
.head = head,
.head_raw = head_raw,
.legacy_octal_loc = logger.Loc.Empty,
}, left.loc);
},
.t_template_head => {
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.string_literal;
const head_raw = p.lexer.rawTemplateContents();
const partsGroup = p.parseTemplateParts(true);
p.lexer.next();
const tag = left;
left = p.e(E.Template{ .tag = tag, .head = head, .head_raw = head_raw, .parts = partsGroup.@"0" }, left.loc);
},
.t_open_bracket => {
// 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 left;
}
p.lexer.next();
// Allow "in" inside the brackets
const old_allow_in = p.allow_in;
p.allow_in = true;
const index = p.parseExpr(.lowest);
p.allow_in = old_allow_in;
p.lexer.expect(.t_close_bracket);
left = p.e(E.Index{
.target = left,
.index = index,
.optional_chain = old_optional_chain,
}, left.loc);
optional_chain = old_optional_chain;
},
.t_open_paren => {
if (level.gte(.call)) {
return left;
}
left = p.e(
E.Call{
.target = left,
.args = p.parseCallArgs(),
.optional_chain = old_optional_chain,
},
left.loc,
);
optional_chain = old_optional_chain;
},
.t_question => {
if (level.gte(.conditional)) {
return left;
}
p.lexer.next();
// Stop now if we're parsing one of these:
// "(a?) => {}"
// "(a?: b) => {}"
// "(a?, b?) => {}"
if (p.options.ts 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.isEmpty()) {
p.lexer.unexpected();
}
errors.invalid_expr_after_question = p.lexer.range();
return left;
}
// Allow "in" in between "?" and ":"
const old_allow_in = p.allow_in;
p.allow_in = true;
const yes = p.parseExpr(.comma);
p.allow_in = old_allow_in;
p.lexer.expect(.t_colon);
const no = p.parseExpr(.comma);
left = p.e(E.If{
.test_ = left,
.yes = yes,
.no = no,
}, left.loc);
},
.t_exclamation => {
// Skip over TypeScript non-null assertions
if (p.lexer.has_newline_before) {
return left;
}
if (!p.options.ts) {
p.lexer.unexpected();
}
if (level.gte(.postfix)) {
return left;
}
p.lexer.next();
optional_chain = old_optional_chain;
},
.t_minus_minus => {
if (p.lexer.has_newline_before or level.gte(.postfix)) {
return left;
}
p.lexer.next();
left = p.e(E.Unary{ .op = .un_post_dec, .value = left }, left.loc);
},
.t_plus_plus => {
if (p.lexer.has_newline_before or level.gte(.postfix)) {
return left;
}
p.lexer.next();
left = p.e(E.Unary{ .op = .un_post_inc, .value = left }, left.loc);
},
.t_comma => {
if (level.gte(.comma)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_comma, .left = left, .right = p.parseExpr(.comma) }, left.loc);
},
.t_plus => {
if (level.gte(.add)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_add, .left = left, .right = p.parseExpr(.add) }, left.loc);
},
.t_plus_equals => {
if (level.gte(.assign)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_add_assign, .left = left, .right = p.parseExpr(@intToEnum(Op.Level, @enumToInt(Op.Level.assign) - 1)) }, left.loc);
},
.t_minus => {
if (level.gte(.add)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_sub, .left = left, .right = p.parseExpr(.add) }, left.loc);
},
.t_minus_equals => {
if (level.gte(.assign)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_sub_assign, .left = left, .right = p.parseExpr(Op.Level.assign.sub(1)) }, left.loc);
},
.t_asterisk => {
if (level.gte(.multiply)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_mul, .left = left, .right = p.parseExpr(.multiply) }, left.loc);
},
.t_asterisk_asterisk => {
if (level.gte(.exponentiation)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_pow, .left = left, .right = p.parseExpr(Op.Level.exponentiation.sub(1)) }, left.loc);
},
.t_asterisk_asterisk_equals => {
if (level.gte(.assign)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_pow_assign, .left = left, .right = p.parseExpr(Op.Level.assign.sub(1)) }, left.loc);
},
.t_asterisk_equals => {
if (level.gte(.assign)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_mul_assign, .left = left, .right = p.parseExpr(Op.Level.assign.sub(1)) }, left.loc);
},
.t_percent => {
if (level.gte(.multiply)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_rem, .left = left, .right = p.parseExpr(Op.Level.multiply) }, left.loc);
},
.t_percent_equals => {
if (level.gte(.assign)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_rem_assign, .left = left, .right = p.parseExpr(Level.assign.sub(1)) }, left.loc);
},
.t_slash => {
if (level.gte(.multiply)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_div, .left = left, .right = p.parseExpr(Level.multiply) }, left.loc);
},
.t_slash_equals => {
if (level.gte(.assign)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_div_assign, .left = left, .right = p.parseExpr(Level.assign.sub(1)) }, left.loc);
},
.t_equals_equals => {
if (level.gte(.equals)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_loose_eq, .left = left, .right = p.parseExpr(Level.equals) }, left.loc);
},
.t_exclamation_equals => {
if (level.gte(.equals)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_loose_ne, .left = left, .right = p.parseExpr(Level.equals) }, left.loc);
},
.t_equals_equals_equals => {
if (level.gte(.equals)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_strict_eq, .left = left, .right = p.parseExpr(Level.equals) }, left.loc);
},
.t_exclamation_equals_equals => {
if (level.gte(.equals)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_strict_ne, .left = left, .right = p.parseExpr(Level.equals) }, left.loc);
},
.t_less_than => {
// 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 (p.options.ts and p.trySkipTypeScriptTypeArgumentsWithBacktracking()) {
optional_chain = old_optional_chain;
continue;
}
if (level.gte(.compare)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_lt, .left = left, .right = p.parseExpr(.compare) }, left.loc);
},
.t_less_than_equals => {
if (level.gte(.compare)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_le, .left = left, .right = p.parseExpr(.compare) }, left.loc);
},
.t_greater_than => {
if (level.gte(.compare)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_gt, .left = left, .right = p.parseExpr(.compare) }, left.loc);
},
.t_greater_than_equals => {
if (level.gte(.compare)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_ge, .left = left, .right = p.parseExpr(.compare) }, left.loc);
},
.t_less_than_less_than => {
if (level.gte(.shift)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_shl, .left = left, .right = p.parseExpr(.shift) }, left.loc);
},
.t_less_than_less_than_equals => {
if (level.gte(.assign)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_shl_assign, .left = left, .right = p.parseExpr(Level.assign.sub(1)) }, left.loc);
},
.t_greater_than_greater_than => {
if (level.gte(.shift)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_shr, .left = left, .right = p.parseExpr(.shift) }, left.loc);
},
.t_greater_than_greater_than_equals => {
if (level.gte(.assign)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_shl_assign, .left = left, .right = p.parseExpr(Level.assign.sub(1)) }, left.loc);
},
.t_greater_than_greater_than_greater_than => {
if (level.gte(.shift)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_u_shr, .left = left, .right = p.parseExpr(.shift) }, left.loc);
},
.t_greater_than_greater_than_greater_than_equals => {
if (level.gte(.assign)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_u_shr_assign, .left = left, .right = p.parseExpr(Level.assign.sub(1)) }, left.loc);
},
.t_question_question => {
if (level.gte(.nullish_coalescing)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_nullish_coalescing, .left = left, .right = p.parseExpr(.nullish_coalescing) }, left.loc);
},
.t_question_question_equals => {
if (level.gte(.assign)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_nullish_coalescing_assign, .left = left, .right = p.parseExpr(Level.assign.sub(1)) }, left.loc);
},
.t_bar_bar => {
if (level.gte(.logical_or)) {
return left;
}
// Prevent "||" inside "??" from the right
if (level.eql(.nullish_coalescing)) {
p.lexer.unexpected();
}
p.lexer.next();
const right = p.parseExpr(.logical_or);
left = p.e(E.Binary{ .op = Op.Code.bin_logical_or, .left = left, .right = right }, left.loc);
if (level.lt(.nullish_coalescing)) {
left = p.parseSuffix(left, Level.nullish_coalescing.add(1), null, flags);
if (p.lexer.token == .t_question_question) {
p.lexer.unexpected();
}
}
},
.t_bar_bar_equals => {
if (level.gte(.assign)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_logical_or_assign, .left = left, .right = p.parseExpr(Level.assign.sub(1)) }, left.loc);
},
.t_ampersand_ampersand => {
if (level.gte(.logical_and)) {
return left;
}
// Prevent "&&" inside "??" from the right
if (level.eql(.nullish_coalescing)) {
p.lexer.unexpected();
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_logical_and, .left = left, .right = p.parseExpr(.logical_and) }, left.loc);
// Prevent "&&" inside "??" from the left
if (level.lt(.nullish_coalescing)) {
left = p.parseSuffix(left, Level.nullish_coalescing.add(1), null, flags);
if (p.lexer.token == .t_question_question) {
p.lexer.unexpected();
}
}
},
.t_ampersand_ampersand_equals => {
if (level.gte(.assign)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_logical_and_assign, .left = left, .right = p.parseExpr(Level.assign.sub(1)) }, left.loc);
},
.t_bar => {
if (level.gte(.bitwise_or)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_bitwise_or, .left = left, .right = p.parseExpr(.bitwise_or) }, left.loc);
},
.t_bar_equals => {
if (level.gte(.assign)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_bitwise_or_assign, .left = left, .right = p.parseExpr(Level.assign.sub(1)) }, left.loc);
},
.t_ampersand => {
if (level.gte(.bitwise_and)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_bitwise_and, .left = left, .right = p.parseExpr(.bitwise_and) }, left.loc);
},
.t_ampersand_equals => {
if (level.gte(.assign)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_shl_assign, .left = left, .right = p.parseExpr(Level.assign.sub(1)) }, left.loc);
},
.t_caret => {
if (level.gte(.bitwise_xor)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_bitwise_xor, .left = left, .right = p.parseExpr(.bitwise_xor) }, left.loc);
},
.t_caret_equals => {
if (level.gte(.assign)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_bitwise_xor_assign, .left = left, .right = p.parseExpr(Level.assign.sub(1)) }, left.loc);
},
.t_equals => {
if (level.gte(.assign)) {
return left;
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_assign, .left = left, .right = p.parseExpr(Level.assign.sub(1)) }, left.loc);
},
.t_in => {
if (level.gte(.compare) or !p.allow_in) {
return left;
}
// 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)
}
},
else => {},
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_in, .left = left, .right = p.parseExpr(.compare) }, left.loc);
},
.t_instanceof => {
if (level.gte(.compare)) {
return left;
}
// 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 => {},
}
}
p.lexer.next();
left = p.e(E.Binary{ .op = .bin_instanceof, .left = left, .right = p.parseExpr(.compare) }, left.loc);
},
else => {
// Handle the TypeScript "as" operator
if (p.options.ts and level.lt(.compare) and !p.lexer.has_newline_before and p.lexer.isContextualKeyword("as")) {
p.lexer.next();
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 left;
},
else => {},
}
if (p.lexer.token.isAssign()) {
p.forbid_suffix_after_as_loc = p.lexer.loc();
return left;
}
continue;
}
return left;
},
}
}
}
pub fn _parsePrefix(p: *P, level: Level, errors: *DeferredErrors, flags: Expr.EFlags) callconv(.Inline) Expr {
pub fn _parsePrefix(p: *P, level: Level, errors: *DeferredErrors, flags: Expr.EFlags) Expr {
const loc = p.lexer.loc();
const l = @enumToInt(level);
@@ -2815,7 +3567,7 @@ const P = struct {
// Skip over TypeScript type arguments here if there are any
if (p.lexer.token == .t_less_than) {
p.trySkipTypeScriptTypeArgumentsWithBacktracking();
_ = p.trySkipTypeScriptTypeArgumentsWithBacktracking();
}
}
@@ -3106,8 +3858,9 @@ const P = struct {
}
}
pub fn trySkipTypeScriptTypeArgumentsWithBacktracking(p: *P) void {
pub fn trySkipTypeScriptTypeArgumentsWithBacktracking(p: *P) bool {
notimpl();
// return false;
}
pub fn parsePrefix(p: *P, level: Level, errors: ?*DeferredErrors, flags: Expr.EFlags) Expr {
return p._parsePrefix(level, errors orelse &DeferredErrors.None, flags);