diff options
author | 2021-04-21 19:00:21 -0700 | |
---|---|---|
committer | 2021-04-21 19:00:21 -0700 | |
commit | ba472faea1a5ed8943916c5a602b415f3242f5c9 (patch) | |
tree | d50e9e07e69dde95443d23337e8f3dbbbdaa617b /src | |
parent | ad46a05ea5b2f1a86ef56bc946ebbe3589e21d43 (diff) | |
download | bun-ba472faea1a5ed8943916c5a602b415f3242f5c9.tar.gz bun-ba472faea1a5ed8943916c5a602b415f3242f5c9.tar.zst bun-ba472faea1a5ed8943916c5a602b415f3242f5c9.zip |
e_identifier!!
Diffstat (limited to 'src')
-rw-r--r-- | src/js_ast.zig | 125 | ||||
-rw-r--r-- | src/js_parser.zig | 249 | ||||
-rw-r--r-- | src/logger.zig | 4 |
3 files changed, 351 insertions, 27 deletions
diff --git a/src/js_ast.zig b/src/js_ast.zig index c8631ca1b..cf476bc54 100644 --- a/src/js_ast.zig +++ b/src/js_ast.zig @@ -50,7 +50,7 @@ pub const Ref = struct { const None = Ref{ .source_index = null, .inner_index = std.math.maxInt(u32) }; }; -pub const ImportItemStatus = enum(u8) { +pub const ImportItemStatus = packed enum { none, // The linker doesn't report import/export mismatch errors @@ -171,7 +171,7 @@ pub const G = struct { name: logger.Loc, extends: ?ExprNodeIndex = null, body_loc: logger.Loc, - properties: ?[]Property = null, + properties: []Property = &([_]Property{}), }; // invalid shadowing if left as Comment @@ -209,7 +209,7 @@ pub const G = struct { pub const Fn = struct { name: ?LocRef, open_parens_loc: logger.Loc, - args: ?[]Arg = null, + args: []Arg = &([_]Arg{}), body: ?FnBody = null, arguments_ref: ?Ref = null, @@ -487,7 +487,7 @@ pub const Symbol = struct { } }; -pub const OptionalChain = enum { +pub const OptionalChain = packed enum { // "a?.b" start, @@ -504,7 +504,7 @@ pub const E = struct { is_parenthesized: bool = false, }; - pub const Unary = struct { + pub const Unary = packed struct { op: Op.Code, value: ExprNodeIndex, }; @@ -518,6 +518,7 @@ pub const E = struct { pub const Boolean = struct { value: bool }; pub const Super = struct {}; pub const Null = struct {}; + pub const This = struct {}; pub const Undefined = struct {}; pub const New = struct { target: ExprNodeIndex, @@ -1019,6 +1020,9 @@ pub const Expr = struct { E.Null => { return Expr{ .loc = loc, .data = Data{ .e_null = data } }; }, + E.This => { + return Expr{ .loc = loc, .data = Data{ .e_this = data } }; + }, E.Undefined => { return Expr{ .loc = loc, .data = Data{ .e_undefined = data } }; }, @@ -1140,12 +1144,14 @@ pub const Expr = struct { e_if, e_require_or_require_resolve, e_import, + e_this, }; pub const Data = union(Tag) { e_array: E.Array, e_unary: E.Unary, e_binary: E.Binary, + e_this: E.This, e_boolean: E.Boolean, e_super: E.Super, e_null: E.Null, @@ -1295,12 +1301,13 @@ pub const S = struct { pub const With = struct { value: ExprNodeIndex, body: StmtNodeIndex, - body_loc: logger.Log, + body_loc: logger.Loc, }; pub const Try = struct { + body_loc: logger.Loc, body: StmtNodeList, - body_loc: logger.Log, + catch_: ?Catch = null, finally: ?Finally = null, }; @@ -1349,11 +1356,11 @@ pub const S = struct { }; pub const Break = struct { - label: *LocRef, + label: ?LocRef = null, }; pub const Continue = struct { - label: *LocRef, + label: ?LocRef = null, }; }; @@ -1372,7 +1379,7 @@ pub const Case = struct { loc: logger.Loc, value: ?ExprNodeIndex, body: StmtNode pub const Op = struct { // If you add a new token, remember to add it to "OpTable" too - pub const Code = enum { + pub const Code = packed enum(u8) { // Prefix un_pos, un_neg, @@ -1439,7 +1446,7 @@ pub const Op = struct { bin_logical_and_assign, }; - pub const Level = enum(u8) { + pub const Level = packed enum(u23) { lowest, comma, spread, @@ -1817,7 +1824,7 @@ pub const NamedExport = struct { alias_loc: logger.Loc, }; -pub const StrictModeKind = enum { +pub const StrictModeKind = packed enum(u7) { sloppy_mode, explicit_strict_mode, implicit_strict_mode_import, @@ -1888,16 +1895,106 @@ test "Binding.init" { ); std.testing.expect(binding.loc.start == 1); std.testing.expect(@as(Binding.Tag, binding.data) == Binding.Tag.b_identifier); + + std.debug.print("-------Binding: {d} bits\n", .{@bitSizeOf(Binding)}); + std.debug.print("B.Identifier: {d} bits\n", .{@bitSizeOf(B.Identifier)}); + std.debug.print("B.Array: {d} bits\n", .{@bitSizeOf(B.Array)}); + std.debug.print("B.Property: {d} bits\n", .{@bitSizeOf(B.Property)}); + std.debug.print("B.Object: {d} bits\n", .{@bitSizeOf(B.Object)}); + std.debug.print("B.Missing: {d} bits\n", .{@bitSizeOf(B.Missing)}); + std.debug.print("-------Binding: {d} bits\n", .{@bitSizeOf(Binding)}); +} + +test "Stmt.init" { + var stmt = Stmt.init( + S.Continue{}, + logger.Loc{ .start = 1 }, + ); + std.testing.expect(stmt.loc.start == 1); + std.testing.expect(@as(Stmt.Tag, stmt.data) == Stmt.Tag.s_continue); + + std.debug.print("-----Stmt {d} bits\n", .{@bitSizeOf(Stmt)}); + std.debug.print("StmtNodeList: {d} bits\n", .{@bitSizeOf(StmtNodeList)}); + std.debug.print("StmtOrExpr: {d} bits\n", .{@bitSizeOf(StmtOrExpr)}); + std.debug.print("S.Block {d} bits\n", .{@bitSizeOf(S.Block)}); + std.debug.print("S.Comment {d} bits\n", .{@bitSizeOf(S.Comment)}); + std.debug.print("S.Directive {d} bits\n", .{@bitSizeOf(S.Directive)}); + std.debug.print("S.ExportClause {d} bits\n", .{@bitSizeOf(S.ExportClause)}); + std.debug.print("S.Empty {d} bits\n", .{@bitSizeOf(S.Empty)}); + std.debug.print("S.TypeScript {d} bits\n", .{@bitSizeOf(S.TypeScript)}); + std.debug.print("S.Debugger {d} bits\n", .{@bitSizeOf(S.Debugger)}); + std.debug.print("S.ExportFrom {d} bits\n", .{@bitSizeOf(S.ExportFrom)}); + std.debug.print("S.ExportDefault {d} bits\n", .{@bitSizeOf(S.ExportDefault)}); + std.debug.print("S.Enum {d} bits\n", .{@bitSizeOf(S.Enum)}); + std.debug.print("S.Namespace {d} bits\n", .{@bitSizeOf(S.Namespace)}); + std.debug.print("S.Function {d} bits\n", .{@bitSizeOf(S.Function)}); + std.debug.print("S.Class {d} bits\n", .{@bitSizeOf(S.Class)}); + std.debug.print("S.If {d} bits\n", .{@bitSizeOf(S.If)}); + std.debug.print("S.For {d} bits\n", .{@bitSizeOf(S.For)}); + std.debug.print("S.ForIn {d} bits\n", .{@bitSizeOf(S.ForIn)}); + std.debug.print("S.ForOf {d} bits\n", .{@bitSizeOf(S.ForOf)}); + std.debug.print("S.DoWhile {d} bits\n", .{@bitSizeOf(S.DoWhile)}); + std.debug.print("S.While {d} bits\n", .{@bitSizeOf(S.While)}); + std.debug.print("S.With {d} bits\n", .{@bitSizeOf(S.With)}); + std.debug.print("S.Try {d} bits\n", .{@bitSizeOf(S.Try)}); + std.debug.print("S.Switch {d} bits\n", .{@bitSizeOf(S.Switch)}); + std.debug.print("S.Import {d} bits\n", .{@bitSizeOf(S.Import)}); + std.debug.print("S.Return {d} bits\n", .{@bitSizeOf(S.Return)}); + std.debug.print("S.Throw {d} bits\n", .{@bitSizeOf(S.Throw)}); + std.debug.print("S.Local {d} bits\n", .{@bitSizeOf(S.Local)}); + std.debug.print("S.Break {d} bits\n", .{@bitSizeOf(S.Break)}); + std.debug.print("S.Continue {d} bits\n", .{@bitSizeOf(S.Continue)}); + std.debug.print("-----Stmt {d} bits\n", .{@bitSizeOf(Stmt)}); } test "Expr.init" { const ident = Expr.init(E.Identifier{}, logger.Loc{ .start = 100 }); - const list = [_]Expr{ident}; - const expr = Expr.init( + var list = [_]Expr{ident}; + var expr = Expr.init( E.Array{ .items = list[0..] }, logger.Loc{ .start = 1 }, ); std.testing.expect(expr.loc.start == 1); std.testing.expect(@as(Expr.Tag, expr.data) == Expr.Tag.e_array); std.testing.expect(expr.data.e_array.items[0].loc.start == 100); + + std.debug.print("--logger.Loc {d} bits\n", .{@bitSizeOf(logger.Loc)}); + std.debug.print("--logger.Range {d} bits\n", .{@bitSizeOf(logger.Range)}); + std.debug.print("----------Expr: {d} bits\n", .{@bitSizeOf(Expr)}); + std.debug.print("ExprNodeList: {d} bits\n", .{@bitSizeOf(ExprNodeList)}); + std.debug.print("E.Array: {d} bits\n", .{@bitSizeOf(E.Array)}); + + std.debug.print("E.Unary: {d} bits\n", .{@bitSizeOf(E.Unary)}); + std.debug.print("E.Binary: {d} bits\n", .{@bitSizeOf(E.Binary)}); + std.debug.print("E.Boolean: {d} bits\n", .{@bitSizeOf(E.Boolean)}); + std.debug.print("E.Super: {d} bits\n", .{@bitSizeOf(E.Super)}); + std.debug.print("E.Null: {d} bits\n", .{@bitSizeOf(E.Null)}); + std.debug.print("E.Undefined: {d} bits\n", .{@bitSizeOf(E.Undefined)}); + std.debug.print("E.New: {d} bits\n", .{@bitSizeOf(E.New)}); + std.debug.print("E.NewTarget: {d} bits\n", .{@bitSizeOf(E.NewTarget)}); + std.debug.print("E.Function: {d} bits\n", .{@bitSizeOf(E.Function)}); + std.debug.print("E.ImportMeta: {d} bits\n", .{@bitSizeOf(E.ImportMeta)}); + std.debug.print("E.Call: {d} bits\n", .{@bitSizeOf(E.Call)}); + std.debug.print("E.Dot: {d} bits\n", .{@bitSizeOf(E.Dot)}); + std.debug.print("E.Index: {d} bits\n", .{@bitSizeOf(E.Index)}); + std.debug.print("E.Arrow: {d} bits\n", .{@bitSizeOf(E.Arrow)}); + std.debug.print("E.Identifier: {d} bits\n", .{@bitSizeOf(E.Identifier)}); + std.debug.print("E.ImportIdentifier: {d} bits\n", .{@bitSizeOf(E.ImportIdentifier)}); + std.debug.print("E.PrivateIdentifier: {d} bits\n", .{@bitSizeOf(E.PrivateIdentifier)}); + std.debug.print("E.JSXElement: {d} bits\n", .{@bitSizeOf(E.JSXElement)}); + std.debug.print("E.Missing: {d} bits\n", .{@bitSizeOf(E.Missing)}); + std.debug.print("E.Number: {d} bits\n", .{@bitSizeOf(E.Number)}); + std.debug.print("E.BigInt: {d} bits\n", .{@bitSizeOf(E.BigInt)}); + std.debug.print("E.Object: {d} bits\n", .{@bitSizeOf(E.Object)}); + std.debug.print("E.Spread: {d} bits\n", .{@bitSizeOf(E.Spread)}); + std.debug.print("E.String: {d} bits\n", .{@bitSizeOf(E.String)}); + std.debug.print("E.TemplatePart: {d} bits\n", .{@bitSizeOf(E.TemplatePart)}); + std.debug.print("E.Template: {d} bits\n", .{@bitSizeOf(E.Template)}); + std.debug.print("E.RegExp: {d} bits\n", .{@bitSizeOf(E.RegExp)}); + std.debug.print("E.Await: {d} bits\n", .{@bitSizeOf(E.Await)}); + std.debug.print("E.Yield: {d} bits\n", .{@bitSizeOf(E.Yield)}); + std.debug.print("E.If: {d} bits\n", .{@bitSizeOf(E.If)}); + std.debug.print("E.RequireOrRequireResolve: {d} bits\n", .{@bitSizeOf(E.RequireOrRequireResolve)}); + std.debug.print("E.Import: {d} bits\n", .{@bitSizeOf(E.Import)}); + std.debug.print("----------Expr: {d} bits\n", .{@bitSizeOf(Expr)}); } 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 { diff --git a/src/logger.zig b/src/logger.zig index 933e20667..492761f8d 100644 --- a/src/logger.zig +++ b/src/logger.zig @@ -25,7 +25,7 @@ pub const Kind = enum { } }; -pub const Loc = struct { +pub const Loc = packed struct { start: i32 = -1, pub fn toUsize(self: *Loc) usize { @@ -95,7 +95,7 @@ pub const Data = struct { text: string, location: ?Location = null }; pub const Msg = struct { kind: Kind = Kind.err, data: Data, notes: ?[]Data = null }; -pub const Range = struct { +pub const Range = packed struct { loc: Loc = Loc.Empty, len: i32 = 0, pub const None = Range{ .loc = Loc.Empty, .len = 0 }; |