aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2021-04-18 15:24:24 -0700
committerGravatar Jarred Sumner <jarred@jarredsumner.com> 2021-04-18 15:24:24 -0700
commit41367499de215d8125310118ef82737c9aca8a57 (patch)
treeb0961c395e9228220a474e1385056c30f501ac17 /src
parent025fe36defe2468ca1ed224855aa1effa09001ca (diff)
downloadbun-41367499de215d8125310118ef82737c9aca8a57.tar.gz
bun-41367499de215d8125310118ef82737c9aca8a57.tar.zst
bun-41367499de215d8125310118ef82737c9aca8a57.zip
wip wip
Diffstat (limited to 'src')
-rw-r--r--src/alloc.zig27
-rw-r--r--src/bundler.zig10
-rw-r--r--src/fs.zig76
-rw-r--r--src/import_record.zig64
-rw-r--r--src/js_ast.zig390
-rw-r--r--src/js_lexer.zig156
-rw-r--r--src/js_lexer_tables.zig714
-rw-r--r--src/js_parser.zig10
-rw-r--r--src/lexer/js_lexer.zig176
-rw-r--r--src/logger.zig118
-rw-r--r--src/logger/logger.zig103
-rw-r--r--src/main.zig3
-rw-r--r--src/options.zig31
-rw-r--r--src/strings.zig8
-rw-r--r--src/test/fixtures.zig9
-rw-r--r--src/test/fixtures/noop.js1
-rw-r--r--src/test/fixtures/package.json7
-rw-r--r--src/test/fixtures/simple-component.js5
-rw-r--r--src/test/fixtures/simple-component.tsx9
-rw-r--r--src/test/fixtures/tsconfig.json9
20 files changed, 1646 insertions, 280 deletions
diff --git a/src/alloc.zig b/src/alloc.zig
new file mode 100644
index 000000000..3fe8f1307
--- /dev/null
+++ b/src/alloc.zig
@@ -0,0 +1,27 @@
+const std = @import("std");
+
+const STATIC_MEMORY_SIZE = 256000;
+pub var static_manager: ?std.heap.FixedBufferAllocator = null;
+pub var dynamic_manager: ?std.heap.ArenaAllocator = null;
+pub var root_manager: ?std.heap.ArenaAllocator = null;
+pub var static: *std.mem.Allocator = undefined;
+pub var dynamic: *std.mem.Allocator = undefined;
+
+pub fn setup(root: *std.mem.Allocator) !void {
+ root_manager = std.heap.ArenaAllocator.init(root);
+ var buf = try root_manager.?.child_allocator.alloc(u8, STATIC_MEMORY_SIZE);
+ dynamic_manager = std.heap.ArenaAllocator.init(root_manager.?.child_allocator);
+ static_manager = std.heap.FixedBufferAllocator.init(buf);
+ static = root_manager.?.child_allocator;
+
+ dynamic_manager = std.heap.ArenaAllocator.init(root);
+ dynamic = dynamic_manager.?.child_allocator;
+
+ // static = @ptrCast(*std.mem.Allocator, &stat.allocator);
+}
+
+test "GlobalAllocator" {
+ try setup(std.heap.page_allocator);
+ var testType = try static.alloc(u8, 10);
+ testType[1] = 1;
+}
diff --git a/src/bundler.zig b/src/bundler.zig
new file mode 100644
index 000000000..b249c266a
--- /dev/null
+++ b/src/bundler.zig
@@ -0,0 +1,10 @@
+const std = @import("std");
+const options = @import("options.zig");
+
+pub const Bundler = struct {
+ options: options.TransformOptions,
+
+
+ pub fn
+
+} \ No newline at end of file
diff --git a/src/fs.zig b/src/fs.zig
new file mode 100644
index 000000000..c6e353d9b
--- /dev/null
+++ b/src/fs.zig
@@ -0,0 +1,76 @@
+const std = @import("std");
+const strings = @import("strings.zig");
+const expect = std.testing.expect;
+
+pub const FileSystem = struct { tree: std.AutoHashMap(FileSystemEntry) };
+
+pub const FileSystemEntry = union(enum) {
+ file: File,
+ directory: Directory,
+};
+
+pub const File = struct { path: Path, mtime: ?usize, contents: ?[]u8 };
+pub const Directory = struct { path: Path, mtime: ?usize, contents: []FileSystemEntry };
+
+pub const PathName = struct {
+ base: []u8,
+ dir: []u8,
+ ext: []u8,
+
+ pub fn init(_path: []u8) PathName {
+ var path = _path;
+ var base: []u8 = path;
+ var dir: []u8 = path;
+ var ext: []u8 = path;
+
+ var _i = strings.lastIndexOfChar(path, '/');
+ while (_i) |i| {
+ // Stop if we found a non-trailing slash
+ if (i + 1 != path.len) {
+ base = path[i + 1 ..];
+ dir = path[0..i];
+ break;
+ }
+
+ // Ignore trailing slashes
+ path = path[0..i];
+
+ _i = strings.lastIndexOfChar(path, '/');
+ }
+
+ // Strip off the extension
+ var _dot = strings.lastIndexOfChar(base, '.');
+ if (_dot) |dot| {
+ ext = base[dot..];
+ base = base[0..dot];
+ }
+
+ return PathName{
+ .dir = dir,
+ .base = base,
+ .ext = ext,
+ };
+ }
+};
+
+pub const Path = struct {
+ pretty_path: []u8,
+ text: []u8,
+ namespace: []u8,
+ path_disabled: []u8,
+
+ pub fn isBefore(a: *Path, b: Path) bool {
+ return a.namespace > b.namespace ||
+ (a.namespace == b.namespace and (a.text < b.text ||
+ (a.text == b.text and (a.flags < b.flags ||
+ (a.flags == b.flags)))));
+ }
+};
+
+test "PathName.init" {
+ var file = "/root/directory/file.ext".*;
+ const res = PathName.init(&file);
+ std.testing.expectEqualStrings(res.dir, "/root/directory");
+ std.testing.expectEqualStrings(res.base, "file");
+ std.testing.expectEqualStrings(res.ext, ".ext");
+}
diff --git a/src/import_record.zig b/src/import_record.zig
new file mode 100644
index 000000000..baa390ac4
--- /dev/null
+++ b/src/import_record.zig
@@ -0,0 +1,64 @@
+const fs = @import("fs.zig");
+const logger = @import("logger.zig");
+
+export const ImportKind = enum(u8) {
+
+ // An entry point provided by the user
+ entry_point,
+
+ // An ES6 import or re-export statement
+ stmt,
+
+ // A call to "require()"
+ require,
+
+ // An "import()" expression with a string argument
+ dynamic,
+
+ // A call to "require.resolve()"
+ require_resolve,
+
+ // A CSS "@import" rule
+ at,
+
+ // A CSS "@import" rule with import conditions
+ at_conditional,
+
+ // A CSS "url(...)" token
+ url,
+};
+
+pub const ImportRecord = struct {
+ range: logger.Range,
+ path: fs.Path,
+
+ // Sometimes the parser creates an import record and decides it isn't needed.
+ // For example, TypeScript code may have import statements that later turn
+ // out to be type-only imports after analyzing the whole file.
+ is_unused: bool,
+
+ // If this is true, the import contains syntax like "* as ns". This is used
+ // to determine whether modules that have no exports need to be wrapped in a
+ // CommonJS wrapper or not.
+ contains_import_star: bool,
+
+ // If this is true, the import contains an import for the alias "default",
+ // either via the "import x from" or "import {default as x} from" syntax.
+ contains_default_alias: bool,
+
+ // If true, this "export * from 'path'" statement is evaluated at run-time by
+ // calling the "__reExport()" helper function
+ calls_run_time_re_export_fn: bool,
+
+ // Tell the printer to wrap this call to "require()" in "__toModule(...)"
+ wrap_with_to_module: bool,
+
+ // True for require calls like this: "try { require() } catch {}". In this
+ // case we shouldn't generate an error if the path could not be resolved.
+ is_inside_try_body: bool,
+
+ // If true, this was originally written as a bare "import 'file'" statement
+ was_originally_bare_import: bool,
+
+ kind: ImportKind,
+};
diff --git a/src/js_ast.zig b/src/js_ast.zig
new file mode 100644
index 000000000..cb2f2b150
--- /dev/null
+++ b/src/js_ast.zig
@@ -0,0 +1,390 @@
+const std = @import("std");
+const logger = @import("logger.zig");
+
+pub const NodeIndex = u32;
+pub const NodeIndexNone = 4294967293;
+
+pub const DataIndex = u16;
+pub const DataIndexNone = 65533;
+
+pub const BindingNodeIndex = NodeIndex;
+pub const StmtNodeIndex = NodeIndex;
+pub const ExprNodeIndex = NodeIndex;
+
+pub const Comment = struct { text: []u8 };
+
+pub const FnBody = struct {
+ loc: logger.Loc,
+ stmts: []StmtNodeIndex,
+};
+
+pub const Fn = struct {
+ name: NodeIndex = NodeIndexNone,
+ open_parens_loc: logger.Loc,
+ args: []Arg,
+ body: FnBody,
+
+ is_async: bool,
+ is_generator: bool,
+ has_rest_arg: bool,
+ has_if_scope: bool,
+
+ // This is true if the function is a method
+ is_unique_formal_parameters: bool,
+};
+
+pub const BindingType = enum {
+ b_missing,
+ b_identifier,
+ b_array,
+ b_object,
+};
+
+pub const Property = struct {
+ pub const Kind = enum {
+ normal,
+ get,
+ set,
+ spread,
+ };
+
+ key: NodeIndex,
+ value: NodeIndex = NodeIndexNone,
+ initializer: Kind = Kind.normal,
+ is_computed: bool,
+ is_method: bool,
+ is_static: bool,
+ was_shorthand: bool,
+};
+
+pub const Arg = struct {
+ ts_decorators: []NodeIndex,
+ binding: Binding,
+ default: NodeIndex = NodeIndexNone,
+
+ // "constructor(public x: boolean) {}"
+ is_typescript_ctor_field: bool,
+};
+
+pub const Try = struct {};
+pub const Binding = struct {};
+
+pub const Class = struct {
+ class_keyword: logger.Range,
+ ts_decorators: []NodeIndex,
+ name: logger.Loc,
+ extends: NodeIndex = NodeIndexNone,
+ body_loc: logger.Loc,
+ properties: []Property,
+};
+
+pub const Expr = struct {
+ pub const Array = struct {
+ items: []ExprNodeIndex,
+ comma_after_spread: logger.Loc,
+ is_parenthesized: bool,
+ };
+
+ pub const Unary = struct {
+ op: Op.Code,
+ };
+
+ // TODO: THIS IS WHERE YOU LEFT OFF!
+ // pub const Binary = {}
+};
+
+pub const Op = struct {
+ // If you add a new token, remember to add it to "OpTable" too
+ const Code = enum {
+ // Prefix
+ un_pos,
+ un_neg,
+ un_cpl,
+ un_not,
+ un_void,
+ un_typeof,
+ un_delete,
+
+ // Prefix update
+ un_pre_dec,
+ un_pre_inc,
+
+ // Postfix update
+ un_post_dec,
+ un_post_inc,
+
+ // Left-associative
+ bin_add,
+ bin_sub,
+ bin_mul,
+ bin_div,
+ bin_rem,
+ bin_pow,
+ bin_lt,
+ bin_le,
+ bin_gt,
+ bin_ge,
+ bin_in,
+ bin_instanceof,
+ bin_shl,
+ bin_shr,
+ bin_u_shr,
+ bin_loose_eq,
+ bin_loose_ne,
+ bin_strict_eq,
+ bin_strict_ne,
+ bin_nullish_coalescing,
+ bin_logical_or,
+ bin_logical_and,
+ bin_bitwise_or,
+ bin_bitwise_and,
+ bin_bitwise_xor,
+
+ // Non-associative
+ bin_comma,
+
+ // Right-associative
+ bin_assign,
+ bin_add_assign,
+ bin_sub_assign,
+ bin_mul_assign,
+ bin_div_assign,
+ bin_rem_assign,
+ bin_pow_assign,
+ bin_shl_assign,
+ bin_shr_assign,
+ bin_u_shr_assign,
+ bin_bitwise_or_assign,
+ bin_bitwise_and_assign,
+ bin_bitwise_xor_assign,
+ bin_nullish_coalescing_assign,
+ bin_logical_or_assign,
+ bin_logical_and_assign,
+ };
+
+ const Level = enum {
+ lowest,
+ comma,
+ spread,
+ yield,
+ assign,
+ conditional,
+ nullish_coalescing,
+ logical_or,
+ logical_and,
+ bitwise_or,
+ bitwise_xor,
+ bitwise_and,
+ equals,
+ compare,
+ shift,
+ add,
+ multiply,
+ exponentiation,
+ prefix,
+ postfix,
+ new,
+ call,
+ member,
+ };
+
+ text: string,
+ level: Level,
+ is_keyword: bool,
+
+ const Table = []Op{
+ // Prefix
+ .{ "+", Level.prefix, false },
+ .{ "-", Level.prefix, false },
+ .{ "~", Level.prefix, false },
+ .{ "!", Level.prefix, false },
+ .{ "void", Level.prefix, true },
+ .{ "typeof", Level.prefix, true },
+ .{ "delete", Level.prefix, true },
+
+ // Prefix update
+ .{ "--", Level.prefix, false },
+ .{ "++", Level.prefix, false },
+
+ // Postfix update
+ .{ "--", Level.postfix, false },
+ .{ "++", Level.postfix, false },
+
+ // Left-associative
+ .{ "+", Level.add, false },
+ .{ "-", Level.add, false },
+ .{ "*", Level.multiply, false },
+ .{ "/", Level.multiply, false },
+ .{ "%", Level.multiply, false },
+ .{ "**", Level.exponentiation, false }, // Right-associative
+ .{ "<", Level.compare, false },
+ .{ "<=", Level.compare, false },
+ .{ ">", Level.compare, false },
+ .{ ">=", Level.compare, false },
+ .{ "in", Level.compare, true },
+ .{ "instanceof", Level.compare, true },
+ .{ "<<", Level.shift, false },
+ .{ ">>", Level.shift, false },
+ .{ ">>>", Level.shift, false },
+ .{ "==", Level.equals, false },
+ .{ "!=", Level.equals, false },
+ .{ "===", Level.equals, false },
+ .{ "!==", Level.equals, false },
+ .{ "??", Level.nullish_coalescing, false },
+ .{ "||", Level.logical_or, false },
+ .{ "&&", Level.logical_and, false },
+ .{ "|", Level.bitwise_or, false },
+ .{ "&", Level.bitwise_and, false },
+ .{ "^", Level.bitwise_xor, false },
+
+ // Non-associative
+ .{ ",", LComma, false },
+
+ // Right-associative
+ .{ "=", Level.assign, false },
+ .{ "+=", Level.assign, false },
+ .{ "-=", Level.assign, false },
+ .{ "*=", Level.assign, false },
+ .{ "/=", Level.assign, false },
+ .{ "%=", Level.assign, false },
+ .{ "**=", Level.assign, false },
+ .{ "<<=", Level.assign, false },
+ .{ ">>=", Level.assign, false },
+ .{ ">>>=", Level.assign, false },
+ .{ "|=", Level.assign, false },
+ .{ "&=", Level.assign, false },
+ .{ "^=", Level.assign, false },
+ .{ "??=", Level.assign, false },
+ .{ "||=", Level.assign, false },
+ .{ "&&=", Level.assign, false },
+ };
+};
+
+pub const ArrayBinding = struct {
+ binding: BindingNodeIndex,
+ default_value: ExprNodeIndex = NodeIndexNone,
+};
+
+pub const Node = struct {
+ pub const Tag = enum {
+ s_block,
+ s_comment,
+ s_debugger,
+ s_directive,
+ s_empty,
+ s_type_script,
+ s_export_clause,
+ s_export_from,
+ s_export_default,
+ s_export_star,
+ s_export_equals,
+ s_lazy_export,
+ s_expr,
+ s_enum,
+ s_namespace,
+ s_function,
+ s_class,
+ s_label,
+ s_if,
+ s_for,
+ s_for_in,
+ s_for_of,
+ s_do_while,
+ s_while,
+ s_with,
+ s_try,
+ s_switch,
+ s_import,
+ s_return,
+ s_throw,
+ s_local,
+ s_break,
+ s_continue,
+
+ e_array,
+ e_unary,
+ e_binary,
+ e_boolean,
+ e_super,
+ e_null,
+ e_undefined,
+ e_this,
+ e_new,
+ e_new_target,
+ e_import_meta,
+ e_call,
+ e_dot,
+ e_index,
+ e_arrow,
+ e_function,
+ e_class,
+ e_identifier,
+ e_import_identifier,
+ e_private_identifier,
+ ejsx_element,
+ e_missing,
+ e_number,
+ e_big_int,
+ e_object,
+ e_spread,
+ e_string,
+ e_template,
+ e_reg_exp,
+ e_await,
+ e_yield,
+ e_if,
+ e_require,
+ e_require_resolve,
+ e_import,
+ };
+
+ // Source code location of the AST node.
+ loc: logger.Loc,
+ // this is relatively common.
+ is_single_line: bool,
+
+ //
+ child: NodeIndex = NodeIndexNone,
+ extra_data: ?[]NodeIndex,
+ data_index: u16,
+};
+
+pub const AST = struct {
+ node_tags: std.ArrayList(Node.Tag),
+};
+
+pub const Span = struct {
+ text: []u8,
+ range: logger.Range,
+};
+
+pub const ExportsKind = enum {
+// This file doesn't have any kind of export, so it's impossible to say what
+// kind of file this is. An empty file is in this category, for example.
+none,
+
+// The exports are stored on "module" and/or "exports". Calling "require()"
+// on this module returns "module.exports". All imports to this module are
+// allowed but may return undefined.
+cjs,
+
+// All export names are known explicitly. Calling "require()" on this module
+// generates an exports object (stored in "exports") with getters for the
+// export names. Named imports to this module are only allowed if they are
+// in the set of export names.
+esm,
+
+// Some export names are known explicitly, but others fall back to a dynamic
+// run-time object. This is necessary when using the "export * from" syntax
+// with either a CommonJS module or an external module (i.e. a module whose
+// export names are not known at compile-time).
+//
+// Calling "require()" on this module generates an exports object (stored in
+// "exports") with getters for the export names. All named imports to this
+// module are allowed. Direct named imports reference the corresponding export
+// directly. Other imports go through property accesses on "exports".
+esm_with_dyn };
+
+pub fn isDynamicExport(exp: ExportsKind) bool {
+ return kind == .cjs || kind == .esm_with_dyn;
+}
diff --git a/src/js_lexer.zig b/src/js_lexer.zig
new file mode 100644
index 000000000..41c5a084e
--- /dev/null
+++ b/src/js_lexer.zig
@@ -0,0 +1,156 @@
+const std = @import("std");
+const logger = @import("logger.zig");
+const tables = @import("js_lexer_tables.zig");
+const unicode = std.unicode;
+
+const Source = logger.Source;
+pub const T = tables.T;
+pub const CodePoint = tables.CodePoint;
+pub const Keywords = tables.Keywords;
+pub const tokenToString = tables.tokenToString;
+pub const jsxEntity = tables.jsxEntity;
+
+pub const Lexer = struct {
+ log: logger.Log,
+ source: logger.Source,
+ current: usize = 0,
+ start: usize = 0,
+ end: usize = 0,
+ approximate_newline_count: i32 = 0,
+ legacy_octal_loc: logger.Loc = 0,
+ previous_backslash_quote_in_jsx: logger.Range = logger.Range{},
+ token: T = T.t_end_of_file,
+ has_newline_before: bool = false,
+ has_pure_comment_before: bool = false,
+ preserve_all_comments_before: bool = false,
+ is_legacy_octal_literal: bool = false,
+ // comments_to_preserve_before: []js_ast.Comment,
+ // all_original_comments: []js_ast.Comment,
+ code_point: CodePoint = 0,
+ string_literal: []u16,
+ identifier: []u8 = "",
+ // jsx_factory_pragma_comment: js_ast.Span,
+ // jsx_fragment_pragma_comment: js_ast.Span,
+ // source_mapping_url: js_ast.Span,
+ number: f64 = 0.0,
+ rescan_close_brace_as_template_token: bool = false,
+ for_global_name: bool = false,
+ prev_error_loc: i32 = -1,
+ fn nextCodepointSlice(it: *Lexer) callconv(.Inline) ?[]const u8 {
+ if (it.current >= it.source.contents.len) {
+ return null;
+ }
+
+ const cp_len = unicode.utf8ByteSequenceLength(it.source.contents[it.current]) catch unreachable;
+ it.end = it.current;
+ it.current += cp_len;
+
+ return it.source.contents[it.current - cp_len .. it.current];
+ }
+
+ pub fn addError(self: *Lexer, loc: logger.Loc, text: []u8) void {
+ if (loc == self.prevErrorLoc) {
+ return;
+ }
+
+ self.prev_error_loc = loc;
+ }
+
+ pub fn codePointEql(self: *Lexer, a: u8) bool {
+ return @intCast(CodePoint, a) == self.code_point;
+ }
+
+ fn nextCodepoint(it: *Lexer) callconv(.Inline) CodePoint {
+ const slice = it.nextCodepointSlice() orelse return @as(CodePoint, 0);
+
+ switch (slice.len) {
+ 1 => return @as(CodePoint, slice[0]),
+ 2 => return @as(CodePoint, unicode.utf8Decode2(slice) catch unreachable),
+ 3 => return @as(CodePoint, unicode.utf8Decode3(slice) catch unreachable),
+ 4 => return @as(CodePoint, unicode.utf8Decode4(slice) catch unreachable),
+ else => unreachable,
+ }
+ }
+
+ /// Look ahead at the next n codepoints without advancing the iterator.
+ /// If fewer than n codepoints are available, then return the remainder of the string.
+ fn peek(it: *Lexer, n: usize) []const u8 {
+ const original_i = it.current;
+ defer it.current = original_i;
+
+ var end_ix = original_i;
+ var found: usize = 0;
+ while (found < n) : (found += 1) {
+ const next_codepoint = it.nextCodepointSlice() orelse return it.source.contents[original_i..];
+ end_ix += next_codepoint.len;
+ }
+
+ return it.source.contents[original_i..end_ix];
+ }
+
+ fn step(lexer: *Lexer) void {
+ lexer.code_point = lexer.nextCodepoint();
+
+ // Track the approximate number of newlines in the file so we can preallocate
+ // the line offset table in the printer for source maps. The line offset table
+ // is the #1 highest allocation in the heap profile, so this is worth doing.
+ // This count is approximate because it handles "\n" and "\r\n" (the common
+ // cases) but not "\r" or "\u2028" or "\u2029". Getting this wrong is harmless
+ // because it's only a preallocation. The array will just grow if it's too small.
+ if (lexer.code_point == '\n') {
+ lexer.approximate_newline_count += 1;
+ }
+ }
+
+ pub fn expect(self: *Lexer, token: T) void {
+ if (self.token != token) {
+ lexer.expected(token);
+ }
+
+ lexer.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))
+ {
+ lexer.expect(T.semicolon);
+ }
+ }
+
+ pub fn next(self: *Lexer) void {}
+
+ pub fn init(log: logger.Log, source: logger.Source) Lexer {
+ var string_literal = [1]u16{0};
+
+ var lex = Lexer{
+ .log = log,
+ .source = source,
+ .string_literal = &string_literal,
+ .prev_error_loc = -1,
+ };
+ lex.step();
+ lex.next();
+
+ return lex;
+ }
+};
+
+test "Lexer.step()" {
+ const msgs = std.ArrayList(logger.Msg).init(std.testing.allocator);
+ const log = logger.Log{
+ .msgs = msgs,
+ };
+
+ var sourcefile = "for (let i = 0; i < 100; i++) { console.log('hi'); }".*;
+ var identifier_name = "loop".*;
+ defer std.testing.allocator.free(msgs.items);
+ const source = logger.Source{ .index = 0, .contents = &sourcefile, .identifier_name = &identifier_name };
+
+ var lex = Lexer.init(log, source);
+ std.testing.expect('f' == lex.code_point);
+ lex.step();
+ std.testing.expect('o' == lex.code_point);
+ lex.step();
+ std.testing.expect('r' == lex.code_point);
+}
diff --git a/src/js_lexer_tables.zig b/src/js_lexer_tables.zig
new file mode 100644
index 000000000..916323f4f
--- /dev/null
+++ b/src/js_lexer_tables.zig
@@ -0,0 +1,714 @@
+const std = @import("std");
+const expectString = std.testing.expectEqualStrings;
+const expect = std.testing.expect;
+const logger = @import("logger.zig");
+const unicode = std.unicode;
+const alloc = @import("alloc.zig");
+
+pub const T = enum(u8) {
+ t_end_of_file,
+ t_syntax_error,
+
+ // "#!/usr/bin/env node"
+ t_hashbang,
+
+ // literals
+ t_no_substitution_template_literal, // contents are in lexer.string_literal ([]uint16)
+ t_numeric_literal, // contents are in lexer.number (float64)
+ t_string_literal, // contents are in lexer.string_literal ([]uint16)
+ t_big_integer_literal, // contents are in lexer.identifier (string)
+
+ // pseudo-literals
+ t_template_head, // contents are in lexer.string_literal ([]uint16)
+ t_template_middle, // contents are in lexer.string_literal ([]uint16)
+ t_template_tail, // contents are in lexer.string_literal ([]uint16)
+
+ // punctuation
+ t_ampersand,
+ t_ampersand_ampersand,
+ t_asterisk,
+ t_asterisk_asterisk,
+ t_at,
+ t_bar,
+ t_bar_bar,
+ t_caret,
+ t_close_brace,
+ t_close_bracket,
+ t_close_paren,
+ t_colon,
+ t_comma,
+ t_dot,
+ t_dot_dot_dot,
+ t_equals_equals,
+ t_equals_equals_equals,
+ t_equals_greater_than,
+ t_exclamation,
+ t_exclamation_equals,
+ t_exclamation_equals_equals,
+ t_greater_than,
+ t_greater_than_equals,
+ t_greater_than_greater_than,
+ t_greater_than_greater_than_greater_than,
+ t_less_than,
+ t_less_than_equals,
+ t_less_than_less_than,
+ t_minus,
+ t_minus_minus,
+ t_open_brace,
+ t_open_bracket,
+ t_open_paren,
+ t_percent,
+ t_plus,
+ t_plus_plus,
+ t_question,
+ t_question_dot,
+ t_question_question,
+ t_semicolon,
+ t_slash,
+ t_tilde,
+
+ // assignments (keep in sync with is_assign() below)
+ t_ampersand_ampersand_equals,
+ t_ampersand_equals,
+ t_asterisk_asterisk_equals,
+ t_asterisk_equals,
+ t_bar_bar_equals,
+ t_bar_equals,
+ t_caret_equals,
+ t_equals,
+ t_greater_than_greater_than_equals,
+ t_greater_than_greater_than_greater_than_equals,
+ t_less_than_less_than_equals,
+ t_minus_equals,
+ t_percent_equals,
+ t_plus_equals,
+ t_question_question_equals,
+ t_slash_equals,
+
+ // class-private fields and methods
+ t_private_identifier,
+
+ // identifiers
+ t_identifier, // contents are in lexer.identifier (string)
+ t_escaped_keyword, // a keyword that has been escaped as an identifer
+
+ // reserved words
+ t_break,
+ t_case,
+ t_catch,
+ t_class,
+ t_const,
+ t_continue,
+ t_debugger,
+ t_default,
+ t_delete,
+ t_do,
+ t_else,
+ t_enum,
+ t_export,
+ t_extends,
+ t_false,
+ t_finally,
+ t_for,
+ t_function,
+ t_if,
+ t_import,
+ t_in,
+ t_instanceof,
+ t_new,
+ t_null,
+ t_return,
+ t_super,
+ t_switch,
+ t_this,
+ t_throw,
+ t_true,
+ t_try,
+ t_typeof,
+ t_var,
+ t_void,
+ t_while,
+ t_with,
+
+ pub fn isAssign() bool {
+ return self >= T.t_ampersand_ampersand_equals and self <= T.t_slash_equals;
+ }
+
+ pub fn isReservedWord() bool {
+ return self >= T.t_break and self <= T.t_with;
+ }
+};
+
+pub const Keywords = std.ComptimeStringMap(T, .{
+ .{ "break", .t_break },
+ .{ "case", .t_case },
+ .{ "catch", .t_catch },
+ .{ "class", .t_class },
+ .{ "const", .t_const },
+ .{ "continue", .t_continue },
+ .{ "debugger", .t_debugger },
+ .{ "default", .t_default },
+ .{ "delete", .t_delete },
+ .{ "do", .t_do },
+ .{ "else", .t_else },
+ .{ "enum", .t_enum },
+ .{ "export", .t_export },
+ .{ "extends", .t_extends },
+ .{ "false", .t_false },
+ .{ "finally", .t_finally },
+ .{ "for", .t_for },
+ .{ "function", .t_function },
+ .{ "if", .t_if },
+ .{ "import", .t_import },
+ .{ "in", .t_in },
+ .{ "instanceof", .t_instanceof },
+ .{ "new", .t_new },
+ .{ "null", .t_null },
+ .{ "return", .t_return },
+ .{ "super", .t_super },
+ .{ "switch", .t_switch },
+ .{ "this", .t_this },
+ .{ "throw", .t_throw },
+ .{ "true", .t_true },
+ .{ "try", .t_try },
+ .{ "typeof", .t_typeof },
+ .{ "var", .t_var },
+ .{ "void", .t_void },
+ .{ "while", .t_while },
+ .{ "with", .t_with },
+});
+
+pub const CodePoint = u21;
+
+pub const TokenEnumType = std.EnumArray(T, []u8);
+
+pub const tokenToString: TokenEnumType = comptime {
+ var TEndOfFile = "end of file".*;
+ var TSyntaxError = "syntax error".*;
+ var THashbang = "hashbang comment".*;
+
+ // Literals
+ var TNoSubstitutionTemplateLiteral = "template literal".*;
+ var TNumericLiteral = "number".*;
+ var TStringLiteral = "string".*;
+ var TBigIntegerLiteral = "bigint".*;
+
+ // Pseudo-literals
+ var TTemplateHead = "template literal".*;
+ var TTemplateMiddle = "template literal".*;
+ var TTemplateTail = "template literal".*;
+
+ // Punctuation
+ var TAmpersand = "\"&\"".*;
+ var TAmpersandAmpersand = "\"&&\"".*;
+ var TAsterisk = "\"*\"".*;
+ var TAsteriskAsterisk = "\"**\"".*;
+ var TAt = "\"@\"".*;
+ var TBar = "\"|\"".*;
+ var TBarBar = "\"||\"".*;
+ var TCaret = "\"^\"".*;
+ var TCloseBrace = "\"}\"".*;
+ var TCloseBracket = "\"]\"".*;
+ var TCloseParen = "\")\"".*;
+ var TColon = "\" =\"".*;
+ var TComma = "\",\"".*;
+ var TDot = "\".\"".*;
+ var TDotDotDot = "\"...\"".*;
+ var TEqualsEquals = "\"==\"".*;
+ var TEqualsEqualsEquals = "\"===\"".*;
+ var TEqualsGreaterThan = "\"=>\"".*;
+ var TExclamation = "\"!\"".*;
+ var TExclamationEquals = "\"!=\"".*;
+ var TExclamationEqualsEquals = "\"!==\"".*;
+ var TGreaterThan = "\">\"".*;
+ var TGreaterThanEquals = "\">=\"".*;
+ var TGreaterThanGreaterThan = "\">>\"".*;
+ var TGreaterThanGreaterThanGreaterThan = "\">>>\"".*;
+ var TLessThan = "\"<\"".*;
+ var TLessThanEquals = "\"<=\"".*;
+ var TLessThanLessThan = "\"<<\"".*;
+ var TMinus = "\"-\"".*;
+ var TMinusMinus = "\"--\"".*;
+ var TOpenBrace = "\"{\"".*;
+ var TOpenBracket = "\"[\"".*;
+ var TOpenParen = "\"(\"".*;
+ var TPercent = "\"%\"".*;
+ var TPlus = "\"+\"".*;
+ var TPlusPlus = "\"++\"".*;
+ var TQuestion = "\"?\"".*;
+ var TQuestionDot = "\"?.\"".*;
+ var TQuestionQuestion = "\"??\"".*;
+ var TSemicolon = "\";\"".*;
+ var TSlash = "\"/\"".*;
+ var TTilde = "\"~\"".*;
+
+ // Assignments
+ var TAmpersandAmpersandEquals = "\"&&=\"".*;
+ var TAmpersandEquals = "\"&=\"".*;
+ var TAsteriskAsteriskEquals = "\"**=\"".*;
+ var TAsteriskEquals = "\"*=\"".*;
+ var TBarBarEquals = "\"||=\"".*;
+ var TBarEquals = "\"|=\"".*;
+ var TCaretEquals = "\"^=\"".*;
+ var TEquals = "\"=\"".*;
+ var TGreaterThanGreaterThanEquals = "\">>=\"".*;
+ var TGreaterThanGreaterThanGreaterThanEquals = "\">>>=\"".*;
+ var TLessThanLessThanEquals = "\"<<=\"".*;
+ var TMinusEquals = "\"-=\"".*;
+ var TPercentEquals = "\"%=\"".*;
+ var TPlusEquals = "\"+=\"".*;
+ var TQuestionQuestionEquals = "\"??=\"".*;
+ var TSlashEquals = "\"/=\"".*;
+
+ // Class-private fields and methods
+ var TPrivateIdentifier = "private identifier".*;
+
+ // Identifiers
+ var TIdentifier = "identifier".*;
+ var TEscapedKeyword = "escaped keyword".*;
+
+ // Reserved words
+ var TBreak = "\"break\"".*;
+ var TCase = "\"case\"".*;
+ var TCatch = "\"catch\"".*;
+ var TClass = "\"class\"".*;
+ var TConst = "\"const\"".*;
+ var TContinue = "\"continue\"".*;
+ var TDebugger = "\"debugger\"".*;
+ var TDefault = "\"default\"".*;
+ var TDelete = "\"delete\"".*;
+ var TDo = "\"do\"".*;
+ var TElse = "\"else\"".*;
+ var TEnum = "\"enum\"".*;
+ var TExport = "\"export\"".*;
+ var TExtends = "\"extends\"".*;
+ var TFalse = "\"false\"".*;
+ var TFinally = "\"finally\"".*;
+ var TFor = "\"for\"".*;
+ var TFunction = "\"function\"".*;
+ var TIf = "\"if\"".*;
+ var TImport = "\"import\"".*;
+ var TIn = "\"in\"".*;
+ var TInstanceof = "\"instanceof\"".*;
+ var TNew = "\"new\"".*;
+ var TNull = "\"null\"".*;
+ var TReturn = "\"return\"".*;
+ var TSuper = "\"super\"".*;
+ var TSwitch = "\"switch\"".*;
+ var TThis = "\"this\"".*;
+ var TThrow = "\"throw\"".*;
+ var TTrue = "\"true\"".*;
+ var TTry = "\"try\"".*;
+ var TTypeof = "\"typeof\"".*;
+ var TVar = "\"var\"".*;
+ var TVoid = "\"void\"".*;
+ var TWhile = "\"while\"".*;
+ var TWith = "\"with\"".*;
+
+ var tokenEnums = TokenEnumType.initUndefined();
+
+ var eof = "end of file";
+
+ tokenEnums.set(T.t_end_of_file, &TEndOfFile);
+ tokenEnums.set(T.t_syntax_error, &TSyntaxError);
+ tokenEnums.set(T.t_hashbang, &THashbang);
+
+ // Literals
+ tokenEnums.set(T.t_no_substitution_template_literal, &TNoSubstitutionTemplateLiteral);
+ tokenEnums.set(T.t_numeric_literal, &TNumericLiteral);
+ tokenEnums.set(T.t_string_literal, &TStringLiteral);
+ tokenEnums.set(T.t_big_integer_literal, &TBigIntegerLiteral);
+
+ // Pseudo-literals
+ tokenEnums.set(T.t_template_head, &TTemplateHead);
+ tokenEnums.set(T.t_template_middle, &TTemplateMiddle);
+ tokenEnums.set(T.t_template_tail, &TTemplateTail);
+
+ // Punctuation
+ tokenEnums.set(T.t_ampersand, &TAmpersand);
+ tokenEnums.set(T.t_ampersand_ampersand, &TAmpersandAmpersand);
+ tokenEnums.set(T.t_asterisk, &TAsterisk);
+ tokenEnums.set(T.t_asterisk_asterisk, &TAsteriskAsterisk);
+ tokenEnums.set(T.t_at, &TAt);
+ tokenEnums.set(T.t_bar, &TBar);
+ tokenEnums.set(T.t_bar_bar, &TBarBar);
+ tokenEnums.set(T.t_caret, &TCaret);
+ tokenEnums.set(T.t_close_brace, &TCloseBrace);
+ tokenEnums.set(T.t_close_bracket, &TCloseBracket);
+ tokenEnums.set(T.t_close_paren, &TCloseParen);
+ tokenEnums.set(T.t_colon, &TColon);
+ tokenEnums.set(T.t_comma, &TComma);
+ tokenEnums.set(T.t_dot, &TDot);
+ tokenEnums.set(T.t_dot_dot_dot, &TDotDotDot);
+ tokenEnums.set(T.t_equals_equals, &TEqualsEquals);
+ tokenEnums.set(T.t_equals_equals_equals, &TEqualsEqualsEquals);
+ tokenEnums.set(T.t_equals_greater_than, &TEqualsGreaterThan);
+ tokenEnums.set(T.t_exclamation, &TExclamation);
+ tokenEnums.set(T.t_exclamation_equals, &TExclamationEquals);
+ tokenEnums.set(T.t_exclamation_equals_equals, &TExclamationEqualsEquals);
+ tokenEnums.set(T.t_greater_than, &TGreaterThan);
+ tokenEnums.set(T.t_greater_than_equals, &TGreaterThanEquals);
+ tokenEnums.set(T.t_greater_than_greater_than, &TGreaterThanGreaterThan);
+ tokenEnums.set(T.t_greater_than_greater_than_greater_than, &TGreaterThanGreaterThanGreaterThan);
+ tokenEnums.set(T.t_less_than, &TLessThan);
+ tokenEnums.set(T.t_less_than_equals, &TLessThanEquals);
+ tokenEnums.set(T.t_less_than_less_than, &TLessThanLessThan);
+ tokenEnums.set(T.t_minus, &TMinus);
+ tokenEnums.set(T.t_minus_minus, &TMinusMinus);
+ tokenEnums.set(T.t_open_brace, &TOpenBrace);
+ tokenEnums.set(T.t_open_bracket, &TOpenBracket);
+ tokenEnums.set(T.t_open_paren, &TOpenParen);
+ tokenEnums.set(T.t_percent, &TPercent);
+ tokenEnums.set(T.t_plus, &TPlus);
+ tokenEnums.set(T.t_plus_plus, &TPlusPlus);
+ tokenEnums.set(T.t_question, &TQuestion);
+ tokenEnums.set(T.t_question_dot, &TQuestionDot);
+ tokenEnums.set(T.t_question_question, &TQuestionQuestion);
+ tokenEnums.set(T.t_semicolon, &TSemicolon);
+ tokenEnums.set(T.t_slash, &TSlash);
+ tokenEnums.set(T.t_tilde, &TTilde);
+
+ // Assignments
+ tokenEnums.set(T.t_ampersand_ampersand_equals, &TAmpersandAmpersandEquals);
+ tokenEnums.set(T.t_ampersand_equals, &TAmpersandEquals);
+ tokenEnums.set(T.t_asterisk_asterisk_equals, &TAsteriskAsteriskEquals);
+ tokenEnums.set(T.t_asterisk_equals, &TAsteriskEquals);
+ tokenEnums.set(T.t_bar_bar_equals, &TBarBarEquals);
+ tokenEnums.set(T.t_bar_equals, &TBarEquals);
+ tokenEnums.set(T.t_caret_equals, &TCaretEquals);
+ tokenEnums.set(T.t_equals, &TEquals);
+ tokenEnums.set(T.t_greater_than_greater_than_equals, &TGreaterThanGreaterThanEquals);
+ tokenEnums.set(T.t_greater_than_greater_than_greater_than_equals, &TGreaterThanGreaterThanGreaterThanEquals);
+ tokenEnums.set(T.t_less_than_less_than_equals, &TLessThanLessThanEquals);
+ tokenEnums.set(T.t_minus_equals, &TMinusEquals);
+ tokenEnums.set(T.t_percent_equals, &TPercentEquals);
+ tokenEnums.set(T.t_plus_equals, &TPlusEquals);
+ tokenEnums.set(T.t_question_question_equals, &TQuestionQuestionEquals);
+ tokenEnums.set(T.t_slash_equals, &TSlashEquals);
+
+ // Class-private fields and methods
+ tokenEnums.set(T.t_private_identifier, &TPrivateIdentifier);
+
+ // Identifiers
+ tokenEnums.set(T.t_identifier, &TIdentifier);
+ tokenEnums.set(T.t_escaped_keyword, &TEscapedKeyword);
+
+ // Reserved words
+ tokenEnums.set(T.t_break, &TBreak);
+ tokenEnums.set(T.t_case, &TCase);
+ tokenEnums.set(T.t_catch, &TCatch);
+ tokenEnums.set(T.t_class, &TClass);
+ tokenEnums.set(T.t_const, &TConst);
+ tokenEnums.set(T.t_continue, &TContinue);
+ tokenEnums.set(T.t_debugger, &TDebugger);
+ tokenEnums.set(T.t_default, &TDefault);
+ tokenEnums.set(T.t_delete, &TDelete);
+ tokenEnums.set(T.t_do, &TDo);
+ tokenEnums.set(T.t_else, &TElse);
+ tokenEnums.set(T.t_enum, &TEnum);
+ tokenEnums.set(T.t_export, &TExport);
+ tokenEnums.set(T.t_extends, &TExtends);
+ tokenEnums.set(T.t_false, &TFalse);
+ tokenEnums.set(T.t_finally, &TFinally);
+ tokenEnums.set(T.t_for, &TFor);
+ tokenEnums.set(T.t_function, &TFunction);
+ tokenEnums.set(T.t_if, &TIf);
+ tokenEnums.set(T.t_import, &TImport);
+ tokenEnums.set(T.t_in, &TIn);
+ tokenEnums.set(T.t_instanceof, &TInstanceof);
+ tokenEnums.set(T.t_new, &TNew);
+ tokenEnums.set(T.t_null, &TNull);
+ tokenEnums.set(T.t_return, &TReturn);
+ tokenEnums.set(T.t_super, &TSuper);
+ tokenEnums.set(T.t_switch, &TSwitch);
+ tokenEnums.set(T.t_this, &TThis);
+ tokenEnums.set(T.t_throw, &TThrow);
+ tokenEnums.set(T.t_true, &TTrue);
+ tokenEnums.set(T.t_try, &TTry);
+ tokenEnums.set(T.t_typeof, &TTypeof);
+ tokenEnums.set(T.t_var, &TVar);
+ tokenEnums.set(T.t_void, &TVoid);
+ tokenEnums.set(T.t_while, &TWhile);
+ tokenEnums.set(T.t_with, &TWith);
+
+ return tokenEnums;
+};
+
+pub const JSXEntityMap = std.StringHashMap(CodePoint);
+
+pub var jsxEntity: JSXEntityMap = undefined;
+
+pub fn initJSXEntityMap() !void {
+ jsxEntity = JSXEntityMap.init(alloc.dynamic);
+ jsxEntity.ensureCapacity(255) catch unreachable;
+
+ jsxEntity.putAssumeCapacity("quot", @as(CodePoint, 0x0022));
+ jsxEntity.putAssumeCapacity("amp", @as(CodePoint, 0x0026));
+ jsxEntity.putAssumeCapacity("apos", @as(CodePoint, 0x0027));
+ jsxEntity.putAssumeCapacity("lt", @as(CodePoint, 0x003C));
+ jsxEntity.putAssumeCapacity("gt", @as(CodePoint, 0x003E));
+ jsxEntity.putAssumeCapacity("nbsp", @as(CodePoint, 0x00A0));
+ jsxEntity.putAssumeCapacity("iexcl", @as(CodePoint, 0x00A1));
+ jsxEntity.putAssumeCapacity("cent", @as(CodePoint, 0x00A2));
+ jsxEntity.putAssumeCapacity("pound", @as(CodePoint, 0x00A3));
+ jsxEntity.putAssumeCapacity("curren", @as(CodePoint, 0x00A4));
+ jsxEntity.putAssumeCapacity("yen", @as(CodePoint, 0x00A5));
+ jsxEntity.putAssumeCapacity("brvbar", @as(CodePoint, 0x00A6));
+ jsxEntity.putAssumeCapacity("sect", @as(CodePoint, 0x00A7));
+ jsxEntity.putAssumeCapacity("uml", @as(CodePoint, 0x00A8));
+ jsxEntity.putAssumeCapacity("copy", @as(CodePoint, 0x00A9));
+ jsxEntity.putAssumeCapacity("ordf", @as(CodePoint, 0x00AA));
+ jsxEntity.putAssumeCapacity("laquo", @as(CodePoint, 0x00AB));
+ jsxEntity.putAssumeCapacity("not", @as(CodePoint, 0x00AC));
+ jsxEntity.putAssumeCapacity("shy", @as(CodePoint, 0x00AD));
+ jsxEntity.putAssumeCapacity("reg", @as(CodePoint, 0x00AE));
+ jsxEntity.putAssumeCapacity("macr", @as(CodePoint, 0x00AF));
+ jsxEntity.putAssumeCapacity("deg", @as(CodePoint, 0x00B0));
+ jsxEntity.putAssumeCapacity("plusmn", @as(CodePoint, 0x00B1));
+ jsxEntity.putAssumeCapacity("sup2", @as(CodePoint, 0x00B2));
+ jsxEntity.putAssumeCapacity("sup3", @as(CodePoint, 0x00B3));
+ jsxEntity.putAssumeCapacity("acute", @as(CodePoint, 0x00B4));
+ jsxEntity.putAssumeCapacity("micro", @as(CodePoint, 0x00B5));
+ jsxEntity.putAssumeCapacity("para", @as(CodePoint, 0x00B6));
+ jsxEntity.putAssumeCapacity("middot", @as(CodePoint, 0x00B7));
+ jsxEntity.putAssumeCapacity("cedil", @as(CodePoint, 0x00B8));
+ jsxEntity.putAssumeCapacity("sup1", @as(CodePoint, 0x00B9));
+ jsxEntity.putAssumeCapacity("ordm", @as(CodePoint, 0x00BA));
+ jsxEntity.putAssumeCapacity("raquo", @as(CodePoint, 0x00BB));
+ jsxEntity.putAssumeCapacity("frac14", @as(CodePoint, 0x00BC));
+ jsxEntity.putAssumeCapacity("frac12", @as(CodePoint, 0x00BD));
+ jsxEntity.putAssumeCapacity("frac34", @as(CodePoint, 0x00BE));
+ jsxEntity.putAssumeCapacity("iquest", @as(CodePoint, 0x00BF));
+ jsxEntity.putAssumeCapacity("Agrave", @as(CodePoint, 0x00C0));
+ jsxEntity.putAssumeCapacity("Aacute", @as(CodePoint, 0x00C1));
+ jsxEntity.putAssumeCapacity("Acirc", @as(CodePoint, 0x00C2));
+ jsxEntity.putAssumeCapacity("Atilde", @as(CodePoint, 0x00C3));
+ jsxEntity.putAssumeCapacity("Auml", @as(CodePoint, 0x00C4));
+ jsxEntity.putAssumeCapacity("Aring", @as(CodePoint, 0x00C5));
+ jsxEntity.putAssumeCapacity("AElig", @as(CodePoint, 0x00C6));
+ jsxEntity.putAssumeCapacity("Ccedil", @as(CodePoint, 0x00C7));
+ jsxEntity.putAssumeCapacity("Egrave", @as(CodePoint, 0x00C8));
+ jsxEntity.putAssumeCapacity("Eacute", @as(CodePoint, 0x00C9));
+ jsxEntity.putAssumeCapacity("Ecirc", @as(CodePoint, 0x00CA));
+ jsxEntity.putAssumeCapacity("Euml", @as(CodePoint, 0x00CB));
+ jsxEntity.putAssumeCapacity("Igrave", @as(CodePoint, 0x00CC));
+ jsxEntity.putAssumeCapacity("Iacute", @as(CodePoint, 0x00CD));
+ jsxEntity.putAssumeCapacity("Icirc", @as(CodePoint, 0x00CE));
+ jsxEntity.putAssumeCapacity("Iuml", @as(CodePoint, 0x00CF));
+ jsxEntity.putAssumeCapacity("ETH", @as(CodePoint, 0x00D0));
+ jsxEntity.putAssumeCapacity("Ntilde", @as(CodePoint, 0x00D1));
+ jsxEntity.putAssumeCapacity("Ograve", @as(CodePoint, 0x00D2));
+ jsxEntity.putAssumeCapacity("Oacute", @as(CodePoint, 0x00D3));
+ jsxEntity.putAssumeCapacity("Ocirc", @as(CodePoint, 0x00D4));
+ jsxEntity.putAssumeCapacity("Otilde", @as(CodePoint, 0x00D5));
+ jsxEntity.putAssumeCapacity("Ouml", @as(CodePoint, 0x00D6));
+ jsxEntity.putAssumeCapacity("times", @as(CodePoint, 0x00D7));
+ jsxEntity.putAssumeCapacity("Oslash", @as(CodePoint, 0x00D8));
+ jsxEntity.putAssumeCapacity("Ugrave", @as(CodePoint, 0x00D9));
+ jsxEntity.putAssumeCapacity("Uacute", @as(CodePoint, 0x00DA));
+ jsxEntity.putAssumeCapacity("Ucirc", @as(CodePoint, 0x00DB));
+ jsxEntity.putAssumeCapacity("Uuml", @as(CodePoint, 0x00DC));
+ jsxEntity.putAssumeCapacity("Yacute", @as(CodePoint, 0x00DD));
+ jsxEntity.putAssumeCapacity("THORN", @as(CodePoint, 0x00DE));
+ jsxEntity.putAssumeCapacity("szlig", @as(CodePoint, 0x00DF));
+ jsxEntity.putAssumeCapacity("agrave", @as(CodePoint, 0x00E0));
+ jsxEntity.putAssumeCapacity("aacute", @as(CodePoint, 0x00E1));
+ jsxEntity.putAssumeCapacity("acirc", @as(CodePoint, 0x00E2));
+ jsxEntity.putAssumeCapacity("atilde", @as(CodePoint, 0x00E3));
+ jsxEntity.putAssumeCapacity("auml", @as(CodePoint, 0x00E4));
+ jsxEntity.putAssumeCapacity("aring", @as(CodePoint, 0x00E5));
+ jsxEntity.putAssumeCapacity("aelig", @as(CodePoint, 0x00E6));
+ jsxEntity.putAssumeCapacity("ccedil", @as(CodePoint, 0x00E7));
+ jsxEntity.putAssumeCapacity("egrave", @as(CodePoint, 0x00E8));
+ jsxEntity.putAssumeCapacity("eacute", @as(CodePoint, 0x00E9));
+ jsxEntity.putAssumeCapacity("ecirc", @as(CodePoint, 0x00EA));
+ jsxEntity.putAssumeCapacity("euml", @as(CodePoint, 0x00EB));
+ jsxEntity.putAssumeCapacity("igrave", @as(CodePoint, 0x00EC));
+ jsxEntity.putAssumeCapacity("iacute", @as(CodePoint, 0x00ED));
+ jsxEntity.putAssumeCapacity("icirc", @as(CodePoint, 0x00EE));
+ jsxEntity.putAssumeCapacity("iuml", @as(CodePoint, 0x00EF));
+ jsxEntity.putAssumeCapacity("eth", @as(CodePoint, 0x00F0));
+ jsxEntity.putAssumeCapacity("ntilde", @as(CodePoint, 0x00F1));
+ jsxEntity.putAssumeCapacity("ograve", @as(CodePoint, 0x00F2));
+ jsxEntity.putAssumeCapacity("oacute", @as(CodePoint, 0x00F3));
+ jsxEntity.putAssumeCapacity("ocirc", @as(CodePoint, 0x00F4));
+ jsxEntity.putAssumeCapacity("otilde", @as(CodePoint, 0x00F5));
+ jsxEntity.putAssumeCapacity("ouml", @as(CodePoint, 0x00F6));
+ jsxEntity.putAssumeCapacity("divide", @as(CodePoint, 0x00F7));
+ jsxEntity.putAssumeCapacity("oslash", @as(CodePoint, 0x00F8));
+ jsxEntity.putAssumeCapacity("ugrave", @as(CodePoint, 0x00F9));
+ jsxEntity.putAssumeCapacity("uacute", @as(CodePoint, 0x00FA));
+ jsxEntity.putAssumeCapacity("ucirc", @as(CodePoint, 0x00FB));
+ jsxEntity.putAssumeCapacity("uuml", @as(CodePoint, 0x00FC));
+ jsxEntity.putAssumeCapacity("yacute", @as(CodePoint, 0x00FD));
+ jsxEntity.putAssumeCapacity("thorn", @as(CodePoint, 0x00FE));
+ jsxEntity.putAssumeCapacity("yuml", @as(CodePoint, 0x00FF));
+ jsxEntity.putAssumeCapacity("OElig", @as(CodePoint, 0x0152));
+ jsxEntity.putAssumeCapacity("oelig", @as(CodePoint, 0x0153));
+ jsxEntity.putAssumeCapacity("Scaron", @as(CodePoint, 0x0160));
+ jsxEntity.putAssumeCapacity("scaron", @as(CodePoint, 0x0161));
+ jsxEntity.putAssumeCapacity("Yuml", @as(CodePoint, 0x0178));
+ jsxEntity.putAssumeCapacity("fnof", @as(CodePoint, 0x0192));
+ jsxEntity.putAssumeCapacity("circ", @as(CodePoint, 0x02C6));
+ jsxEntity.putAssumeCapacity("tilde", @as(CodePoint, 0x02DC));
+ jsxEntity.putAssumeCapacity("Alpha", @as(CodePoint, 0x0391));
+ jsxEntity.putAssumeCapacity("Beta", @as(CodePoint, 0x0392));
+ jsxEntity.putAssumeCapacity("Gamma", @as(CodePoint, 0x0393));
+ jsxEntity.putAssumeCapacity("Delta", @as(CodePoint, 0x0394));
+ jsxEntity.putAssumeCapacity("Epsilon", @as(CodePoint, 0x0395));
+ jsxEntity.putAssumeCapacity("Zeta", @as(CodePoint, 0x0396));
+ jsxEntity.putAssumeCapacity("Eta", @as(CodePoint, 0x0397));
+ jsxEntity.putAssumeCapacity("Theta", @as(CodePoint, 0x0398));
+ jsxEntity.putAssumeCapacity("Iota", @as(CodePoint, 0x0399));
+ jsxEntity.putAssumeCapacity("Kappa", @as(CodePoint, 0x039A));
+ jsxEntity.putAssumeCapacity("Lambda", @as(CodePoint, 0x039B));
+ jsxEntity.putAssumeCapacity("Mu", @as(CodePoint, 0x039C));
+ jsxEntity.putAssumeCapacity("Nu", @as(CodePoint, 0x039D));
+ jsxEntity.putAssumeCapacity("Xi", @as(CodePoint, 0x039E));
+ jsxEntity.putAssumeCapacity("Omicron", @as(CodePoint, 0x039F));
+ jsxEntity.putAssumeCapacity("Pi", @as(CodePoint, 0x03A0));
+ jsxEntity.putAssumeCapacity("Rho", @as(CodePoint, 0x03A1));
+ jsxEntity.putAssumeCapacity("Sigma", @as(CodePoint, 0x03A3));
+ jsxEntity.putAssumeCapacity("Tau", @as(CodePoint, 0x03A4));
+ jsxEntity.putAssumeCapacity("Upsilon", @as(CodePoint, 0x03A5));
+ jsxEntity.putAssumeCapacity("Phi", @as(CodePoint, 0x03A6));
+ jsxEntity.putAssumeCapacity("Chi", @as(CodePoint, 0x03A7));
+ jsxEntity.putAssumeCapacity("Psi", @as(CodePoint, 0x03A8));
+ jsxEntity.putAssumeCapacity("Omega", @as(CodePoint, 0x03A9));
+ jsxEntity.putAssumeCapacity("alpha", @as(CodePoint, 0x03B1));
+ jsxEntity.putAssumeCapacity("beta", @as(CodePoint, 0x03B2));
+ jsxEntity.putAssumeCapacity("gamma", @as(CodePoint, 0x03B3));
+ jsxEntity.putAssumeCapacity("delta", @as(CodePoint, 0x03B4));
+ jsxEntity.putAssumeCapacity("epsilon", @as(CodePoint, 0x03B5));
+ jsxEntity.putAssumeCapacity("zeta", @as(CodePoint, 0x03B6));
+ jsxEntity.putAssumeCapacity("eta", @as(CodePoint, 0x03B7));
+ jsxEntity.putAssumeCapacity("theta", @as(CodePoint, 0x03B8));
+ jsxEntity.putAssumeCapacity("iota", @as(CodePoint, 0x03B9));
+ jsxEntity.putAssumeCapacity("kappa", @as(CodePoint, 0x03BA));
+ jsxEntity.putAssumeCapacity("lambda", @as(CodePoint, 0x03BB));
+ jsxEntity.putAssumeCapacity("mu", @as(CodePoint, 0x03BC));
+ jsxEntity.putAssumeCapacity("nu", @as(CodePoint, 0x03BD));
+ jsxEntity.putAssumeCapacity("xi", @as(CodePoint, 0x03BE));
+ jsxEntity.putAssumeCapacity("omicron", @as(CodePoint, 0x03BF));
+ jsxEntity.putAssumeCapacity("pi", @as(CodePoint, 0x03C0));
+ jsxEntity.putAssumeCapacity("rho", @as(CodePoint, 0x03C1));
+ jsxEntity.putAssumeCapacity("sigmaf", @as(CodePoint, 0x03C2));
+ jsxEntity.putAssumeCapacity("sigma", @as(CodePoint, 0x03C3));
+ jsxEntity.putAssumeCapacity("tau", @as(CodePoint, 0x03C4));
+ jsxEntity.putAssumeCapacity("upsilon", @as(CodePoint, 0x03C5));
+ jsxEntity.putAssumeCapacity("phi", @as(CodePoint, 0x03C6));
+ jsxEntity.putAssumeCapacity("chi", @as(CodePoint, 0x03C7));
+ jsxEntity.putAssumeCapacity("psi", @as(CodePoint, 0x03C8));
+ jsxEntity.putAssumeCapacity("omega", @as(CodePoint, 0x03C9));
+ jsxEntity.putAssumeCapacity("thetasym", @as(CodePoint, 0x03D1));
+ jsxEntity.putAssumeCapacity("upsih", @as(CodePoint, 0x03D2));
+ jsxEntity.putAssumeCapacity("piv", @as(CodePoint, 0x03D6));
+ jsxEntity.putAssumeCapacity("ensp", @as(CodePoint, 0x2002));
+ jsxEntity.putAssumeCapacity("emsp", @as(CodePoint, 0x2003));
+ jsxEntity.putAssumeCapacity("thinsp", @as(CodePoint, 0x2009));
+ jsxEntity.putAssumeCapacity("zwnj", @as(CodePoint, 0x200C));
+ jsxEntity.putAssumeCapacity("zwj", @as(CodePoint, 0x200D));
+ jsxEntity.putAssumeCapacity("lrm", @as(CodePoint, 0x200E));
+ jsxEntity.putAssumeCapacity("rlm", @as(CodePoint, 0x200F));
+ jsxEntity.putAssumeCapacity("ndash", @as(CodePoint, 0x2013));
+ jsxEntity.putAssumeCapacity("mdash", @as(CodePoint, 0x2014));
+ jsxEntity.putAssumeCapacity("lsquo", @as(CodePoint, 0x2018));
+ jsxEntity.putAssumeCapacity("rsquo", @as(CodePoint, 0x2019));
+ jsxEntity.putAssumeCapacity("sbquo", @as(CodePoint, 0x201A));
+ jsxEntity.putAssumeCapacity("ldquo", @as(CodePoint, 0x201C));
+ jsxEntity.putAssumeCapacity("rdquo", @as(CodePoint, 0x201D));
+ jsxEntity.putAssumeCapacity("bdquo", @as(CodePoint, 0x201E));
+ jsxEntity.putAssumeCapacity("dagger", @as(CodePoint, 0x2020));
+ jsxEntity.putAssumeCapacity("Dagger", @as(CodePoint, 0x2021));
+ jsxEntity.putAssumeCapacity("bull", @as(CodePoint, 0x2022));
+ jsxEntity.putAssumeCapacity("hellip", @as(CodePoint, 0x2026));
+ jsxEntity.putAssumeCapacity("permil", @as(CodePoint, 0x2030));
+ jsxEntity.putAssumeCapacity("prime", @as(CodePoint, 0x2032));
+ jsxEntity.putAssumeCapacity("Prime", @as(CodePoint, 0x2033));
+ jsxEntity.putAssumeCapacity("lsaquo", @as(CodePoint, 0x2039));
+ jsxEntity.putAssumeCapacity("rsaquo", @as(CodePoint, 0x203A));
+ jsxEntity.putAssumeCapacity("oline", @as(CodePoint, 0x203E));
+ jsxEntity.putAssumeCapacity("frasl", @as(CodePoint, 0x2044));
+ jsxEntity.putAssumeCapacity("euro", @as(CodePoint, 0x20AC));
+ jsxEntity.putAssumeCapacity("image", @as(CodePoint, 0x2111));
+ jsxEntity.putAssumeCapacity("weierp", @as(CodePoint, 0x2118));
+ jsxEntity.putAssumeCapacity("real", @as(CodePoint, 0x211C));
+ jsxEntity.putAssumeCapacity("trade", @as(CodePoint, 0x2122));
+ jsxEntity.putAssumeCapacity("alefsym", @as(CodePoint, 0x2135));
+ jsxEntity.putAssumeCapacity("larr", @as(CodePoint, 0x2190));
+ jsxEntity.putAssumeCapacity("uarr", @as(CodePoint, 0x2191));
+ jsxEntity.putAssumeCapacity("rarr", @as(CodePoint, 0x2192));
+ jsxEntity.putAssumeCapacity("darr", @as(CodePoint, 0x2193));
+ jsxEntity.putAssumeCapacity("harr", @as(CodePoint, 0x2194));
+ jsxEntity.putAssumeCapacity("crarr", @as(CodePoint, 0x21B5));
+ jsxEntity.putAssumeCapacity("lArr", @as(CodePoint, 0x21D0));
+ jsxEntity.putAssumeCapacity("uArr", @as(CodePoint, 0x21D1));
+ jsxEntity.putAssumeCapacity("rArr", @as(CodePoint, 0x21D2));
+ jsxEntity.putAssumeCapacity("dArr", @as(CodePoint, 0x21D3));
+ jsxEntity.putAssumeCapacity("hArr", @as(CodePoint, 0x21D4));
+ jsxEntity.putAssumeCapacity("forall", @as(CodePoint, 0x2200));
+ jsxEntity.putAssumeCapacity("part", @as(CodePoint, 0x2202));
+ jsxEntity.putAssumeCapacity("exist", @as(CodePoint, 0x2203));
+ jsxEntity.putAssumeCapacity("empty", @as(CodePoint, 0x2205));
+ jsxEntity.putAssumeCapacity("nabla", @as(CodePoint, 0x2207));
+ jsxEntity.putAssumeCapacity("isin", @as(CodePoint, 0x2208));
+ jsxEntity.putAssumeCapacity("notin", @as(CodePoint, 0x2209));
+ jsxEntity.putAssumeCapacity("ni", @as(CodePoint, 0x220B));
+ jsxEntity.putAssumeCapacity("prod", @as(CodePoint, 0x220F));
+ jsxEntity.putAssumeCapacity("sum", @as(CodePoint, 0x2211));
+ jsxEntity.putAssumeCapacity("minus", @as(CodePoint, 0x2212));
+ jsxEntity.putAssumeCapacity("lowast", @as(CodePoint, 0x2217));
+ jsxEntity.putAssumeCapacity("radic", @as(CodePoint, 0x221A));
+ jsxEntity.putAssumeCapacity("prop", @as(CodePoint, 0x221D));
+ jsxEntity.putAssumeCapacity("infin", @as(CodePoint, 0x221E));
+ jsxEntity.putAssumeCapacity("ang", @as(CodePoint, 0x2220));
+ jsxEntity.putAssumeCapacity("and", @as(CodePoint, 0x2227));
+ jsxEntity.putAssumeCapacity("or", @as(CodePoint, 0x2228));
+ jsxEntity.putAssumeCapacity("cap", @as(CodePoint, 0x2229));
+ jsxEntity.putAssumeCapacity("cup", @as(CodePoint, 0x222A));
+ jsxEntity.putAssumeCapacity("int", @as(CodePoint, 0x222B));
+ jsxEntity.putAssumeCapacity("there4", @as(CodePoint, 0x2234));
+ jsxEntity.putAssumeCapacity("sim", @as(CodePoint, 0x223C));
+ jsxEntity.putAssumeCapacity("cong", @as(CodePoint, 0x2245));
+ jsxEntity.putAssumeCapacity("asymp", @as(CodePoint, 0x2248));
+ jsxEntity.putAssumeCapacity("ne", @as(CodePoint, 0x2260));
+ jsxEntity.putAssumeCapacity("equiv", @as(CodePoint, 0x2261));
+ jsxEntity.putAssumeCapacity("le", @as(CodePoint, 0x2264));
+ jsxEntity.putAssumeCapacity("ge", @as(CodePoint, 0x2265));
+ jsxEntity.putAssumeCapacity("sub", @as(CodePoint, 0x2282));
+ jsxEntity.putAssumeCapacity("sup", @as(CodePoint, 0x2283));
+ jsxEntity.putAssumeCapacity("nsub", @as(CodePoint, 0x2284));
+ jsxEntity.putAssumeCapacity("sube", @as(CodePoint, 0x2286));
+ jsxEntity.putAssumeCapacity("supe", @as(CodePoint, 0x2287));
+ jsxEntity.putAssumeCapacity("oplus", @as(CodePoint, 0x2295));
+ jsxEntity.putAssumeCapacity("otimes", @as(CodePoint, 0x2297));
+ jsxEntity.putAssumeCapacity("perp", @as(CodePoint, 0x22A5));
+ jsxEntity.putAssumeCapacity("sdot", @as(CodePoint, 0x22C5));
+ jsxEntity.putAssumeCapacity("lceil", @as(CodePoint, 0x2308));
+ jsxEntity.putAssumeCapacity("rceil", @as(CodePoint, 0x2309));
+ jsxEntity.putAssumeCapacity("lfloor", @as(CodePoint, 0x230A));
+ jsxEntity.putAssumeCapacity("rfloor", @as(CodePoint, 0x230B));
+ jsxEntity.putAssumeCapacity("lang", @as(CodePoint, 0x2329));
+ jsxEntity.putAssumeCapacity("rang", @as(CodePoint, 0x232A));
+ jsxEntity.putAssumeCapacity("loz", @as(CodePoint, 0x25CA));
+ jsxEntity.putAssumeCapacity("spades", @as(CodePoint, 0x2660));
+ jsxEntity.putAssumeCapacity("clubs", @as(CodePoint, 0x2663));
+ jsxEntity.putAssumeCapacity("hearts", @as(CodePoint, 0x2665));
+ jsxEntity.putAssumeCapacity("diams", @as(CodePoint, 0x2666));
+}
+
+test "tokenToString" {
+ expectString(tokenToString.get(T.t_end_of_file), "end of file");
+}
+
+test "jsxEntity" {
+ try alloc.setup(std.heap.page_allocator);
+
+ initJSXEntityMap() catch |err| {
+ @panic(@errorName(err));
+ };
+
+ if (jsxEntity.get("sim")) |v| {
+ expect(v == 0x223C);
+ }
+}
diff --git a/src/js_parser.zig b/src/js_parser.zig
new file mode 100644
index 000000000..8c9ff1e1a
--- /dev/null
+++ b/src/js_parser.zig
@@ -0,0 +1,10 @@
+const std = @import("std");
+const logger = @import("logger.zig");
+const lexer = @import("lexer.zig");
+const ast = @import("js_ast.zig");
+
+pub fn Parse(
+ log: logger.Log,
+ source: logger.Source,
+
+) \ No newline at end of file
diff --git a/src/lexer/js_lexer.zig b/src/lexer/js_lexer.zig
deleted file mode 100644
index 587ea2caa..000000000
--- a/src/lexer/js_lexer.zig
+++ /dev/null
@@ -1,176 +0,0 @@
-const std = @import("std");
-
-pub const T = enum(u8) {
- t_end_of_file,
- t_syntax_error,
-
- // "#!/usr/bin/env node"
- t_hashbang,
-
- // literals
- t_no_substitution_template_literal, // contents are in lexer.string_literal ([]uint16)
- t_numeric_literal, // contents are in lexer.number (float64)
- t_string_literal, // contents are in lexer.string_literal ([]uint16)
- t_big_integer_literal, // contents are in lexer.identifier (string)
-
- // pseudo-literals
- t_template_head, // contents are in lexer.string_literal ([]uint16)
- t_template_middle, // contents are in lexer.string_literal ([]uint16)
- t_template_tail, // contents are in lexer.string_literal ([]uint16)
-
- // punctuation
- t_ampersand,
- t_ampersand_ampersand,
- t_asterisk,
- t_asterisk_asterisk,
- t_at,
- t_bar,
- t_bar_bar,
- t_caret,
- t_close_brace,
- t_close_bracket,
- t_close_paren,
- t_colon,
- t_comma,
- t_dot,
- t_dot_dot_dot,
- t_equals_equals,
- t_equals_equals_equals,
- t_equals_greater_than,
- t_exclamation,
- t_exclamation_equals,
- t_exclamation_equals_equals,
- t_greater_than,
- t_greater_than_equals,
- t_greater_than_greater_than,
- t_greater_than_greater_than_greater_than,
- t_less_than,
- t_less_than_equals,
- t_less_than_less_than,
- t_minus,
- t_minus_minus,
- t_open_brace,
- t_open_bracket,
- t_open_paren,
- t_percent,
- t_plus,
- t_plus_plus,
- t_question,
- t_question_dot,
- t_question_question,
- t_semicolon,
- t_slash,
- t_tilde,
-
- // assignments (keep in sync with is_assign() below)
- t_ampersand_ampersand_equals,
- t_ampersand_equals,
- t_asterisk_asterisk_equals,
- t_asterisk_equals,
- t_bar_bar_equals,
- t_bar_equals,
- t_caret_equals,
- t_equals,
- t_greater_than_greater_than_equals,
- t_greater_than_greater_than_greater_than_equals,
- t_less_than_less_than_equals,
- t_minus_equals,
- t_percent_equals,
- t_plus_equals,
- t_question_question_equals,
- t_slash_equals,
-
- // class-private fields and methods
- t_private_identifier,
-
- // identifiers
- t_identifier, // contents are in lexer.identifier (string)
- t_escaped_keyword, // a keyword that has been escaped as an identifer
-
- // reserved words
- t_break,
- t_case,
- t_catch,
- t_class,
- t_const,
- t_continue,
- t_debugger,
- t_default,
- t_delete,
- t_do,
- t_else,
- t_enum,
- t_export,
- t_extends,
- t_false,
- t_finally,
- t_for,
- t_function,
- t_if,
- t_import,
- t_in,
- t_instanceof,
- t_new,
- t_null,
- t_return,
- t_super,
- t_switch,
- t_this,
- t_throw,
- t_true,
- t_try,
- t_typeof,
- t_var,
- t_void,
- t_while,
- t_with,
-
- pub fn isAssign() bool {
- return self >= T.t_ampersand_ampersand_equals and self <= T.t_slash_equals;
- }
-
- pub fn isReservedWord() bool {
- return self >= T.t_break and self <= T.t_with;
- }
-};
-
-pub const Keywords = std.ComptimeStringMap(T, .{
- .{ "break", .t_break },
- .{ "case", .t_case },
- .{ "catch", .t_catch },
- .{ "class", .t_class },
- .{ "const", .t_const },
- .{ "continue", .t_continue },
- .{ "debugger", .t_debugger },
- .{ "default", .t_default },
- .{ "delete", .t_delete },
- .{ "do", .t_do },
- .{ "else", .t_else },
- .{ "enum", .t_enum },
- .{ "export", .t_export },
- .{ "extends", .t_extends },
- .{ "false", .t_false },
- .{ "finally", .t_finally },
- .{ "for", .t_for },
- .{ "function", .t_function },
- .{ "if", .t_if },
- .{ "import", .t_import },
- .{ "in", .t_in },
- .{ "instanceof", .t_instanceof },
- .{ "new", .t_new },
- .{ "null", .t_null },
- .{ "return", .t_return },
- .{ "super", .t_super },
- .{ "switch", .t_switch },
- .{ "this", .t_this },
- .{ "throw", .t_throw },
- .{ "true", .t_true },
- .{ "try", .t_try },
- .{ "typeof", .t_typeof },
- .{ "var", .t_var },
- .{ "void", .t_void },
- .{ "while", .t_while },
- .{ "with", .t_with },
-});
-
-const Lexer = struct {};
diff --git a/src/logger.zig b/src/logger.zig
new file mode 100644
index 000000000..617eb4434
--- /dev/null
+++ b/src/logger.zig
@@ -0,0 +1,118 @@
+const std = @import("std");
+const strings = @import("strings.zig");
+
+const expect = std.testing.expect;
+const assert = std.debug.assert;
+const ArrayList = std.ArrayList;
+
+pub const Kind = enum {
+ err,
+ warn,
+ note,
+ debug,
+
+ pub fn string(self: Kind) []const u8 {
+ return switch (self) {
+ .err => "error",
+ .warn => "warn",
+ .note => "note",
+ .debug => "debug",
+ };
+ }
+};
+
+pub const Loc = i32;
+
+pub const Location = struct {
+ file: []u8,
+ namespace: []u8 = "file",
+ line: i32 = 1, // 1-based
+ column: i32 = 0, // 0-based, in bytes
+ length: u32 = 0, // in bytes
+ line_text: ?[]u8,
+ suggestion: ?[]u8,
+
+ pub fn init(file: []u8, namespace: []u8, line: i32, column: i32, length: u32, line_text: ?[]u8, suggestion: ?[]u8) Location {
+ return Location{
+ .file = file,
+ .namespace = namespace,
+ .line = line,
+ .column = column,
+ .length = length,
+ .line_text = line_text,
+ .suggestion = suggestion,
+ };
+ }
+
+ pub fn init_file(file: []u8, line: i32, column: i32, length: u32, line_text: ?[]u8, suggestion: ?[]u8) Location {
+ var namespace = "file".*;
+
+ return Location{
+ .file = file,
+ .namespace = &namespace,
+ .line = line,
+ .column = column,
+ .length = length,
+ .line_text = line_text,
+ .suggestion = suggestion,
+ };
+ }
+};
+
+pub const Data = struct { text: []u8, location: *Location };
+
+pub const Msg = struct {
+ kind: Kind = Kind.err,
+ data: Data,
+};
+
+pub const Range = struct { start: u32 = 0, len: i32 = 0 };
+
+pub const Log = struct {
+ debug: bool = false,
+ warnings: u8 = 0,
+ errors: u8 = 0,
+ msgs: ArrayList(Msg),
+
+ // TODO:
+ pub fn add_msg(self: *Log, msg: Msg) !void {
+ try self.msgs.append(msg);
+ }
+
+ // TODO:
+ pub fn add_err(self: *Log, msg: Msg) !void {
+ // try self.msgs.append(msg);
+ }
+
+ // TODO:
+ pub fn print(self: *Log, to: anytype) !void {
+ for (self.msgs.items) |msg| {
+ try std.fmt.format(to, "\n\n{s}: {s}\n{s}\n{s}:{}:{}", .{ msg.kind.string(), msg.data.text, msg.data.location.line_text, msg.data.location.file, msg.data.location.line, msg.data.location.column });
+ }
+ }
+};
+
+pub const Source = struct { index: u32 = 0, contents: []u8,
+
+// An identifier that is mixed in to automatically-generated symbol names to
+// improve readability. For example, if the identifier is "util" then the
+// symbol for an "export default" statement will be called "util_default".
+identifier_name: []u8 };
+
+test "print msg" {
+ var log = Log{ .msgs = ArrayList(Msg).init(std.testing.allocator) };
+ defer log.msgs.deinit();
+ var filename = "test.js".*;
+ var syntax = "for(i = 0;)".*;
+ var err = "invalid syntax".*;
+ var namespace = "file".*;
+
+ try log.add_msg(Msg{
+ .kind = .err,
+ .data = Data{ .location = &Location.init_file(&filename, 1, 3, 0, &syntax, ""), .text = &err },
+ });
+
+ const stdout = std.io.getStdOut().writer();
+
+ try log.print(stdout);
+}
diff --git a/src/logger/logger.zig b/src/logger/logger.zig
deleted file mode 100644
index 8409d767a..000000000
--- a/src/logger/logger.zig
+++ /dev/null
@@ -1,103 +0,0 @@
-const std = @import("std");
-
-const expect = std.testing.expect;
-
-const ArrayList = std.ArrayList;
-
-pub const Msg = struct {
- pub const Kind = enum {
- err,
- warn,
- note,
- debug,
-
- pub fn string(self: Kind) []const u8 {
- return switch (self) {
- .err => "error",
- .warn => "warn",
- .note => "note",
- .debug => "debug",
- };
- }
- };
-
- pub const Location = struct {
- file: []u8,
- namespace: []u8 = "file",
- line: i32 = 1, // 1-based
- column: i32 = 0, // 0-based, in bytes
- length: u32 = 0, // in bytes
- line_text: ?[]u8,
- suggestion: ?[]u8,
-
- pub fn init(file: []u8, namespace: []u8, line: i32, column: i32, length: u32, line_text: ?[]u8, suggestion: ?[]u8) Location {
- return Location{
- .file = file,
- .namespace = namespace,
- .line = line,
- .column = column,
- .length = length,
- .line_text = line_text,
- .suggestion = suggestion,
- };
- }
-
- pub fn init_file(file: []u8, line: i32, column: i32, length: u32, line_text: ?[]u8, suggestion: ?[]u8) Location {
- var namespace = "file".*;
- return Location{
- .file = file,
- .namespace = &namespace,
- .line = line,
- .column = column,
- .length = length,
- .line_text = line_text,
- .suggestion = suggestion,
- };
- }
- };
-
- pub const Data = struct { text: []u8, location: *Msg.Location };
-
- kind: Kind,
- data: Data,
-};
-
-pub const Log = struct {
- debug: bool = false,
- warnings: u8 = 0,
- errors: u8 = 0,
- msgs: ArrayList(Msg),
-
- pub fn add_msg(self: *Log, msg: Msg) !void {
- try self.msgs.append(msg);
- }
-
- pub fn print(self: *Log) void {
- if (self.msgs.items.len > 0) {
- var msg: Msg = self.msgs.items[0];
- std.debug.print("\n\n{s}: {s}\n{s}\n{s}:{}:{}", .{ msg.kind.string(), msg.data.text, msg.data.location.line_text, msg.data.location.file, msg.data.location.line, msg.data.location.column });
- }
- }
-};
-
-pub const Source = struct { index: u32, contents: []u8,
-// An identifier that is mixed in to automatically-generated symbol names to
-// improve readability. For example, if the identifier is "util" then the
-// symbol for an "export default" statement will be called "util_default".
-identifier_name: []u8 };
-
-test "print msg" {
- var log = Log{ .msgs = ArrayList(Msg).init(std.testing.allocator) };
- defer log.msgs.deinit();
- var filename = "test.js".*;
- var syntax = "for(i = 0;)".*;
- var err = "invalid syntax".*;
- var namespace = "file".*;
-
- try log.add_msg(Msg{
- .kind = .err,
- .data = Msg.Data{ .location = &Msg.Location.init_file(&filename, 1, 3, 0, &syntax, ""), .text = &err },
- });
-
- log.print();
-}
diff --git a/src/main.zig b/src/main.zig
index 582427975..b8ee34f6e 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -1,6 +1,7 @@
const std = @import("std");
-const lex = @import("lexer/js_lexer.zig");
+const lex = @import("js_lexer.zig");
pub fn main() anyerror!void {
std.log.info("All your codebase are belong to us. {s}", .{lex.Keywords.get("hey")});
+
}
diff --git a/src/options.zig b/src/options.zig
new file mode 100644
index 000000000..3ffd24ab6
--- /dev/null
+++ b/src/options.zig
@@ -0,0 +1,31 @@
+const std = @import("std");
+const log = @import("logger.zig");
+
+pub const Loader = enum {
+ jsx,
+ js,
+ ts,
+ tsx,
+};
+
+pub const TransformOptions = struct {
+ footer: []u8 = "",
+ banner: []u8 = "",
+ define: std.StringHashMap([]u8),
+ loader: Loader = Loader.tsx,
+ resolve_dir: []u8 = "/",
+ react_fast_refresh: bool = false,
+ jsx_factory: []u8 = "React.createElement",
+ jsx_pragma: []u8 = "jsx",
+ inject: [][]u8,
+ public_url: []u8,
+ filesystem_cache: std.StringHashMap(fs.File),
+ entry_point: fs.File,
+};
+
+pub const OutputFile = struct {
+ path: []u8,
+ contents: []u8,
+};
+
+pub const TransformResult = struct { errors: []log.Msg, warnings: []log.Msg, output_files: []OutputFile };
diff --git a/src/strings.zig b/src/strings.zig
new file mode 100644
index 000000000..02ec4180b
--- /dev/null
+++ b/src/strings.zig
@@ -0,0 +1,8 @@
+const std = @import("std");
+pub fn indexOfChar(contents: []u8, char: u8) callconv(.Inline) ?usize {
+ return std.mem.indexOfScalar(u8, contents, char);
+}
+
+pub fn lastIndexOfChar(contents: []u8, char: u8) callconv(.Inline) ?usize {
+ return std.mem.lastIndexOfScalar(u8, contents, char);
+}
diff --git a/src/test/fixtures.zig b/src/test/fixtures.zig
new file mode 100644
index 000000000..8733a4e68
--- /dev/null
+++ b/src/test/fixtures.zig
@@ -0,0 +1,9 @@
+const std = @import("std");
+
+pub const fixtures = std.ComptimeStringMap([]u8, .{
+ .{ "package.json", @embedFile("./fixtures/package.json") },
+ .{ "tsconfig.json", @embedFile("./fixtures/tsconfig.json") },
+ .{ "simple-component.js", @embedFile("./fixtures/simple-component.js") },
+ .{ "simple-component.tsx", @embedFile("./fixtures/simple-component.tsx") },
+ .{ "simple-component.tsx", @embedFile("./fixtures/simple-component.tsx") },
+});
diff --git a/src/test/fixtures/noop.js b/src/test/fixtures/noop.js
new file mode 100644
index 000000000..b67880d83
--- /dev/null
+++ b/src/test/fixtures/noop.js
@@ -0,0 +1 @@
+function hi() {}
diff --git a/src/test/fixtures/package.json b/src/test/fixtures/package.json
new file mode 100644
index 000000000..8555c8500
--- /dev/null
+++ b/src/test/fixtures/package.json
@@ -0,0 +1,7 @@
+{
+ "name": "repo",
+ "dependencies": {
+ "react": "^16.8.4",
+ "react-dom": "^16.8.4"
+ }
+}
diff --git a/src/test/fixtures/simple-component.js b/src/test/fixtures/simple-component.js
new file mode 100644
index 000000000..b59f9f667
--- /dev/null
+++ b/src/test/fixtures/simple-component.js
@@ -0,0 +1,5 @@
+import * as React from "react";
+
+export function Welcome() {
+ return <div>Hi.</div>;
+}
diff --git a/src/test/fixtures/simple-component.tsx b/src/test/fixtures/simple-component.tsx
new file mode 100644
index 000000000..9f4fe729a
--- /dev/null
+++ b/src/test/fixtures/simple-component.tsx
@@ -0,0 +1,9 @@
+import * as React from "react";
+
+type WelcomeProps = {
+ greeting?: string;
+};
+
+export function Welcome(props: WelcomeProps) {
+ return <div>{props.greeting}</div>;
+}
diff --git a/src/test/fixtures/tsconfig.json b/src/test/fixtures/tsconfig.json
new file mode 100644
index 000000000..b594d9102
--- /dev/null
+++ b/src/test/fixtures/tsconfig.json
@@ -0,0 +1,9 @@
+{
+ "compilerOptions": {
+ "baseUrl": "/Users/jarredsumner/Code/esdev/src/test/fixtures",
+ "paths": {
+ "components": ["components/*"]
+ },
+ "jsx": "preserve"
+ }
+}