This commit is contained in:
Jarred Sumner
2021-04-26 00:34:45 -07:00
parent 19ab1672ca
commit 568db047a3
8 changed files with 247 additions and 23 deletions

View File

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

View File

@@ -595,7 +595,7 @@ pub fn NewLexerType(comptime jsonOptions: ?JSONOptions) type {
},
else => {
lexer.token = T.t_plus;
lexer.token = T.t_minus;
},
}
},

View File

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

View File

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

View File

@@ -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" {

View File

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

View File

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

View File

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