mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
wip
This commit is contained in:
178
src/js_ast.zig
178
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,
|
||||
|
||||
@@ -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),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user