mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
lots
This commit is contained in:
370
src/js_ast.zig
370
src/js_ast.zig
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user