diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/js_ast.zig | 2 | ||||
-rw-r--r-- | src/js_lexer.zig | 2 | ||||
-rw-r--r-- | src/js_parser.zig | 11 | ||||
-rw-r--r-- | src/js_printer.zig | 196 | ||||
-rw-r--r-- | src/json_parser.zig | 17 | ||||
-rw-r--r-- | src/main.zig | 9 | ||||
-rw-r--r-- | src/main_wasm.zig | 12 | ||||
-rw-r--r-- | src/string_mutable.zig | 21 |
8 files changed, 247 insertions, 23 deletions
diff --git a/src/js_ast.zig b/src/js_ast.zig index 7ab5e39ad..5a5e6dec4 100644 --- a/src/js_ast.zig +++ b/src/js_ast.zig @@ -2372,7 +2372,7 @@ pub const Case = struct { loc: logger.Loc, value: ?ExprNodeIndex, body: StmtNode pub const Op = struct { // If you add a new token, remember to add it to "OpTable" too - pub const Code = packed enum(u6) { + pub const Code = enum { // Prefix un_pos, un_neg, diff --git a/src/js_lexer.zig b/src/js_lexer.zig index 3118f31d2..bef9893c8 100644 --- a/src/js_lexer.zig +++ b/src/js_lexer.zig @@ -595,7 +595,7 @@ pub fn NewLexerType(comptime jsonOptions: ?JSONOptions) type { }, else => { - lexer.token = T.t_plus; + lexer.token = T.t_minus; }, } }, diff --git a/src/js_parser.zig b/src/js_parser.zig index e32adc859..44cb5e036 100644 --- a/src/js_parser.zig +++ b/src/js_parser.zig @@ -405,8 +405,15 @@ pub const Parser = struct { // Pop the module scope to apply the "ContainsDirectEval" rules p.popScope(); - result = p.toAST(parts); - result.source_map_comment = p.lexer.source_mapping_url; + result.ast = js_ast.Ast{ + .parts = parts.toOwnedSlice(), + .symbols = p.symbols.toOwnedSlice(), + .module_scope = p.module_scope.*, + }; + result.ok = true; + + // result = p.toAST(parts); + // result.source_map_comment = p.lexer.source_mapping_url; } return result; diff --git a/src/js_printer.zig b/src/js_printer.zig index fb7d83f14..243e4bb11 100644 --- a/src/js_printer.zig +++ b/src/js_printer.zig @@ -33,6 +33,14 @@ const Scope = js_ast.Scope; const locModuleScope = logger.Loc.Empty; const Ast = js_ast.Ast; +const hex_chars = "0123456789ABCDEF"; +const first_ascii = 0x20; +const last_ascii = 0x7E; +const first_high_surrogate = 0xD800; +const last_high_surrogate = 0xDBFF; +const first_low_surrogate = 0xDC00; +const last_low_surrogate = 0xDFFF; + fn notimpl() void { std.debug.panic("Not implemented yet!", .{}); } @@ -100,6 +108,7 @@ pub fn NewPrinter(comptime ascii_only: bool) type { prev_reg_exp_end: i32 = -1, call_target: ?Expr.Data = null, int_to_bytes_buffer: [64]u8 = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + writer: MutableString.Writer, allocator: *std.mem.Allocator, const Printer = @This(); @@ -150,8 +159,21 @@ pub fn NewPrinter(comptime ascii_only: bool) type { // } // } - pub fn print(p: *Printer, str: string) void { - p.js.append(str) catch unreachable; + pub fn print(p: *Printer, str: anytype) void { + switch (@TypeOf(str)) { + string => { + p.js.append(str) catch unreachable; + }, + u8 => { + p.js.appendChar(str) catch unreachable; + }, + u16 => { + p.js.appendChar(@intCast(u8, str)) catch unreachable; + }, + else => { + p.js.append(@as(string, str)) catch unreachable; + }, + } } pub fn unsafePrint(p: *Printer, str: string) void { @@ -184,7 +206,9 @@ pub fn NewPrinter(comptime ascii_only: bool) type { pub fn printSemicolonIfNeeded(p: *Printer) void { notimpl(); } - pub fn printSpaceBeforeIdentifier(p: *Printer) void { + pub fn printSpaceBeforeIdentifier( + p: *Printer, + ) void { const n = p.js.len(); if (n > 0 and (js_lexer.isIdentifierContinue(p.js.list.items[n - 1]) or n == p.prev_reg_exp_end)) { p.print(" "); @@ -223,6 +247,125 @@ pub fn NewPrinter(comptime ascii_only: bool) type { pub fn printClass(p: *Printer, class: G.Class) void { notimpl(); } + + pub fn bestQuoteCharForString(p: *Printer, str: JavascriptString, allow_backtick: bool) u8 { + var single_cost: usize = 0; + var double_cost: usize = 0; + var backtick_cost: usize = 0; + var char: u8 = 0; + var i: usize = 0; + while (i < str.len) { + switch (str[i]) { + '\'' => { + single_cost += 1; + }, + '"' => { + double_cost += 1; + }, + '`' => { + backtick_cost += 1; + }, + '$' => { + if (i + 1 < str.len and str[i + 1] == '{') { + backtick_cost += 1; + } + }, + else => {}, + } + i += 1; + } + + char = '"'; + if (double_cost > single_cost) { + char = '\''; + + if (single_cost > backtick_cost and allow_backtick) { + char = '`'; + } + } else if (double_cost > backtick_cost and allow_backtick) { + char = '`'; + } + + return char; + } + + pub fn printNonNegativeFloat(p: *Printer, float: f64) void { + // cool thing about languages like this + // i know this is going to be in the stack and not the heap + var parts = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + // normally, you pay the cost of parsing a string formatter at runtime + // not in zig! CI pays for it instead + // its probably still doing some unnecessary integer conversion somewhere though + var slice = std.fmt.bufPrint(&parts, "{d}", .{float}) catch unreachable; + p.js.list.appendSlice(p.allocator, slice) catch unreachable; + } + + pub fn printQuotedUTF16(e: *Printer, text: JavascriptString, quote: u8) void { + // utf-8 is a max of 4 bytes + var temp = [4]u8{ 0, 0, 0, 0 }; + var i: usize = 0; + const n: usize = text.len; + var c: u16 = 0; + + e.js.growIfNeeded(text.len) catch unreachable; + + while (i < n) { + c = text[i]; + i += 1; + + // TODO: here + switch (c) { + // Special-case the null character since it may mess with code written in C + // that treats null characters as the end of the string. + 0x00 => { + // We don't want "\x001" to be written as "\01" + if (i < n) { + e.print("\\x00"); + } else { + e.print("\\0"); + } + }, + + // Special-case the bell character since it may cause dumping this file to + // the terminal to make a sound, which is undesirable. Note that we can't + // use an octal literal to print this shorter since octal literals are not + // allowed in strict mode (or in template strings). + 0x07 => { + e.print("\\x07"); + }, + 0x08 => { + e.print("\\b"); + }, + 0x0C => { + e.print("\\f"); + }, + '\n' => { + if (quote == '`') { + e.print("\n"); + } else { + e.print("\\n"); + } + }, + 0x0D => { + e.print("\\r"); + }, + 0x0B => { + e.print("\\r"); + }, + 0x5C => {}, + '\'' => {}, + '"' => {}, + '`' => {}, + '$' => {}, + 0x2028 => {}, + 0x2029 => {}, + 0xFEFF => {}, + else => {}, + } + } + } + pub fn printExpr(p: *Printer, expr: Expr, level: Level, flags: ExprFlag) void { p.addSourceMapping(expr.loc); @@ -295,7 +438,18 @@ pub fn NewPrinter(comptime ascii_only: bool) type { p.print(if (e.value) "true" else "false"); }, .e_string => |e| { - notimpl(); + // If this was originally a template literal, print it as one as long as we're not minifying + if (e.prefer_template) { + p.print("`"); + p.printQuotedUTF16(e.value, '`'); + p.print("`"); + return; + } + + const c = p.bestQuoteCharForString(e.value, true); + p.print(c); + p.printQuotedUTF16(e.value, c); + p.print(c); }, .e_template => |e| { notimpl(); @@ -323,7 +477,13 @@ pub fn NewPrinter(comptime ascii_only: bool) type { p.printSpaceBeforeIdentifier(); p.print("(-Infinity)"); } - } else if (!std.math.signbit(value)) {} else if (level.gte(.prefix)) { + } else if (!std.math.signbit(value)) { + p.printSpaceBeforeIdentifier(); + p.printNonNegativeFloat(absValue); + + // Remember the end of the latest number + p.prev_num_end = p.js.lenI(); + } else if (level.gte(.prefix)) { // Expressions such as "(-1).toString" need to wrap negative numbers. // Instead of testing for "value < 0" we test for "signbit(value)" and // "!isNaN(value)" because we need this to be true for "-0" and "-0 < 0" @@ -332,7 +492,7 @@ pub fn NewPrinter(comptime ascii_only: bool) type { p.printNonNegativeFloat(absValue); p.print(")"); } else { - p.printSpaceBeforeIdentifier(Op.Code.un_neg); + p.printSpaceBeforeOperator(Op.Code.un_neg); p.print("-"); p.printNonNegativeFloat(absValue); @@ -364,6 +524,26 @@ pub fn NewPrinter(comptime ascii_only: bool) type { } } + pub fn printSpaceBeforeOperator(p: *Printer, next: Op.Code) void { + // if (p.prev_op_end == p.js.lenI()) { + // const prev = p.prev_op; + // "+ + y" => "+ +y" + // "+ ++ y" => "+ ++y" + // "x + + y" => "x+ +y" + // "x ++ + y" => "x+++y" + // "x + ++ y" => "x+ ++y" + // "-- >" => "-- >" + // "< ! --" => "<! --" + // if (((prev == Op.Code.bin_add or prev == Op.Code.un_pos) and (next == Op.Code.bin_add or next == Op.Code.un_pos or next == Op.Code.un_pre_inc)) or + // ((prev == Op.Code.bin_sub or prev == Op.Code.un_neg) and (next == Op.Code.bin_sub or next == Op.Code.un_neg or next == Op.Code.un_pre_dec)) or + // (prev == Op.Code.un_post_dec and next == Op.Code.bin_gt) or + // (prev == Op.Code.un_not and next == Op.Code.un_pre_dec and p.js.len() > 1 and p.js.list.items[p.js.list.items.len - 2] == '<')) + // { + // p.print(" "); + // } + // } + } + pub fn printProperty(p: *Printer, prop: G.Property) void { notimpl(); } @@ -438,12 +618,14 @@ pub fn NewPrinter(comptime ascii_only: bool) type { } pub fn init(allocator: *std.mem.Allocator, tree: Ast, symbols: Symbol.Map, opts: Options) !Printer { + var js = try MutableString.init(allocator, 1024); return Printer{ .allocator = allocator, .import_records = tree.import_records, .options = opts, .symbols = symbols, - .js = try MutableString.init(allocator, 1024), + .js = js, + .writer = js.writer(), }; } }; diff --git a/src/json_parser.zig b/src/json_parser.zig index c6b7ec9aa..0654c074c 100644 --- a/src/json_parser.zig +++ b/src/json_parser.zig @@ -210,6 +210,9 @@ pub fn ParseTSConfig(log: logger.Loc, source: logger.Source, allocator: *std.mem const duplicateKeyJson = "{ \"name\": \"valid\", \"name\": \"invalid\" }"; +const js_printer = @import("js_printer.zig"); +const renamer = @import("renamer.zig"); + fn expectPrintedJSON(_contents: string, expected: string) void { if (alloc.dynamic_manager == null) { alloc.setup(std.heap.page_allocator) catch unreachable; @@ -219,15 +222,14 @@ fn expectPrintedJSON(_contents: string, expected: string) void { std.mem.copy(u8, contents, _contents); contents[contents.len - 1] = ';'; var log = logger.Log.init(alloc.dynamic); - const js_printer = @import("js_printer.zig"); - const renamer = @import("renamer.zig"); + defer log.msgs.deinit(); var source = logger.Source.initPathString( "source.json", contents, ); const expr = try ParseJSON(&source, &log, alloc.dynamic); - var stmt = Stmt.alloc(std.heap.page_allocator, S.SExpr{ .value = expr }, logger.Loc{ .start = 0 }); + var stmt = Stmt.alloc(alloc.dynamic, S.SExpr{ .value = expr }, logger.Loc{ .start = 0 }); var part = js_ast.Part{ .stmts = &([_]Stmt{stmt}), @@ -238,7 +240,7 @@ fn expectPrintedJSON(_contents: string, expected: string) void { std.debug.panic("--FAIL--\nExpr {s}\nLog: {s}\n--FAIL--", .{ expr, log.msgs.items[0].data.text }); } - const result = js_printer.printAst(std.heap.page_allocator, tree, symbol_map, true, js_printer.Options{ .to_module_ref = Ref{ .inner_index = 0 } }) catch unreachable; + const result = js_printer.printAst(alloc.dynamic, tree, symbol_map, true, js_printer.Options{ .to_module_ref = Ref{ .inner_index = 0 } }) catch unreachable; var js = result.js; @@ -259,6 +261,13 @@ test "ParseJSON" { expectPrintedJSON("true", "true"); expectPrintedJSON("false", "false"); expectPrintedJSON("1", "1"); + expectPrintedJSON("10", "10"); + expectPrintedJSON("100", "100"); + expectPrintedJSON("100.1", "100.1"); + expectPrintedJSON("19.1", "19.1"); + expectPrintedJSON("19.12", "19.12"); + expectPrintedJSON("3.4159820837456", "3.4159820837456"); + expectPrintedJSON("-10000.25", "-10000.25"); } test "ParseJSON DuplicateKey warning" { diff --git a/src/main.zig b/src/main.zig index 6bdd34cb4..5410b0e2a 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4,6 +4,8 @@ const logger = @import("logger.zig"); const alloc = @import("alloc.zig"); const options = @import("options.zig"); const js_parser = @import("js_parser.zig"); +const js_printer = @import("js_printer.zig"); +const js_ast = @import("js_ast.zig"); pub fn main() anyerror!void { try alloc.setup(std.heap.page_allocator); @@ -20,11 +22,14 @@ pub fn main() anyerror!void { const entryPointName = "/var/foo/index.js"; const code = "for (let i = 0; i < 100; i++) { console.log('hi') aposkdpoaskdpokasdpokasdpokasdpokasdpoaksdpoaksdpoaskdpoaksdpoaksdpoaskdpoaskdpoasdk; "; - var log = logger.Log.init(alloc.dynamic); + const opts = try options.TransformOptions.initUncached(alloc.dynamic, entryPointName, code); + var log = logger.Log.init(alloc.dynamic); var source = logger.Source.initFile(opts.entry_point, alloc.dynamic); var parser = try js_parser.Parser.init(opts, &log, &source, alloc.dynamic); var res = try parser.parse(); - std.debug.print("{s}", .{res}); + const printed = try js_printer.printAst(alloc.dynamic, res.ast, js_ast.Symbol.Map{}, false, js_printer.Options{ .to_module_ref = js_ast.Ref{ .inner_index = 0 } }); + + std.debug.print("{s}\n{s}", .{ res, printed }); } diff --git a/src/main_wasm.zig b/src/main_wasm.zig index 73f03f9ad..5410b0e2a 100644 --- a/src/main_wasm.zig +++ b/src/main_wasm.zig @@ -4,6 +4,8 @@ const logger = @import("logger.zig"); const alloc = @import("alloc.zig"); const options = @import("options.zig"); const js_parser = @import("js_parser.zig"); +const js_printer = @import("js_printer.zig"); +const js_ast = @import("js_ast.zig"); pub fn main() anyerror!void { try alloc.setup(std.heap.page_allocator); @@ -20,8 +22,14 @@ pub fn main() anyerror!void { 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); + + const opts = try options.TransformOptions.initUncached(alloc.dynamic, entryPointName, code); + var log = logger.Log.init(alloc.dynamic); + var source = logger.Source.initFile(opts.entry_point, alloc.dynamic); + var parser = try js_parser.Parser.init(opts, &log, &source, alloc.dynamic); var res = try parser.parse(); - std.debug.print("{s}", .{res}); + const printed = try js_printer.printAst(alloc.dynamic, res.ast, js_ast.Symbol.Map{}, false, js_printer.Options{ .to_module_ref = js_ast.Ref{ .inner_index = 0 } }); + + std.debug.print("{s}\n{s}", .{ res, printed }); } diff --git a/src/string_mutable.zig b/src/string_mutable.zig index 8c9c9e99e..7a7964dec 100644 --- a/src/string_mutable.zig +++ b/src/string_mutable.zig @@ -7,15 +7,24 @@ pub const MutableString = struct { allocator: *std.mem.Allocator, list: std.ArrayListUnmanaged(u8), - pub const Writer = std.io.Writer(@This(), anyerror, MutableString.writeAll); + pub const Writer = std.io.Writer(*@This(), anyerror, MutableString.writeAll); pub fn writer(self: *MutableString) Writer { return Writer{ .context = self, }; } - pub fn writeAll(self: *MutableString, bytes: []u8) !usize { - try self.list.appendSlice(self.allocator, bytes); + pub fn growIfNeeded(self: *MutableString, amount: usize) !void { + const new_capacity = self.list.items.len + amount; + if (self.list.capacity < new_capacity) { + try self.list.ensureCapacity(self.allocator, new_capacity); + } + } + + pub fn writeAll(self: *MutableString, bytes: string) !usize { + const new_capacity = self.list.items.len + bytes.len; + try self.list.ensureCapacity(self.allocator, new_capacity); + self.list.appendSliceAssumeCapacity(bytes); return self.list.items.len; } @@ -74,7 +83,11 @@ pub const MutableString = struct { } } pub fn growBy(self: *MutableString, amount: usize) callconv(.Inline) !void { - try self.list.ensureCapacity(self.allocator, self.list.capacity + amount); + try self.ensureCapacity(self.list.capacity + amount); + } + + pub fn ensureCapacity(self: *MutableString, amount: usize) callconv(.Inline) !void { + try self.list.ensureCapacity(self.allocator, amount); } pub fn deinit(self: *MutableString) !void { |