aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2021-04-20 14:59:21 -0700
committerGravatar Jarred Sumner <jarred@jarredsumner.com> 2021-04-20 14:59:21 -0700
commit775bac7bbcf50eae02b73916e02fbd4f4e87baeb (patch)
tree23929f2ec388fa39eaedb582597001e7ccceec44
parentba1c784316a062124b56f4643af200c0508a1006 (diff)
downloadbun-775bac7bbcf50eae02b73916e02fbd4f4e87baeb.tar.gz
bun-775bac7bbcf50eae02b73916e02fbd4f4e87baeb.tar.zst
bun-775bac7bbcf50eae02b73916e02fbd4f4e87baeb.zip
wip
-rw-r--r--src/js_ast.zig178
-rw-r--r--src/js_lexer.zig45
-rw-r--r--src/js_parser.zig66
-rw-r--r--src/logger.zig32
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,