diff options
author | 2021-04-20 14:59:21 -0700 | |
---|---|---|
committer | 2021-04-20 14:59:21 -0700 | |
commit | 775bac7bbcf50eae02b73916e02fbd4f4e87baeb (patch) | |
tree | 23929f2ec388fa39eaedb582597001e7ccceec44 | |
parent | ba1c784316a062124b56f4643af200c0508a1006 (diff) | |
download | bun-775bac7bbcf50eae02b73916e02fbd4f4e87baeb.tar.gz bun-775bac7bbcf50eae02b73916e02fbd4f4e87baeb.tar.zst bun-775bac7bbcf50eae02b73916e02fbd4f4e87baeb.zip |
wip
-rw-r--r-- | src/js_ast.zig | 178 | ||||
-rw-r--r-- | src/js_lexer.zig | 45 | ||||
-rw-r--r-- | src/js_parser.zig | 66 | ||||
-rw-r--r-- | src/logger.zig | 32 |
4 files changed, 286 insertions, 35 deletions
diff --git a/src/js_ast.zig b/src/js_ast.zig index 86d641224..a5894e6fa 100644 --- a/src/js_ast.zig +++ b/src/js_ast.zig @@ -680,7 +680,183 @@ pub const Stmt = struct { loc: logger.Loc, data: Data, - const Data = union(enum) { + pub fn init(t: anytype, loc: logger.Loc) Stmt { + switch (@TypeOf(t)) { + S.Block => { + return Stmt{ + .loc = loc, + .data = Data{ .s_block = t }, + }; + }, + S.Comment => { + return Stmt{ + .loc = loc, + .data = Data{ .s_comment = t }, + }; + }, + S.Directive => { + return Stmt{ + .loc = loc, + .data = Data{ .s_directive = t }, + }; + }, + S.ExportClause => { + return Stmt{ + .loc = loc, + .data = Data{ .s_export_clause = t }, + }; + }, + S.Empty => { + return Stmt{ + .loc = loc, + .data = Data{ .s_empty = t }, + }; + }, + S.TypeScript => { + return Stmt{ + .loc = loc, + .data = Data{ .s_type_script = t }, + }; + }, + S.Debugger => { + return Stmt{ + .loc = loc, + .data = Data{ .s_debugger = t }, + }; + }, + S.ExportFrom => { + return Stmt{ + .loc = loc, + .data = Data{ .s_export_from = t }, + }; + }, + S.ExportDefault => { + return Stmt{ + .loc = loc, + .data = Data{ .s_export_default = t }, + }; + }, + S.Enum => { + return Stmt{ + .loc = loc, + .data = Data{ .s_enum = t }, + }; + }, + S.Namespace => { + return Stmt{ + .loc = loc, + .data = Data{ .s_namespace = t }, + }; + }, + S.Function => { + return Stmt{ + .loc = loc, + .data = Data{ .s_function = t }, + }; + }, + S.Class => { + return Stmt{ + .loc = loc, + .data = Data{ .s_class = t }, + }; + }, + S.If => { + return Stmt{ + .loc = loc, + .data = Data{ .s_if = t }, + }; + }, + S.For => { + return Stmt{ + .loc = loc, + .data = Data{ .s_for = t }, + }; + }, + S.ForIn => { + return Stmt{ + .loc = loc, + .data = Data{ .s_for_in = t }, + }; + }, + S.ForOf => { + return Stmt{ + .loc = loc, + .data = Data{ .s_for_of = t }, + }; + }, + S.DoWhile => { + return Stmt{ + .loc = loc, + .data = Data{ .s_do_while = t }, + }; + }, + S.While => { + return Stmt{ + .loc = loc, + .data = Data{ .s_while = t }, + }; + }, + S.With => { + return Stmt{ + .loc = loc, + .data = Data{ .s_with = t }, + }; + }, + S.Try => { + return Stmt{ + .loc = loc, + .data = Data{ .s_try = t }, + }; + }, + S.Switch => { + return Stmt{ + .loc = loc, + .data = Data{ .s_switch = t }, + }; + }, + S.Import => { + return Stmt{ + .loc = loc, + .data = Data{ .s_import = t }, + }; + }, + S.Return => { + return Stmt{ + .loc = loc, + .data = Data{ .s_return = t }, + }; + }, + S.Throw => { + return Stmt{ + .loc = loc, + .data = Data{ .s_throw = t }, + }; + }, + S.Local => { + return Stmt{ + .loc = loc, + .data = Data{ .s_local = t }, + }; + }, + S.Break => { + return Stmt{ + .loc = loc, + .data = Data{ .s_break = t }, + }; + }, + S.Continue => { + return Stmt{ + .loc = loc, + .data = Data{ .s_continue = t }, + }; + }, + else => { + @compileError("Invalid type in Stmt.init"); + }, + } + } + + pub const Data = union(enum) { s_block: S.Block, s_comment: S.Comment, s_directive: S.Directive, diff --git a/src/js_lexer.zig b/src/js_lexer.zig index b907ecf13..5aeecba9e 100644 --- a/src/js_lexer.zig +++ b/src/js_lexer.zig @@ -56,6 +56,10 @@ pub const Lexer = struct { prev_error_loc: logger.Loc = logger.Loc.Empty, allocator: *std.mem.Allocator, + pub fn loc(self: *Lexer) logger.Loc { + return logger.usize2Loc(self.start); + } + fn nextCodepointSlice(it: *Lexer) callconv(.Inline) ?[]const u8 { if (it.current >= it.source.contents.len) { return null; @@ -77,28 +81,28 @@ pub const Lexer = struct { } pub fn addError(self: *Lexer, _loc: usize, comptime format: []const u8, args: anytype, panic: bool) void { - const loc = logger.usize2Loc(_loc); - if (eql(loc, self.prev_error_loc)) { + var __loc = logger.usize2Loc(_loc); + if (__loc.eql(self.prev_error_loc)) { return; } const errorMessage = std.fmt.allocPrint(self.allocator, format, args) catch unreachable; - self.log.addError(self.source, loc, errorMessage) catch unreachable; - self.prev_error_loc = loc; + self.log.addError(self.source, __loc, errorMessage) catch unreachable; + self.prev_error_loc = __loc; // if (panic) { self.doPanic(errorMessage); // } } - pub fn addRangeError(self: *Lexer, range: logger.Range, comptime format: []const u8, args: anytype, panic: bool) void { - if (eql(loc, self.prev_error_loc)) { + pub fn addRangeError(self: *Lexer, r: logger.Range, comptime format: []const u8, args: anytype, panic: bool) void { + if (self.prev_error_loc.eql(r.loc)) { return; } const errorMessage = std.fmt.allocPrint(self.allocator, format, args) catch unreachable; - var msg = self.log.addRangeError(self.source, range, errorMessage); - self.prev_error_loc = loc; + var msg = self.log.addRangeError(self.source, r, errorMessage); + self.prev_error_loc = r.loc; if (panic) { self.doPanic(errorMessage); @@ -728,29 +732,44 @@ pub const Lexer = struct { } pub fn expected(self: *Lexer, token: T) void { - if (tokenToString.has(text)) { - self.expectedString(text); + if (tokenToString.get(token).len > 0) { + self.expectedString(tokenToString.get(token)); } else { self.unexpected(); } } + pub fn unexpected(lexer: *Lexer) void { + var found: string = undefined; + if (lexer.start == lexer.source.contents.len) { + found = "end of file"; + } else { + found = lexer.raw(); + } + + lexer.addRangeError(lexer.range(), "Unexpected {s}", .{found}, true); + } + pub fn raw(self: *Lexer) []const u8 { return self.source.contents[self.start..self.end]; } + pub fn isContextualKeyword(self: *Lexer, keyword: string) bool { + return strings.eql(self.raw(), keyword); + } + pub fn expectedString(self: *Lexer, text: string) void { var found = text; if (self.source.contents.len == self.start) { found = "end of file"; } - self.addRangeError(self.range(), "Expected %s but found %s", .{ text, found }, true); + self.addRangeError(self.range(), "Expected {s} but found {s}", .{ text, found }, true); } pub fn range(self: *Lexer) logger.Range { return logger.Range{ - .start = self.start, - .len = self.end - self.start, + .loc = logger.usize2Loc(self.start), + .len = std.math.lossyCast(i32, self.end - self.start), }; } diff --git a/src/js_parser.zig b/src/js_parser.zig index 746b72b76..5bd0fc8e1 100644 --- a/src/js_parser.zig +++ b/src/js_parser.zig @@ -7,7 +7,14 @@ const options = @import("options.zig"); const alloc = @import("alloc.zig"); usingnamespace @import("strings.zig"); -const Comment = js_ast._Comment; +usingnamespace js_ast.G; +const S = js_ast.S; +const B = js_ast.B; +const T = js_lexer.T; +const E = js_ast.E; +const Stmt = js_ast.Stmt; +const Expr = js_ast.Expr; +const Binding = js_ast.Binding; const locModuleScope = logger.Loc.Empty; const TempRef = struct { @@ -537,10 +544,10 @@ const P = struct { } pub fn pushScopeForVisitPass(p: *P, kind: js_ast.Scope.Kind, loc: logger.Loc) !void { - const order = try p.unshiftScopeOrder(); + var order = try p.unshiftScopeOrder(); // Sanity-check that the scopes generated by the first and second passes match - if (nql(order.loc, loc) or nql(order.scope.kind, kind)) { + if (!order.loc.eql(loc) or order.scope.kind != kind) { std.debug.panic("Expected scope ({s}, {d}) in {s}, found scope ({s}, {d})", .{ kind, loc.start, p.source.path.pretty, order.scope.kind, order.loc.start }); } @@ -591,6 +598,43 @@ const P = struct { } } + pub fn parseStmt(p: *P, opts: *ParseStatementOptions) js_ast.Stmt { + var loc = p.lexer.loc(); + var stmt: js_ast.Stmt = undefined; + + switch (p.lexer.token) { + js_lexer.T.t_semicolon => { + p.lexer.next(); + return js_ast.Stmt.init(js_ast.S.Empty{}, loc); + }, + + js_lexer.T.t_export => { + var previousExportKeyword = p.es6_export_keyword; + if (opts.is_module_scope) { + p.es6_export_keyword = p.lexer.range(); + } else if (!opts.is_namespace_scope) { + p.lexer.unexpected(); + } + p.lexer.next(); + + // TypeScript decorators only work on class declarations + // "@decorator export class Foo {}" + // "@decorator export abstract class Foo {}" + // "@decorator export default class Foo {}" + // "@decorator export default abstract class Foo {}" + // "@decorator export declare class Foo {}" + // "@decorator export declare abstract class Foo {}" + if (opts.ts_decorators != null and p.lexer.token != js_lexer.T.t_class and p.lexer.token != js_lexer.T.t_default and !p.lexer.isContextualKeyword("abstract") and !p.lexer.isContextualKeyword("declare")) { + p.lexer.expected(js_lexer.T.t_class); + } + }, + + else => {}, + } + + return stmt; + } + pub fn parseStmtsUpTo(p: *P, eend: js_lexer.T, opts: *ParseStatementOptions) ![]js_ast.Stmt { var stmts = List(js_ast.Stmt).init(p.allocator); try stmts.ensureCapacity(1); @@ -599,8 +643,20 @@ const P = struct { opts.lexical_decl = .allow_all; var isDirectivePrologue = true; - while (true) { - // var comments = p.lexer + run: while (true) { + if (p.lexer.comments_to_preserve_before) |comments| { + for (comments) |comment| { + try stmts.append(Stmt.init(S.Comment{ + .text = comment.text, + }, p.lexer.loc())); + } + } + + if (p.lexer.token == .t_end_of_file) { + break :run; + } + + var stmt = p.parseStmt(opts); } return stmts.toOwnedSlice(); diff --git a/src/logger.zig b/src/logger.zig index 7db9e4c97..e1d6d4517 100644 --- a/src/logger.zig +++ b/src/logger.zig @@ -106,56 +106,56 @@ pub const Log = struct { errors: u8 = 0, msgs: ArrayList(Msg), - pub fn addVerbose(log: *Log, source: ?Source, loc: Loc, text: []u8) void { - log.addMsg(Msg{ + pub fn addVerbose(log: *Log, source: ?Source, loc: Loc, text: []u8) !void { + try log.addMsg(Msg{ .kind = .verbose, - .data = rangeData(source, Range{ .Loc = loc }, text), + .data = rangeData(source, Range{ .loc = loc }, text), }); } - pub fn addVerboseWithNotes(source: ?Source, loc: Loc, text: []u8, notes: []Data) void { - log.addMsg(Msg{ + pub fn addVerboseWithNotes(source: ?Source, loc: Loc, text: []u8, notes: []Data) !void { + try log.addMsg(Msg{ .kind = .verbose, .data = rangeData(source, Range{ .loc = loc }, text), .notes = notes, }); } - pub fn addRangeError(log: *Log, source: ?Source, r: Range, text: []u8) void { + pub fn addRangeError(log: *Log, source: ?Source, r: Range, text: []u8) !void { log.errors += 1; - log.addMsg(Msg{ - .kind = .Error, + try log.addMsg(Msg{ + .kind = .err, .data = rangeData(source, r, text), }); } - pub fn addRangeWarning(log: *Log, source: ?Source, r: Range, text: []u8) void { + pub fn addRangeWarning(log: *Log, source: ?Source, r: Range, text: []u8) !void { log.warnings += 1; - log.addMsg(Msg{ + try log.addMsg(Msg{ .kind = .warning, .data = rangeData(source, r, text), }); } - pub fn addRangeDebug(log: *Log, source: ?Source, r: Range, text: []u8) void { - log.addMsg(Msg{ + pub fn addRangeDebug(log: *Log, source: ?Source, r: Range, text: []u8) !void { + try log.addMsg(Msg{ .kind = .debug, .data = rangeData(source, r, text), }); } - pub fn addRangeErrorWithNotes(log: *Log, source: ?Source, r: Range, text: []u8, notes: []Data) void { + pub fn addRangeErrorWithNotes(log: *Log, source: ?Source, r: Range, text: []u8, notes: []Data) !void { log.errors += 1; - log.addMsg(Msg{ + try log.addMsg(Msg{ .kind = Kind.err, .data = rangeData(source, r, text), .notes = notes, }); } - pub fn addRangeWarningWithNotes(log: *Log, source: ?Source, r: Range, text: []u8, notes: []Data) void { + pub fn addRangeWarningWithNotes(log: *Log, source: ?Source, r: Range, text: []u8, notes: []Data) !void { log.warnings += 1; - log.addMsg(Msg{ + try log.addMsg(Msg{ .kind = .warning, .data = rangeData(source, r, text), .notes = notes, |