aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--.vscode/tasks.json5
-rw-r--r--src/js_ast.zig35
-rw-r--r--src/js_lexer.zig10
-rw-r--r--src/js_lexer_tables.zig2
-rw-r--r--src/js_parser.zig289
-rw-r--r--src/logger.zig20
-rw-r--r--src/main.zig29
8 files changed, 273 insertions, 119 deletions
diff --git a/.gitignore b/.gitignore
index d591ee704..201478fd8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,5 @@
zig-cache
*.wasm
+*.o
+*.a \ No newline at end of file
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index cf269bbef..29d6b3592 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -5,7 +5,7 @@
"label": "build",
"type": "shell",
"command": "zig build",
- "problemMatcher": [],
+
"group": {
"kind": "build",
"isDefault": true
@@ -24,9 +24,10 @@
},
{
"label": "test",
- "type": "process",
+ "type": "shell",
"command": "zig",
"args": ["test", "${file}", "-femit-bin=zig-cache/bin/test"],
+
"group": {
"kind": "test",
"isDefault": true
diff --git a/src/js_ast.zig b/src/js_ast.zig
index 813e76c0e..c8631ca1b 100644
--- a/src/js_ast.zig
+++ b/src/js_ast.zig
@@ -594,7 +594,7 @@ pub const E = struct {
prefer_expr: bool = false, // Use shorthand if true and "Body" is a single return statement
};
- pub const Function = Fn;
+ pub const Function = struct { func: G.Fn };
pub const Identifier = struct {
ref: Ref = Ref.None,
@@ -695,7 +695,9 @@ pub const E = struct {
value: string,
};
- pub const Class = G.Class;
+ pub const Class = struct {
+ class: G.Class,
+ };
pub const Await = struct { value: ExprNodeIndex };
@@ -732,6 +734,7 @@ pub const E = struct {
pub const Stmt = struct {
loc: logger.Loc,
data: Data,
+
pub fn empty() Stmt {
return Stmt.init(S.Empty{}, logger.Loc.Empty);
}
@@ -1094,6 +1097,9 @@ pub const Expr = struct {
E.Import => {
return Expr{ .loc = loc, .data = Data{ .e_import = data } };
},
+ E.Function => {
+ return Expr{ .loc = loc, .data = Data{ .e_function = data } };
+ },
else => {
@compileError("Invalid type passed to Expr.init");
},
@@ -1109,6 +1115,7 @@ pub const Expr = struct {
e_null,
e_undefined,
e_new,
+ e_function,
e_new_target,
e_import_meta,
e_call,
@@ -1145,6 +1152,7 @@ pub const Expr = struct {
e_undefined: E.Undefined,
e_new: E.New,
e_new_target: E.NewTarget,
+ e_function: E.Function,
e_import_meta: E.ImportMeta,
e_call: E.Call,
e_dot: E.Dot,
@@ -1431,7 +1439,7 @@ pub const Op = struct {
bin_logical_and_assign,
};
- pub const Level = enum {
+ pub const Level = enum(u8) {
lowest,
comma,
spread,
@@ -1455,6 +1463,21 @@ pub const Op = struct {
new,
call,
member,
+ pub fn lt(self: Level, b: Level) callconv(.Inline) bool {
+ return @enumToInt(self) < @enumToInt(b);
+ }
+ pub fn gt(self: Level, b: Level) callconv(.Inline) bool {
+ return @enumToInt(self) > @enumToInt(b);
+ }
+ pub fn gte(self: Level, b: Level) callconv(.Inline) bool {
+ return @enumToInt(self) >= @enumToInt(b);
+ }
+ pub fn lte(self: Level, b: Level) callconv(.Inline) bool {
+ return @enumToInt(self) <= @enumToInt(b);
+ }
+ pub fn eql(self: Level, b: Level) callconv(.Inline) bool {
+ return @enumToInt(self) == @enumToInt(b);
+ }
};
text: string,
@@ -1868,9 +1891,9 @@ test "Binding.init" {
}
test "Expr.init" {
- var ident = Expr.init(E.Identifier{}, logger.Loc{ .start = 100 });
- var list = [_]Expr{ident};
- var expr = Expr.init(
+ const ident = Expr.init(E.Identifier{}, logger.Loc{ .start = 100 });
+ const list = [_]Expr{ident};
+ const expr = Expr.init(
E.Array{ .items = list[0..] },
logger.Loc{ .start = 1 },
);
diff --git a/src/js_lexer.zig b/src/js_lexer.zig
index 56b824ffa..303f2e4f3 100644
--- a/src/js_lexer.zig
+++ b/src/js_lexer.zig
@@ -18,6 +18,7 @@ pub const CodePoint = tables.CodePoint;
pub const Keywords = tables.Keywords;
pub const tokenToString = tables.tokenToString;
pub const jsxEntity = tables.jsxEntity;
+pub const StrictModeReservedWords = tables.StrictModeReservedWords;
// TODO: JSON
const IS_JSON_FILE = false;
@@ -1179,12 +1180,13 @@ fn isWhitespace(codepoint: CodePoint) bool {
// this fn is a stub!
pub fn rangeOfIdentifier(source: *Source, loc: logger.Loc) logger.Range {
var r = logger.Range{ .loc = loc, .len = 0 };
+ const offset = @intCast(usize, loc.start);
var i: usize = 0;
- for (source.contents[loc..]) |c| {
- if (isIdentifierStart(@as(c, CodePoint))) {
- for (source.contents[loc + i ..]) |c_| {
+ for (source.contents[offset..]) |c| {
+ if (isIdentifierStart(@as(CodePoint, c))) {
+ for (source.contents[offset + i ..]) |c_| {
if (!isIdentifierContinue(c_)) {
- r.len = i;
+ r.len = std.math.lossyCast(i32, i);
return r;
}
i += 1;
diff --git a/src/js_lexer_tables.zig b/src/js_lexer_tables.zig
index 0ab696b62..b6e62ed48 100644
--- a/src/js_lexer_tables.zig
+++ b/src/js_lexer_tables.zig
@@ -178,7 +178,7 @@ pub const Keywords = std.ComptimeStringMap(T, .{
.{ "with", .t_with },
});
-pub const StrictModeReservedWords = std.ComptimeStringMap(Bool, .{
+pub const StrictModeReservedWords = std.ComptimeStringMap(bool, .{
.{ "implements", true },
.{ "interface", true },
.{ "let", true },
diff --git a/src/js_parser.zig b/src/js_parser.zig
index d48a7b0eb..ed74e3a22 100644
--- a/src/js_parser.zig
+++ b/src/js_parser.zig
@@ -12,7 +12,12 @@ usingnamespace js_ast.G;
const BindingNodeIndex = js_ast.BindingNodeIndex;
const StmtNodeIndex = js_ast.StmtNodeIndex;
const ExprNodeIndex = js_ast.ExprNodeIndex;
+const ExprNodeList = js_ast.ExprNodeList;
+const StmtNodeList = js_ast.StmtNodeList;
+const BindingNodeList = js_ast.BindingNodeList;
+const Ref = js_ast.Ref;
+const LocRef = js_ast.LocRef;
const S = js_ast.S;
const B = js_ast.B;
const G = js_ast.G;
@@ -112,13 +117,17 @@ const FnOrArrowDataParse = struct {
// Allow TypeScript decorators in function arguments
allow_ts_decorators: bool = false,
+
+ pub fn i() FnOrArrowDataParse {
+ return FnOrArrowDataParse{ .allow_await = false };
+ }
};
// This is function-specific information used during visiting. It is saved and
// restored on the call stack around code that parses nested functions and
// arrow expressions.
const FnOrArrowDataVisit = struct {
- super_index_ref: *js_ast.Ref,
+ super_index_ref: ?*js_ast.Ref = null,
is_arrow: bool = false,
is_async: bool = false,
@@ -367,7 +376,7 @@ const P = struct {
//
// var ns1;
// (function(ns2) {
- // })(ns1 || (ns1 = {}));
+ // })(ns1 or (ns1 = {}));
//
// This variable is "ns2" not "ns1". It is only used during the second
// "visit" pass.
@@ -433,11 +442,11 @@ const P = struct {
// The TypeScript compiler compiles this into the following code (notice "f"
// is missing):
//
- // var a; (function (a_1) {})(a || (a = {}));
- // var b; (function (b_1) {})(b || (b = {}));
- // var c; (function (c_1) {})(c || (c = {}));
- // var d; (function (d_1) {})(d || (d = {}));
- // var e; (function (e_1) {})(e || (e = {}));
+ // var a; (function (a_1) {})(a or (a = {}));
+ // var b; (function (b_1) {})(b or (b = {}));
+ // var c; (function (c_1) {})(c or (c = {}));
+ // var d; (function (d_1) {})(d or (d = {}));
+ // var e; (function (e_1) {})(e or (e = {}));
//
// Note that this should not be implemented by declaring symbols for "export
// declare" statements because the TypeScript compiler doesn't generate any
@@ -467,7 +476,7 @@ const P = struct {
// var ns;
// (function (ns) {
// console.log(ns.a, ns.b, c, d, e);
- // })(ns || (ns = {}));
+ // })(ns or (ns = {}));
//
// Relevant issue: https://github.com/evanw/esbuild/issues/1158
has_non_local_export_declare_inside_namespace: bool = false,
@@ -631,7 +640,7 @@ const P = struct {
// "var foo; function foo() {}"
// "function foo() {} var foo;"
// "function *foo() {} function *foo() {}" but not "{ function *foo() {} function *foo() {} }"
- if (Symbol.isKindHoistedOrFunction(new) and Symbol.isKindHoistedOrFunction(existing) and (scope.kind == .entry or scope.kind == .function_body ||
+ if (Symbol.isKindHoistedOrFunction(new) and Symbol.isKindHoistedOrFunction(existing) and (scope.kind == .entry or scope.kind == .function_body or
(Symbol.isKindHoisted(new) and Symbol.isKindHoisted(existing))))
{
return .keep_existing;
@@ -778,7 +787,7 @@ const P = struct {
}
if (errors.invalid_expr_after_question) |r| {
- p.log.addRangeError(p.source, r, "Unexpected %q", .{p.source.contents[r.loc.start..r.loc.end()]});
+ p.log.addRangeError(p.source, r, "Unexpected %q", .{p.source.contents[r.loc.start..r.loc.endI()]});
}
// if (errors.array_spread_feature) |err| {
@@ -1200,7 +1209,7 @@ const P = struct {
.reserved_word => {
text = try std.fmt.allocPrint(p.allocator, "{s} is a reserved word and", .{detail});
},
- .legacy_octal_escape => {
+ .legacy_octal_literal => {
text = "Legacy octal literals";
},
.legacy_octal_escape => {
@@ -1209,15 +1218,15 @@ const P = struct {
.if_else_function_stmt => {
text = "Function declarations inside if statements";
},
- else => {
- text = "This feature";
- },
+ // else => {
+ // text = "This feature";
+ // },
}
if (p.current_scope) |scope| {
if (p.isStrictMode()) {
var why: string = "";
- var notes: []logger.MsgData = undefined;
+ var notes: []logger.Data = undefined;
var where: logger.Range = undefined;
switch (scope.strict_mode) {
.implicit_strict_mode_import => {
@@ -1233,14 +1242,15 @@ const P = struct {
why = "All code inside a class is implicitly in strict mode";
where = p.enclosing_class_keyword;
},
+ else => {},
}
if (why.len == 0) {
- why = try std.fmt.allocPrint(p.allocator, "This file is implicitly in strict mode because of the \"{s}\" keyword here", p.source.textForRange(where));
+ why = try std.fmt.allocPrint(p.allocator, "This file is implicitly in strict mode because of the \"{s}\" keyword here", .{p.source.textForRange(where)});
}
- p.log.addRangeErrorWithNotes(p.source, r, std.fmt.allocPrint(p.allocator, "{s} cannot be used in strict mode", .{text}), []logger.Data{logger.rangeData(p.source, where, why)});
+ try p.log.addRangeErrorWithNotes(p.source, r, try std.fmt.allocPrint(p.allocator, "{s} cannot be used in strict mode", .{text}), &([_]logger.Data{logger.rangeData(p.source, where, why)}));
} else if (!can_be_transformed and p.isStrictModeOutputFormat()) {
- p.log.addRangeError(p.source, r, std.fmt.allocPrint(p.allocator, "{s} cannot be used with \"esm\" due to strict mode", .{text}));
+ try p.log.addRangeError(p.source, r, try std.fmt.allocPrint(p.allocator, "{s} cannot be used with \"esm\" due to strict mode", .{text}));
}
}
}
@@ -1258,26 +1268,22 @@ const P = struct {
// Forbid declaring a symbol with a reserved word in strict mode
if (p.isStrictMode() and js_lexer.StrictModeReservedWords.has(name)) {
- p.markStrictModeFeature(reservedWord, js_lexer.rangeOfIdentifier(p.source, loc), name);
+ try p.markStrictModeFeature(.reserved_word, js_lexer.rangeOfIdentifier(&p.source, loc), name);
}
// Allocate a new symbol
- var ref = p.newSymbol(kind, name);
+ var ref = try p.newSymbol(kind, name);
const scope = p.current_scope orelse unreachable;
if (scope.members.get(name)) |existing| {
- const symbol: Symbol = p.symbols.items[@intCast(usize, existing.ref.inner_index)];
+ var symbol: Symbol = p.symbols.items[@intCast(usize, existing.ref.inner_index)];
- switch (p.canMergeSymbols(p.current_scope, symbol.kind, kind)) {
+ switch (p.canMergeSymbols(scope, symbol.kind, kind)) {
.forbidden => {
- const r = js_lexer.rangeOfIdentifier(p.source.*, loc);
+ const r = js_lexer.rangeOfIdentifier(&p.source, loc);
var notes: []logger.Data = undefined;
- notes = []logger.Data{logger.rangeData(p.source, r, try std.fmt.allocPrint(p.allocator, "{s} has already been declared", .{name}))};
- p.log.addRangeErrorWithNotes(
- p.source,
- r,
- try std.fmt.allocPrint(p.allocator, "{s} was originally declared here", .{name}),
- );
+ notes = &([_]logger.Data{logger.rangeData(p.source, r, try std.fmt.allocPrint(p.allocator, "{s} has already been declared", .{name}))});
+ try p.log.addRangeErrorWithNotes(p.source, r, try std.fmt.allocPrint(p.allocator, "{s} was originally declared here", .{name}), notes);
return existing.ref;
},
.keep_existing => {
@@ -1294,8 +1300,9 @@ const P = struct {
ref = existing.ref;
symbol.kind = .private_static_get_set_pair;
},
- .new => {},
- else => unreachable,
+
+ .overwrite_with_new => {},
+ // else => unreachable,
}
}
@@ -1303,7 +1310,7 @@ const P = struct {
return ref;
}
- pub fn parseFnExpr(p: *P, loc: logger.Loc, is_async: bool, async_range: logger.Range) !ExprNodeIndex {
+ pub fn parseFnExpr(p: *P, loc: logger.Loc, is_async: bool, async_range: logger.Range) !Expr {
p.lexer.next();
const is_generator = p.lexer.token == T.t_asterisk;
if (is_generator) {
@@ -1315,19 +1322,19 @@ const P = struct {
var name: ?js_ast.LocRef = null;
- p.pushScopeForParsePass(.function_args, loc);
+ _ = p.pushScopeForParsePass(.function_args, loc) catch unreachable;
defer p.popScope();
if (p.lexer.token == .t_identifier) {
- name = p._(js_ast.LocRef{
+ name = js_ast.LocRef{
.loc = loc,
.ref = null,
- });
+ };
if (p.lexer.identifier.len > 0 and !strings.eql(p.lexer.identifier, "arguments")) {
- name.ref = try p.declareSymbol(.hoisted_function, name.loc, p.lexer.identifier);
+ (name orelse unreachable).ref = try p.declareSymbol(.hoisted_function, (name orelse unreachable).loc, p.lexer.identifier);
} else {
- name.ref = try p.newSymbol(.hoisted_function, p.lexer.identifier);
+ (name orelse unreachable).ref = try p.newSymbol(.hoisted_function, p.lexer.identifier);
}
p.lexer.next();
}
@@ -1336,28 +1343,30 @@ const P = struct {
p.skipTypescriptTypeParameters();
}
- var func = p.parseFn(name, FnOrDDataParse{
+ var func = p.parseFn(name, FnOrArrowDataParse{
.async_range = async_range,
.allow_await = is_async,
.allow_yield = is_generator,
});
- return _(Expr.init(js_ast.E.Function{
+ return Expr.init(js_ast.E.Function{
.func = func,
- }, loc));
+ }, loc);
}
- pub fn parseFnBody(p: *P, data: FnOrArrowDataParse) !G.FnBody {
+ pub fn parseFnBody(p: *P, data: *FnOrArrowDataParse) !G.FnBody {
var oldFnOrArrowData = p.fn_or_arrow_data_parse;
var oldAllowIn = p.allow_in;
- p.fn_or_arrow_data_parse = data;
+ p.fn_or_arrow_data_parse = data.*;
p.allow_in = true;
- p.pushScopeForParsePass(Scope.Kind.function_body, p.lexer.loc());
+ const loc = p.lexer.loc();
+ _ = try p.pushScopeForParsePass(Scope.Kind.function_body, p.lexer.loc());
defer p.popScope();
p.lexer.expect(.t_open_brace);
- const stmts = try p.parseStmtsUpTo(.t_close_brace, ParseStatementOptions{});
+ var opts = ParseStatementOptions{};
+ const stmts = p.parseStmtsUpTo(.t_close_brace, &opts) catch unreachable;
p.lexer.next();
p.allow_in = oldAllowIn;
@@ -1365,7 +1374,7 @@ const P = struct {
return G.FnBody{ .loc = loc, .stmts = stmts };
}
- pub fn parseArrowBody(p: *P, args: []js_ast.G.Arg, data: FnOrArrowDataParse) !E.Arrow {
+ pub fn parseArrowBody(p: *P, args: []js_ast.G.Arg, data: *FnOrArrowDataParse) !E.Arrow {
var arrow_loc = p.lexer.loc();
// Newlines are not allowed before "=>"
@@ -1382,51 +1391,69 @@ const P = struct {
data.allow_super_call = p.fn_or_arrow_data_parse.allow_super_call;
if (p.lexer.token == .t_open_brace) {
- var body = p.parseFnBody(data);
+ var body = try p.parseFnBody(data);
p.after_arrow_body_loc = p.lexer.loc();
- return p._(E.Arrow{ .args = args, .body = body });
+ return E.Arrow{ .args = args, .body = body };
}
- p.pushScopeForParsePass(Scope.Kind.function_body, arrow_loc);
+ _ = try p.pushScopeForParsePass(Scope.Kind.function_body, arrow_loc);
defer p.popScope();
var old_fn_or_arrow_data = p.fn_or_arrow_data_parse;
- p.fn_or_arrow_data_parse = data;
- var expr = try p.parseExpr(Level.comma);
+ p.fn_or_arrow_data_parse = data.*;
+
+ var expr = p.m(p.parseExpr(Level.comma));
p.fn_or_arrow_data_parse = old_fn_or_arrow_data;
- return E.Arrow{ .args = args, .prefer_expr = true, .body = G.FnBody{ .loc = arrow_loc, .stmts = []StmtNodeIndex{p._(Stmt.init(S.Return{ .value = expr }, arrow_loc))} } };
+ var stmts = try p.allocator.alloc(Stmt, 1);
+ stmts[0] = Stmt.init(S.Return{ .value = expr }, arrow_loc);
+
+ return E.Arrow{ .args = args, .prefer_expr = true, .body = G.FnBody{ .loc = arrow_loc, .stmts = stmts } };
}
pub fn declareBinding(p: *P, kind: Symbol.Kind, binding: BindingNodeIndex, opts: ParseStatementOptions) !void {
switch (binding.data) {
- .b_identifier, .b_missing => |b| {
- if (!opts.is_typescript_declare || (opts.is_namespace_scope and opts.is_export)) {
- b.ref = p.declareSymbol(kind, binding.loc, p.loadNameFromRef(b.ref));
+ .b_identifier => |*b| {
+ if (!opts.is_typescript_declare or (opts.is_namespace_scope and opts.is_export)) {
+ b.ref = try p.declareSymbol(kind, binding.loc, p.loadNameFromRef(b.ref));
}
},
+ .b_missing => |b| {},
- .b_array => |b| {
+ .b_array => |*b| {
for (b.items) |item| {
- p.declareBinding(kind, item.binding, opts);
+ p.declareBinding(kind, item.binding, opts) catch unreachable;
}
},
- .b_object => |b| {
+ .b_object => |*b| {
for (b.properties) |prop| {
- p.declareBinding(kind, prop.value, opts);
+ const value = prop.value orelse std.debug.panic("Internal error: property {s} is missing a binding!", .{prop});
+ p.declareBinding(kind, value, opts) catch unreachable;
}
},
else => {
- @compileError("Missing binding type");
+ // @compileError("Missing binding type");
},
}
}
// Saves us from allocating a slice to the heap
- pub fn parseArrowBodySingleArg(p: *P, arg: G.Arg, data: FnOrArrowDataParse) !E.Arrow {
- var args: []G.Arg = []G.Arg{arg};
- return p.parseArrowBody(args[0..], data);
+ pub fn parseArrowBodySingleArg(p: *P, arg: G.Arg, data: anytype) !E.Arrow {
+ switch (@TypeOf(data)) {
+ FnOrArrowDataParse => {
+ var args = [_]G.Arg{arg};
+
+ var d = data;
+
+ return p.parseArrowBody(args[0..], &d);
+ },
+ *FnOrArrowDataParse => {
+ var args = [_]G.Arg{arg};
+ return p.parseArrowBody(args[0..], data);
+ },
+ else => unreachable,
+ }
}
// This is where the allocate memory to the heap for AST objects.
@@ -1434,13 +1461,15 @@ const P = struct {
// It also swallows errors, but I think that's correct here.
// We can handle errors via the log.
// We'll have to deal with @wasmHeapGrow or whatever that thing is.
- pub fn __(self: *P, comptime ast_object_type: type, instance: anytype) callconv(.Inline) *ast_object_type {
+ pub fn mm(self: *P, comptime ast_object_type: type, instance: anytype) callconv(.Inline) *ast_object_type {
var obj = self.allocator.create(ast_object_type) catch unreachable;
- obj = instance;
+ obj.* = instance;
return obj;
}
- pub fn _(self: *P, kind: anytype) callconv(.Inline) *@TypeOf(kind) {
- return self.__(@TypeOf(kind), kind);
+
+ // mmmm memmory allocation
+ pub fn m(self: *P, kind: anytype) callconv(.Inline) *@TypeOf(kind) {
+ return self.mm(@TypeOf(kind), kind);
}
// The name is temporarily stored in the ref until the scope traversal pass
@@ -1461,21 +1490,21 @@ const P = struct {
// we can just say "yeah this is really over here at inner_index"
// .source_index being null is used to identify was this allocated or is just in the orignial thing.
// you could never do this in JavaScript!!
- const ptr0 = @ptrToInt(name);
- const ptr1 = @ptrToInt(p.source.contents);
+ const ptr0 = @ptrToInt(name.ptr);
+ const ptr1 = @ptrToInt(p.source.contents.ptr);
// Is the data in "name" a subset of the data in "p.source.Contents"?
if (ptr0 >= ptr1 and ptr0 + name.len < p.source.contents.len) {
- std.debug.print("storeNameInRef fast path");
+ std.debug.print("storeNameInRef fast path", .{});
// The name is a slice of the file contents, so we can just reference it by
// length and don't have to allocate anything. This is the common case.
//
// It's stored as a negative value so we'll crash if we try to use it. That
// way we'll catch cases where we've forgotten to call loadNameFromRef().
// The length is the negative part because we know it's non-zero.
- return js_ast.Ref{ .source_index = ptr1, .inner_index = (name.len + ptr0) };
+ return js_ast.Ref{ .source_index = @intCast(u32, ptr0), .inner_index = (@intCast(u32, name.len) + @intCast(u32, ptr0)) };
} else {
- std.debug.print("storeNameInRef slow path");
+ std.debug.print("storeNameInRef slow path", .{});
// The name is some memory allocated elsewhere. This is either an inline
// string constant in the parser or an identifier with escape sequences
// in the source code, which is very unusual. Stash it away for later.
@@ -1483,11 +1512,11 @@ const P = struct {
// allocated_names is lazily allocated
if (p.allocated_names.capacity > 0) {
- const inner_index = p.allocated_names.items.len;
+ const inner_index = @intCast(u32, p.allocated_names.items.len);
try p.allocated_names.append(name);
return js_ast.Ref{ .source_index = 0x80000000, .inner_index = inner_index };
} else {
- try p.allocated_names.initCapacity(p.allocator, 1);
+ p.allocated_names = try @TypeOf(p.allocated_names).initCapacity(p.allocator, 1);
p.allocated_names.appendAssumeCapacity(name);
return js_ast.Ref{ .source_index = 0x80000000, .inner_index = 0 };
}
@@ -1498,9 +1527,11 @@ const P = struct {
}
pub fn loadNameFromRef(p: *P, ref: js_ast.Ref) string {
- if (ref.source_index == 0x80000000) {
- return (p.allocated_names orelse unreachable)[ref.inner_index];
- } else if (ref.source_index) |source_index| {
+ if (ref.source_index) |source_index| {
+ if (source_index == 0x80000000) {
+ return p.allocated_names.items[ref.inner_index];
+ }
+
if (std.builtin.mode != std.builtin.Mode.ReleaseFast) {
std.debug.assert(ref.inner_index - source_index > 0);
}
@@ -1508,7 +1539,6 @@ const P = struct {
return p.source.contents[ref.inner_index .. ref.inner_index - source_index];
} else {
std.debug.panic("Internal error: invalid symbol reference.", .{ref});
- return p.source.contents[ref.inner_index .. ref.inner_index - source_index];
}
}
@@ -1523,31 +1553,31 @@ const P = struct {
// Check the precedence level to avoid parsing an arrow function in
// "new async () => {}". This also avoids parsing "new async()" as
// "new (async())()" instead.
- if (!p.lexer.has_newline_before and level < Level.member) {
+ if (!p.lexer.has_newline_before and level.lt(.member)) {
switch (p.lexer.token) {
// "async => {}"
.t_equals_greater_than => {
- const arg = G.Arg{ .binding = p._(Binding.init(
+ const arg = G.Arg{ .binding = p.m(Binding.init(
B.Identifier{
- .ref = p.storeNameInRef("async"),
+ .ref = try p.storeNameInRef("async"),
},
async_range.loc,
)) };
- p.pushScopeForParsePass(.function_args, async_range.loc);
+ _ = p.pushScopeForParsePass(.function_args, async_range.loc) catch unreachable;
defer p.popScope();
- var arrowBody = try p.parseArrowBodySingleArg(arg, FnOrArrowDataParse{});
- return Expr.init(arrowBody, async_range.loc);
+ var arrow_body = try p.parseArrowBodySingleArg(arg, FnOrArrowDataParse{});
+ return Expr.init(arrow_body, async_range.loc);
},
// "async x => {}"
.t_identifier => {
// p.markLoweredSyntaxFeature();
- const ref = p.storeNameInRef(p.lexer.identifier);
- var arg = G.Arg{ .binding = p._(Binding.init(B.Identifier{
+ const ref = try p.storeNameInRef(p.lexer.identifier);
+ var arg = G.Arg{ .binding = p.m(Binding.init(B.Identifier{
.ref = ref,
}, p.lexer.loc())) };
p.lexer.next();
- p.pushScopeForParsePass(.function_args, async_range.loc);
+ _ = try p.pushScopeForParsePass(.function_args, async_range.loc);
defer p.popScope();
var arrowBody = try p.parseArrowBodySingleArg(arg, FnOrArrowDataParse{
@@ -1589,13 +1619,92 @@ const P = struct {
notimpl();
}
- pub fn parseParenExpr(p: *P, loc: logger.Loc, opts: ParenExprOpts) !Expr {}
+ pub fn parseExprOrBindings(p: *P, level: Level, errors: ?*DeferredErrors) Expr {
+ return p.parseExprCommon(level, errors, Expr.Flags.none);
+ }
+
+ pub fn parseExpr(p: *P, level: Level) Expr {
+ return p.parseExprCommon(level, null, Expr.Flags.none);
+ }
+
+ pub fn parseExprWithFlags(p: *P, level: Level, flags: Expr.Flags) Expr {
+ return p.parseExprCommon(level, null, flags);
+ }
+
+ pub fn parseExprCommon(p: *P, level: Level, errors: ?*DeferredErrors, flags: Expr.Flags) Expr {
+ const had_pure_comment_before = p.lexer.has_pure_comment_before and !p.options.ignore_dce_annotations;
+ var expr = p.parsePrefix(level, errors, flags);
+
+ // There is no formal spec for "__PURE__" comments but from reverse-
+ // engineering, it looks like they apply to the next CallExpression or
+ // NewExpression. So in "/* @__PURE__ */ a().b() + c()" the comment applies
+ // to the expression "a().b()".
+ if (had_pure_comment_before and level.lt(.call)) {
+ expr = p.parseSuffix(expr, .call - 1, errors, flags);
+ switch (expr.data) {
+ .e_call => |ex| {
+ ex.can_be_unwrapped_if_unused = true;
+ },
+ .e_new => |ex| {
+ ex.can_be_unwrapped_if_unused = true;
+ },
+ else => {},
+ }
+ }
+
+ return p.parseSuffix(expr, level, errors, flags);
+ }
+
+ // This assumes that the open parenthesis has already been parsed by the caller
+ pub fn parseParenExpr(p: *P, loc: logger.Loc, opts: ParenExprOpts) !Expr {
+ var items = List(Expr).initCapacity(p.allocator, 1);
+ var errors = DeferredErrors{};
+ var arrowArgErrors = DeferredArrowArgErrors{};
+ var spread_range = logger.Range{};
+ var type_colon_range = logger.Range{};
+ var comma_after_spread = logger.Loc{};
+
+ // Push a scope assuming this is an arrow function. It may not be, in which
+ // case we'll need to roll this change back. This has to be done ahead of
+ // parsing the arguments instead of later on when we hit the "=>" token and
+ // we know it's an arrow function because the arguments may have default
+ // values that introduce new scopes and declare new symbols. If this is an
+ // arrow function, then those new scopes will need to be parented under the
+ // scope of the arrow function itself.
+ const scopeIndex = p.pushScopeForParsePass(.function_args, loc);
+
+ // Allow "in" inside parentheses
+ var oldAllowIn = p.allow_in;
+ p.allow_in = true;
+
+ // Forbid "await" and "yield", but only for arrow functions
+ var old_fn_or_arrow_data = p.fn_or_arrow_data_parse;
+ p.fn_or_arrow_data_parse.arrow_arg_errors = arrowArgErrors;
+
+ // Scan over the comma-separated arguments or expressions
+ while (p.lexer.token != .t_close_paren) {
+ const item_loc = p.lexer.loc();
+ const is_spread = p.lexer.token == .t_dot_dot_dot;
+
+ if (is_spread) {
+ spread_range = p.lexer.range();
+ // p.markSyntaxFeature()
+ p.lexer.next();
+ }
+
+ p.latest_arrow_arg_loc = p.lexer.loc();
+ // TODO: here
+ var item = p.parseExprOrBindings(.comma, &errors);
+ }
+ }
pub fn popScope(p: *P) void {
const current_scope = p.current_scope orelse unreachable;
// We cannot rename anything inside a scope containing a direct eval() call
if (current_scope.contains_direct_eval) {
- for (current_scope.members) |member| {
+ var iter = current_scope.members.iterator();
+ while (iter.next()) |member| {
+
// Using direct eval when bundling is not a good idea in general because
// esbuild must assume that it can potentially reach anything in any of
// the containing scopes. We try to make it work but this isn't possible
@@ -1641,14 +1750,14 @@ const P = struct {
// continue;
// }
- p.symbols.items[member.ref.inner_index].must_not_be_renamed = true;
+ p.symbols.items[member.value.ref.inner_index].must_not_be_renamed = true;
}
}
p.current_scope = current_scope.parent;
}
- pub fn parseSuffix(p: *P, left: Expr, level: Level, errors: ?DeferredErrors, flags: Expr.Flags) Expr {
+ pub fn parseSuffix(p: *P, left: Expr, level: Level, errors: ?*DeferredErrors, flags: Expr.Flags) Expr {
var loc = p.lexer.loc();
}
diff --git a/src/logger.zig b/src/logger.zig
index 629e146dc..933e20667 100644
--- a/src/logger.zig
+++ b/src/logger.zig
@@ -28,6 +28,10 @@ pub const Kind = enum {
pub const Loc = struct {
start: i32 = -1,
+ pub fn toUsize(self: *Loc) usize {
+ return @intCast(usize, self.start);
+ }
+
pub const Empty = Loc{ .start = -1 };
pub fn eql(loc: *Loc, other: Loc) bool {
@@ -89,18 +93,18 @@ pub const Location = struct {
pub const Data = struct { text: string, location: ?Location = null };
-pub const Msg = struct {
- kind: Kind = Kind.err,
- data: Data,
-};
+pub const Msg = struct { kind: Kind = Kind.err, data: Data, notes: ?[]Data = null };
pub const Range = struct {
loc: Loc = Loc.Empty,
len: i32 = 0,
pub const None = Range{ .loc = Loc.Empty, .len = 0 };
- pub fn end(self: *Range) Loc {
- return Loc{ .start = self.start + self.len };
+ pub fn end(self: *const Range) Loc {
+ return Loc{ .start = self.loc.start + self.len };
+ }
+ pub fn endI(self: *const Range) usize {
+ return std.math.lossyCast(usize, self.loc.start + self.len);
}
};
@@ -221,6 +225,10 @@ pub const Source = struct {
return Source{ .path = path, .identifier_name = path.name.base, .contents = contents };
}
+ pub fn textForRange(self: *Source, r: Range) string {
+ return self.contents[std.math.lossyCast(usize, r.loc.start)..r.endI()];
+ }
+
pub fn initErrorPosition(self: *const Source, _offset: Loc) ErrorPosition {
var prev_code_point: u21 = 0;
var offset: usize = if (_offset.start < 0) 0 else @intCast(usize, _offset.start);
diff --git a/src/main.zig b/src/main.zig
index e3619f8fd..73f03f9ad 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -1,18 +1,27 @@
const std = @import("std");
const lex = @import("js_lexer.zig");
+const logger = @import("logger.zig");
const alloc = @import("alloc.zig");
+const options = @import("options.zig");
+const js_parser = @import("js_parser.zig");
pub fn main() anyerror!void {
- const args = try std.process.argsAlloc(alloc.dynamic);
- const stdout = std.io.getStdOut();
- const stderr = std.io.getStdErr();
+ try alloc.setup(std.heap.page_allocator);
+ // const args = try std.process.argsAlloc(alloc.dynamic);
+ // // const stdout = std.io.getStdOut();
+ // // const stderr = std.io.getStdErr();
- if (args.len < 1) {
- const len = stderr.write("Pass a file path");
- return;
- }
+ // // if (args.len < 1) {
+ // // const len = stderr.write("Pass a file");
+ // // return;
+ // // }
- // const path = try std.fs.realpathAlloc(alloc.dynamic, args[args.len - 1]);
- // const file = try std.fs.openFileAbsolute(path, std.fs.File.OpenFlags{});
- // const bytes = try file.readToEndAlloc(alloc.dynamic, std.math.maxInt(usize));
+ // // alloc
+
+ const entryPointName = "/var/foo/index.js";
+ const code = "for (let i = 0; i < 100; i++) { console.log('hi') aposkdpoaskdpokasdpokasdpokasdpokasdpoaksdpoaksdpoaskdpoaksdpoaksdpoaskdpoaskdpoasdk; ";
+ var parser = try js_parser.Parser.init(try options.TransformOptions.initUncached(alloc.dynamic, entryPointName, code), alloc.dynamic);
+ var res = try parser.parse();
+
+ std.debug.print("{s}", .{res});
}