aboutsummaryrefslogtreecommitdiff
path: root/src/js_parser.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/js_parser.zig')
-rw-r--r--src/js_parser.zig249
1 files changed, 238 insertions, 11 deletions
diff --git a/src/js_parser.zig b/src/js_parser.zig
index ed74e3a22..963b8e108 100644
--- a/src/js_parser.zig
+++ b/src/js_parser.zig
@@ -99,6 +99,12 @@ const ScopeOrder = struct {
scope: *js_ast.Scope,
};
+const ParenExprOpts = struct {
+ async_range: logger.Range = logger.Range.None,
+ is_async: bool = false,
+ force_arrow_fn: bool = false,
+};
+
// This is function-specific information used during parsing. It is saved and
// restored on the call stack around code that parses nested functions and
// arrow expressions.
@@ -204,12 +210,12 @@ const DeferredErrors = struct {
to.array_spread_feature = inv;
}
}
-};
-const ParenExprOpts = struct {
- async_range: ?logger.Range = null,
- is_async: bool = false,
- force_arrow_fn: bool = false,
+ var None = DeferredErrors{
+ .invalid_expr_default_value = null,
+ .invalid_expr_after_question = null,
+ .array_spread_feature = null,
+ };
};
const ModuleType = enum { esm };
@@ -1538,7 +1544,7 @@ const P = struct {
return p.source.contents[ref.inner_index .. ref.inner_index - source_index];
} else {
- std.debug.panic("Internal error: invalid symbol reference.", .{ref});
+ std.debug.panic("Internal error: invalid symbol reference. {s}", .{ref});
}
}
@@ -1591,15 +1597,15 @@ const P = struct {
// "async () => {}"
.t_open_paren => {
p.lexer.next();
- return p.parseParenExpr(async_range.loc, ParenExprOptions{ .is_async = true, .async_range = asyncRange });
+ return p.parseParenExpr(async_range.loc, ParenExprOpts{ .is_async = true, .async_range = async_range });
},
// "async<T>()"
// "async <T>() => {}"
.t_less_than => {
- if (p.options.ts and p.trySkipTypeScriptTypeParametersThenOpenParenWithBacktracking) {
+ if (p.options.ts and p.trySkipTypeScriptTypeParametersThenOpenParenWithBacktracking()) {
p.lexer.next();
- return p.parseParenExpr(async_range.loc, ParenExprOptions{ .is_async = true, .async_range = asyncRange });
+ return p.parseParenExpr(async_range.loc, ParenExprOpts{ .is_async = true, .async_range = async_range });
}
},
@@ -1610,12 +1616,12 @@ const P = struct {
// "async"
// "async + 1"
return Expr.init(
- E.Identifier{ .ref = p.storeNameInRef("async") },
+ E.Identifier{ .ref = try p.storeNameInRef("async") },
async_range.loc,
);
}
- pub fn trySkipTypeScriptTypeParametersThenOpenParenWithBacktracking() void {
+ pub fn trySkipTypeScriptTypeParametersThenOpenParenWithBacktracking(self: *P) bool {
notimpl();
}
@@ -1639,6 +1645,7 @@ const P = struct {
// engineering, it looks like they apply to the next CallExpression or
// NewExpression. So in "/* @__PURE__ */ a().b() + c()" the comment applies
// to the expression "a().b()".
+
if (had_pure_comment_before and level.lt(.call)) {
expr = p.parseSuffix(expr, .call - 1, errors, flags);
switch (expr.data) {
@@ -1757,8 +1764,228 @@ const P = struct {
p.current_scope = current_scope.parent;
}
+ pub fn markExprAsParenthesized(p: *P, expr: *Expr) void {
+ switch (expr.data) {
+ .e_array => |*ex| {
+ ex.is_parenthesized = true;
+ },
+ .e_object => |*ex| {
+ ex.is_parenthesized = true;
+ },
+ else => {
+ return;
+ },
+ }
+ }
+
+ pub fn parseYieldExpr(p: *P, loc: logger.Loc) Expr {
+ // Parse a yield-from expression, which yields from an iterator
+ const isStar = p.lexer.token == T.t_asterisk;
+
+ if (isStar) {
+ if (p.lexer.has_newline_before) {
+ p.lexer.unexpected();
+ }
+ p.lexer.next();
+ }
+
+ var value: ?ExprNodeIndex = null;
+ switch (p.lexer.token) {
+ .t_close_brace, .t_close_paren, .t_colon, .t_comma, .t_semicolon => {},
+ else => {
+ if (isStar or !p.lexer.has_newline_before) {
+ var expr = p.parseExpr(.yield);
+ value = p.m(expr);
+ }
+ },
+ }
+
+ return e(E.Yield{
+ .value = value,
+ .is_star = isStar,
+ }, loc);
+ }
+
pub fn parseSuffix(p: *P, left: Expr, level: Level, errors: ?*DeferredErrors, flags: Expr.Flags) Expr {
+ return _parseSuffix(p, left, level, errors orelse &DeferredErrors.None, flags);
+ }
+ pub fn _parseSuffix(p: *P, left: Expr, level: Level, errors: *DeferredErrors, flags: Expr.Flags) callconv(.Inline) Expr {
+ var expr: Expr = undefined;
var loc = p.lexer.loc();
+
+ return expr;
+ }
+ pub fn _parsePrefix(p: *P, level: Level, errors: *DeferredErrors, flags: Expr.Flags) callconv(.Inline) Expr {
+ const loc = p.lexer.loc();
+ const l = @enumToInt(level);
+
+ switch (p.lexer.token) {
+ .t_super => {
+ const superRange = p.lexer.range();
+ p.lexer.next();
+
+ switch (p.lexer.token) {
+ .t_open_paren => {
+ if (l < @enumToInt(Level.call) and p.fn_or_arrow_data_parse.allow_super_call) {
+ return e(E.Super{}, loc);
+ }
+ },
+ .t_dot, .t_open_bracket => {
+ return e(E.Super{}, loc);
+ },
+ else => {},
+ }
+
+ p.log.addRangeError(p.source, superRange, "Unexpected \"super\"") catch unreachable;
+ return e(E.Super{}, loc);
+ },
+ .t_open_paren => {
+ p.lexer.next();
+
+ // Arrow functions aren't allowed in the middle of expressions
+ if (l > @enumToInt(Level.assign)) {
+ const oldAllowIn = p.allow_in;
+ p.allow_in = true;
+
+ var value = p.parseExpr(Level.lowest);
+ p.markExprAsParenthesized(&value);
+ p.lexer.expect(.t_close_paren);
+ p.allow_in = oldAllowIn;
+ return value;
+ }
+
+ return p.parseParenExpr(loc, ParenExprOpts{}) catch unreachable;
+ },
+ .t_false => {
+ p.lexer.next();
+ return e(E.Boolean{ .value = false }, loc);
+ },
+ .t_true => {
+ p.lexer.next();
+ return e(E.Boolean{ .value = true }, loc);
+ },
+ .t_null => {
+ p.lexer.next();
+ return e(E.Null{}, loc);
+ },
+ .t_this => {
+ p.lexer.next();
+ return e(E.This{}, loc);
+ },
+ .t_identifier => {
+ const name = p.lexer.identifier;
+ const name_range = p.lexer.range();
+ const raw = p.lexer.raw();
+ p.lexer.next();
+
+ // Handle async and await expressions
+ if (name.len == 5) {
+ if (strings.eql(name, "async")) {
+ if (strings.eql(raw, "async")) {
+ return p.parseAsyncPrefixExpr(name_range, level) catch unreachable;
+ }
+ } else if (strings.eql(name, "await")) {
+ if (p.fn_or_arrow_data_parse.allow_await) {
+ if (!strings.eql(raw, "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;
+ // p.markSyntaxFeature()
+ }
+
+ if (p.fn_or_arrow_data_parse.arrow_arg_errors) |*err| {
+ err.invalid_expr_await = name_range;
+ } else {
+ p.fn_or_arrow_data_parse.arrow_arg_errors = DeferredArrowArgErrors{ .invalid_expr_await = name_range };
+ }
+
+ var value = p.m(p.parseExpr(.prefix));
+ if (p.lexer.token == T.t_asterisk_asterisk) {
+ p.lexer.unexpected();
+ }
+
+ return e(E.Await{ .value = value }, loc);
+ }
+ }
+ } else if (strings.eql(name, "yield")) {
+ if (p.fn_or_arrow_data_parse.allow_yield) {
+ if (strings.eql(raw, "yield")) {
+ p.log.addRangeError(p.source, name_range, "The keyword \"yield\" cannot be escaped") catch unreachable;
+ } else {
+ if (l > @enumToInt(Level.assign)) {
+ p.log.addRangeError(p.source, name_range, "Cannot use a \"yield\" here without parentheses") catch unreachable;
+ }
+
+ if (p.fn_or_arrow_data_parse.arrow_arg_errors) |*err| {
+ err.invalid_expr_yield = name_range;
+ }
+
+ return p.parseYieldExpr(loc);
+ }
+ } else if (!p.lexer.has_newline_before) {
+ // Try to gracefully recover if "yield" is used in the wrong place
+
+ 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 => {},
+ }
+ }
+ }
+
+ // Handle the start of an arrow expression
+ if (p.lexer.token == .t_equals_greater_than) {
+ const ref = p.storeNameInRef(name) catch unreachable;
+ var args = p.allocator.alloc(Arg, 1) catch unreachable;
+ args[0] = Arg{ .binding = p.m(Binding.init(B.Identifier{
+ .ref = ref,
+ }, loc)) };
+
+ _ = p.pushScopeForParsePass(.function_args, loc) catch unreachable;
+ defer p.popScope();
+ return e(p.parseArrowBody(args, p.m(FnOrArrowDataParse{})) catch unreachable, loc);
+ }
+
+ const ref = p.storeNameInRef(name) catch unreachable;
+
+ return e(E.Identifier{
+ .ref = ref,
+ }, loc);
+ }
+ },
+ .t_string_literal, .t_no_substitution_template_literal => {},
+ .t_template_head => {},
+ .t_numeric_literal => {},
+ .t_big_integer_literal => {},
+ .t_slash, .t_slash_equals => {},
+ .t_void => {},
+ .t_typeof => {},
+ .t_delete => {},
+ .t_plus => {},
+ .t_minus => {},
+ .t_tilde => {},
+ .t_exclamation => {},
+ .t_minus_minus => {},
+ .t_plus_plus => {},
+ .t_function => {},
+ .t_class => {},
+ .t_new => {},
+ .t_open_bracket => {},
+ .t_open_brace => {},
+ .t_less_than => {},
+ .t_import => {},
+ else => {
+ p.lexer.unexpected();
+ return Expr.init(E.Missing{}, logger.Loc.Empty);
+ },
+ }
+
+ return Expr.init(E.Missing{}, logger.Loc.Empty);
+ }
+ pub fn parsePrefix(p: *P, level: Level, errors: ?*DeferredErrors, flags: Expr.Flags) Expr {
+ return p._parsePrefix(level, errors orelse &DeferredErrors.None, flags);
}
pub fn init(allocator: *std.mem.Allocator, log: logger.Log, source: logger.Source, lexer: js_lexer.Lexer, opts: Parser.Options) !*P {