aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/js_lexer.zig39
-rw-r--r--src/js_parser.zig111
-rw-r--r--src/logger.zig20
3 files changed, 148 insertions, 22 deletions
diff --git a/src/js_lexer.zig b/src/js_lexer.zig
index 5aeecba9e..56b824ffa 100644
--- a/src/js_lexer.zig
+++ b/src/js_lexer.zig
@@ -263,17 +263,17 @@ pub const Lexer = struct {
pub fn expect(self: *Lexer, token: T) void {
if (self.token != token) {
- lexer.expected(token);
+ self.expected(token);
}
- lexer.next();
+ self.next();
}
pub fn expectOrInsertSemicolon(lexer: *Lexer) void {
- if (lexer.token == T.semicolon || (!lexer.has_newline_before and
- lexer.token != T.close_brace and lexer.token != T.t_end_of_file))
+ if (lexer.token == T.t_semicolon or (!lexer.has_newline_before and
+ lexer.token != T.t_close_brace and lexer.token != T.t_end_of_file))
{
- lexer.expect(T.semicolon);
+ lexer.expect(T.t_semicolon);
}
}
@@ -299,6 +299,13 @@ pub const Lexer = struct {
}
}
+ pub fn expectContextualKeyword(self: *Lexer, keyword: string) void {
+ if (!self.isContextualKeyword(keyword)) {
+ self.addError(self.start, "\"{s}\"", .{keyword}, true);
+ }
+ self.next();
+ }
+
pub fn next(lexer: *Lexer) void {
lexer.has_newline_before = lexer.end == 0;
@@ -1168,6 +1175,28 @@ fn isWhitespace(codepoint: CodePoint) bool {
}
}
+// TODO: implement this to actually work right
+// this fn is a stub!
+pub fn rangeOfIdentifier(source: *Source, loc: logger.Loc) logger.Range {
+ var r = logger.Range{ .loc = loc, .len = 0 };
+ var i: usize = 0;
+ for (source.contents[loc..]) |c| {
+ if (isIdentifierStart(@as(c, CodePoint))) {
+ for (source.contents[loc + i ..]) |c_| {
+ if (!isIdentifierContinue(c_)) {
+ r.len = i;
+ return r;
+ }
+ i += 1;
+ }
+ }
+
+ i += 1;
+ }
+
+ return r;
+}
+
fn float64(num: anytype) callconv(.Inline) f64 {
return @intToFloat(f64, num);
}
diff --git a/src/js_parser.zig b/src/js_parser.zig
index 5bd0fc8e1..9236bc822 100644
--- a/src/js_parser.zig
+++ b/src/js_parser.zig
@@ -17,6 +17,10 @@ const Expr = js_ast.Expr;
const Binding = js_ast.Binding;
const locModuleScope = logger.Loc.Empty;
+fn notimpl() void {
+ std.debug.panic("Not implemented yet!!", .{});
+}
+
const TempRef = struct {
ref: js_ast.Ref,
value: *js_ast.Expr,
@@ -152,6 +156,7 @@ pub const Parser = struct {
pub const Options = struct {
jsx: options.JSX,
+ ts: bool = true,
ascii_only: bool = true,
keep_names: bool = true,
mangle_syntax: bool = false,
@@ -187,6 +192,7 @@ pub const Parser = struct {
const lexer = try js_lexer.Lexer.init(log, source, allocator);
return Parser{
.options = Options{
+ .ts = transform.ts,
.jsx = options.JSX{
.parse = true,
.factory = transform.jsx_factory,
@@ -455,17 +461,17 @@ const P = struct {
// during minification. These counts shouldn't include references inside dead
// code regions since those will be culled.
if (!p.is_control_flow_dead) {
- p.symbols[ref.InnerIndex].use_count_estimate += 1;
- var use = p.symbolUses[ref];
+ p.symbols[ref.inner_index].use_count_estimate += 1;
+ var use = p.symbol_uses[ref];
use.count_estimate += 1;
- p.symbolUses.put(ref, use);
+ p.symbol_uses.put(ref, use);
}
// The correctness of TypeScript-to-JavaScript conversion relies on accurate
// symbol use counts for the whole file, including dead code regions. This is
// tracked separately in a parser-only data structure.
- if (p.options.ts.parse) {
- p.tsUseCounts.items[ref.inner_index] += 1;
+ if (p.options.ts) {
+ p.ts_use_counts.items[ref.inner_index] += 1;
}
}
@@ -598,7 +604,35 @@ const P = struct {
}
}
- pub fn parseStmt(p: *P, opts: *ParseStatementOptions) js_ast.Stmt {
+ pub fn forbidLexicalDecl(p: *P, loc: logger.Loc) !void {
+ try p.log.addRangeError(p.source, p.lexer.range(), "Cannot use a declaration in a single-statement context");
+ }
+
+ pub fn parseFnStmt(p: *P, loc: logger.Loc, opts: *ParseStatementOptions, asyncRange: ?logger.Range) !js_ast.Stmt {
+ const isGenerator = p.lexer.token == T.t_asterisk;
+ const isAsync = asyncRange != null;
+
+ // if isGenerator {
+ // p.markSyntaxFeature(compat.Generator, p.lexer.Range())
+ // p.lexer.Next()
+ // } else if isAsync {
+ // p.markLoweredSyntaxFeature(compat.AsyncAwait, asyncRange, compat.Generator)
+ // }
+
+ switch (opts.lexical_decl) {
+ .forbid => {
+ try p.forbidLexicalDecl(loc);
+ },
+ .allow_fn_inside_if, .allow_fn_inside_label => {
+ if (opts.is_typescript_declare or isGenerator or isAsync) {
+ try p.forbidLexicalDecl(loc);
+ }
+ },
+ else => {},
+ }
+ }
+
+ pub fn parseStmt(p: *P, opts: *ParseStatementOptions) !js_ast.Stmt {
var loc = p.lexer.loc();
var stmt: js_ast.Stmt = undefined;
@@ -627,9 +661,72 @@ const P = struct {
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);
}
+
+ switch (p.lexer.token) {
+ T.t_class, T.t_const, T.t_function, T.t_var => {
+ opts.is_export = true;
+ return p.parseStmt(opts);
+ },
+
+ T.t_import => {
+ // "export import foo = bar"
+ if (p.options.ts and (opts.is_module_scope or opts.is_namespace_scope)) {
+ opts.is_export = true;
+ return p.parseStmt(opts);
+ }
+
+ p.lexer.unexpected();
+ },
+
+ T.t_enum => {
+ if (!p.options.ts) {
+ p.lexer.unexpected();
+ }
+
+ opts.is_export = true;
+ return p.parseStmt(opts);
+ },
+
+ T.t_identifier => {
+ if (p.lexer.isContextualKeyword("let")) {
+ opts.is_export = true;
+ return p.parseStmt(opts);
+ }
+
+ if (opts.is_typescript_declare and p.lexer.isContextualKeyword("as")) {
+ // "export as namespace ns;"
+ p.lexer.next();
+ p.lexer.expectContextualKeyword("namespace");
+ p.lexer.expect(T.t_identifier);
+ p.lexer.expectOrInsertSemicolon();
+
+ return Stmt.init(S.TypeScript{}, loc);
+ }
+
+ if (p.lexer.isContextualKeyword("async")) {
+ var asyncRange = p.lexer.range();
+ p.lexer.next();
+ if (p.lexer.has_newline_before) {
+ try p.log.addRangeError(p.source, asyncRange, "Unexpected newline after \"async\"");
+ }
+
+ p.lexer.expect(T.t_function);
+ opts.is_export = true;
+ return try p.parseFnStmt(loc, opts, asyncRange);
+ }
+
+ return stmt;
+ },
+
+ else => {
+ notimpl();
+ },
+ }
},
- else => {},
+ else => {
+ notimpl();
+ },
}
return stmt;
diff --git a/src/logger.zig b/src/logger.zig
index e1d6d4517..1e0670c92 100644
--- a/src/logger.zig
+++ b/src/logger.zig
@@ -87,7 +87,7 @@ pub const Location = struct {
}
};
-pub const Data = struct { text: []u8, location: ?Location = null };
+pub const Data = struct { text: string, location: ?Location = null };
pub const Msg = struct {
kind: Kind = Kind.err,
@@ -106,14 +106,14 @@ pub const Log = struct {
errors: u8 = 0,
msgs: ArrayList(Msg),
- pub fn addVerbose(log: *Log, source: ?Source, loc: Loc, text: []u8) !void {
+ pub fn addVerbose(log: *Log, source: ?Source, loc: Loc, text: string) !void {
try log.addMsg(Msg{
.kind = .verbose,
.data = rangeData(source, Range{ .loc = loc }, text),
});
}
- pub fn addVerboseWithNotes(source: ?Source, loc: Loc, text: []u8, notes: []Data) !void {
+ pub fn addVerboseWithNotes(source: ?Source, loc: Loc, text: string, notes: []Data) !void {
try log.addMsg(Msg{
.kind = .verbose,
.data = rangeData(source, Range{ .loc = loc }, text),
@@ -121,7 +121,7 @@ pub const Log = struct {
});
}
- pub fn addRangeError(log: *Log, source: ?Source, r: Range, text: []u8) !void {
+ pub fn addRangeError(log: *Log, source: ?Source, r: Range, text: string) !void {
log.errors += 1;
try log.addMsg(Msg{
.kind = .err,
@@ -129,7 +129,7 @@ pub const Log = struct {
});
}
- pub fn addRangeWarning(log: *Log, source: ?Source, r: Range, text: []u8) !void {
+ pub fn addRangeWarning(log: *Log, source: ?Source, r: Range, text: string) !void {
log.warnings += 1;
try log.addMsg(Msg{
.kind = .warning,
@@ -137,14 +137,14 @@ pub const Log = struct {
});
}
- pub fn addRangeDebug(log: *Log, source: ?Source, r: Range, text: []u8) !void {
+ pub fn addRangeDebug(log: *Log, source: ?Source, r: Range, text: string) !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: string, notes: []Data) !void {
log.errors += 1;
try log.addMsg(Msg{
.kind = Kind.err,
@@ -153,7 +153,7 @@ pub const Log = struct {
});
}
- pub fn addRangeWarningWithNotes(log: *Log, source: ?Source, r: Range, text: []u8, notes: []Data) !void {
+ pub fn addRangeWarningWithNotes(log: *Log, source: ?Source, r: Range, text: string, notes: []Data) !void {
log.warnings += 1;
try log.addMsg(Msg{
.kind = .warning,
@@ -168,7 +168,7 @@ pub const Log = struct {
}
// TODO:
- pub fn addError(self: *Log, _source: ?Source, loc: Loc, text: []u8) !void {
+ pub fn addError(self: *Log, _source: ?Source, loc: Loc, text: string) !void {
self.errors += 1;
try self.addMsg(Msg{ .kind = .err, .data = rangeData(_source, Range{ .loc = loc }, text) });
}
@@ -281,7 +281,7 @@ pub const Source = struct {
}
};
-fn rangeData(source: ?Source, r: Range, text: []u8) Data {
+fn rangeData(source: ?Source, r: Range, text: string) Data {
return Data{ .text = text, .location = Location.init_or_nil(source, r) };
}