This commit is contained in:
Jarred Sumner
2021-04-20 14:59:21 -07:00
parent ba1c784316
commit 775bac7bbc
4 changed files with 286 additions and 35 deletions

View File

@@ -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,

View File

@@ -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),
};
}

View File

@@ -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();

View File

@@ -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,