diff options
| author | 2023-05-18 10:52:34 -0700 | |
|---|---|---|
| committer | 2023-05-18 10:52:34 -0700 | |
| commit | 621232c197c0f6f562061e976ae39301cf85897f (patch) | |
| tree | 3d282824b2cf6c15136f5096b4c898609949d4aa /src | |
| parent | 642b3978a6ab518c717fce36acc66004b7dd09f5 (diff) | |
| download | bun-621232c197c0f6f562061e976ae39301cf85897f.tar.gz bun-621232c197c0f6f562061e976ae39301cf85897f.tar.zst bun-621232c197c0f6f562061e976ae39301cf85897f.zip | |
use raw template contents for tagged templates (#2937)
* use raw template contents when tagged
* use union for template contents
* pointer to cooked contents
* raw if suffix
* fix and don't skip test
Diffstat (limited to 'src')
| -rw-r--r-- | src/js_ast.zig | 51 | ||||
| -rw-r--r-- | src/js_parser.zig | 23 | ||||
| -rw-r--r-- | src/js_printer.zig | 23 |
3 files changed, 66 insertions, 31 deletions
diff --git a/src/js_ast.zig b/src/js_ast.zig index fdd220926..a3fb81402 100644 --- a/src/js_ast.zig +++ b/src/js_ast.zig @@ -2358,13 +2358,23 @@ pub const E = struct { pub const TemplatePart = struct { value: ExprNodeIndex, tail_loc: logger.Loc, - tail: E.String, + tail: Template.Contents, }; pub const Template = struct { tag: ?ExprNodeIndex = null, - head: E.String, parts: []TemplatePart = &([_]TemplatePart{}), + head: Contents, + + pub const Contents = union(Tag) { + cooked: E.String, + raw: string, + + const Tag = enum { + cooked, + raw, + }; + }; /// "`a${'b'}c`" => "`abc`" pub fn fold( @@ -2372,7 +2382,7 @@ pub const E = struct { allocator: std.mem.Allocator, loc: logger.Loc, ) Expr { - if (this.tag != null or !this.head.isUTF8()) { + if (this.tag != null or (this.head == .cooked and !this.head.cooked.isUTF8())) { // we only fold utf-8/ascii for now return Expr{ .data = .{ @@ -2382,14 +2392,17 @@ pub const E = struct { }; } + std.debug.assert(this.head == .cooked); + if (this.parts.len == 0) { - return Expr.init(E.String, this.head, loc); + return Expr.init(E.String, this.head.cooked, loc); } var parts = std.ArrayList(TemplatePart).initCapacity(allocator, this.parts.len) catch unreachable; - var head = Expr.init(E.String, this.head, loc); + var head = Expr.init(E.String, this.head.cooked, loc); for (this.parts) |part_| { var part = part_; + std.debug.assert(part.tail == .cooked); switch (part.value.data) { .e_number => { @@ -2412,28 +2425,29 @@ pub const E = struct { else => {}, } - if (part.value.data == .e_string and part.tail.isUTF8() and part.value.data.e_string.isUTF8()) { + if (part.value.data == .e_string and part.tail.cooked.isUTF8() and part.value.data.e_string.isUTF8()) { if (parts.items.len == 0) { if (part.value.data.e_string.len() > 0) { head.data.e_string.push(part.value.data.e_string); } - if (part.tail.len() > 0) { + if (part.tail.cooked.len() > 0) { head.data.e_string.resolveRopeIfNeeded(allocator); - head.data.e_string.push(Expr.init(E.String, part.tail, part.tail_loc).data.e_string); + head.data.e_string.push(Expr.init(E.String, part.tail.cooked, part.tail_loc).data.e_string); } continue; } else { var prev_part = &parts.items[parts.items.len - 1]; + std.debug.assert(prev_part.tail == .cooked); - if (prev_part.tail.isUTF8()) { + if (prev_part.tail.cooked.isUTF8()) { if (part.value.data.e_string.len() > 0) { - prev_part.tail.push(part.value.data.e_string); + prev_part.tail.cooked.push(part.value.data.e_string); } - if (part.tail.len() > 0) { - prev_part.tail.push(Expr.init(E.String, part.tail, part.tail_loc).data.e_string); + if (part.tail.cooked.len() > 0) { + prev_part.tail.cooked.push(Expr.init(E.String, part.tail.cooked, part.tail_loc).data.e_string); } } else { parts.appendAssumeCapacity(part); @@ -2455,7 +2469,7 @@ pub const E = struct { E.Template{ .tag = null, .parts = parts.items, - .head = head.data.e_string.*, + .head = .{ .cooked = head.data.e_string.* }, }, loc, ); @@ -7091,7 +7105,10 @@ pub const Macro = struct { return toStringValue(this, ctx, str.*); }, .e_template => |template| { - const str = template.head; + const str = switch (template.head) { + .cooked => |cooked| cooked, + .raw => |raw| E.String.init(raw), + }; if (str.isBlank()) { return JSC.ZigString.init("").toValue(ctx.ptr()).asRef(); @@ -7134,8 +7151,10 @@ pub const Macro = struct { return JSBindings.toStringValue(this, ctx, str.*); }, .e_template => |template| { - return JSBindings.toStringValue(this, ctx, template.head); - // return JSBindings.toTemplatePrimitive(this, ctx, template.*); + return switch (template.head) { + .cooked => |cooked| JSBindings.toStringValue(this, ctx, cooked), + .raw => |raw| JSBindings.toStringValue(this, ctx, E.String.init(raw)), + }; }, .e_number => |number| { return JSBindings.toNumberValue(this, number); diff --git a/src/js_parser.zig b/src/js_parser.zig index 79a0549f0..90d7a5d24 100644 --- a/src/js_parser.zig +++ b/src/js_parser.zig @@ -12507,7 +12507,7 @@ fn NewParser_( return true; } - pub fn parseTemplateParts(p: *P, _: bool) ![]E.TemplatePart { + pub fn parseTemplateParts(p: *P, include_raw: bool) ![]E.TemplatePart { var parts = ListManaged(E.TemplatePart).initCapacity(p.allocator, 1) catch unreachable; // Allow "in" inside template literals var oldAllowIn = p.allow_in; @@ -12519,7 +12519,10 @@ fn NewParser_( const tail_loc = p.lexer.loc(); try p.lexer.rescanCloseBraceAsTemplateToken(); - var tail = p.lexer.toEString(); + const tail: E.Template.Contents = brk: { + if (!include_raw) break :brk .{ .cooked = p.lexer.toEString() }; + break :brk .{ .raw = p.lexer.rawTemplateContents() }; + }; parts.append(E.TemplatePart{ .value = value, @@ -12617,7 +12620,7 @@ fn NewParser_( // Reset the optional chain flag by default. That way we won't accidentally // treat "c.d" as OptionalChainContinue in "a?.b + c.d". - var old_optional_chain = optional_chain; + const old_optional_chain = optional_chain; optional_chain = null; switch (p.lexer.token) { .t_dot => { @@ -12778,11 +12781,11 @@ fn NewParser_( p.log.addRangeError(p.source, p.lexer.range(), "Template literals cannot have an optional chain as a tag") catch unreachable; } // p.markSyntaxFeature(compat.TemplateLiteral, p.lexer.Range()); - const head = p.lexer.toEString(); + const head = p.lexer.rawTemplateContents(); try p.lexer.next(); left = p.newExpr(E.Template{ .tag = left, - .head = head, + .head = .{ .raw = head }, }, left.loc); }, .t_template_head => { @@ -12790,10 +12793,14 @@ fn NewParser_( p.log.addRangeError(p.source, p.lexer.range(), "Template literals cannot have an optional chain as a tag") catch unreachable; } // p.markSyntaxFeature(compat.TemplateLiteral, p.lexer.Range()); - const head = p.lexer.toEString(); + const head = p.lexer.rawTemplateContents(); const partsGroup = try p.parseTemplateParts(true); const tag = left; - left = p.newExpr(E.Template{ .tag = tag, .head = head, .parts = partsGroup }, left.loc); + left = p.newExpr(E.Template{ + .tag = tag, + .head = .{ .raw = head }, + .parts = partsGroup, + }, left.loc); }, .t_open_bracket => { // When parsing a decorator, ignore EIndex expressions since they may be @@ -13611,7 +13618,7 @@ fn NewParser_( // if () return p.newExpr(E.Template{ - .head = head, + .head = .{ .cooked = head }, .parts = parts, }, loc); }, diff --git a/src/js_printer.zig b/src/js_printer.zig index 52dfdc5bd..f2c9d0ed5 100644 --- a/src/js_printer.zig +++ b/src/js_printer.zig @@ -2546,19 +2546,28 @@ fn NewPrinter( } p.print("`"); - if (e.head.isPresent()) { - e.head.resolveRopeIfNeeded(p.options.allocator); - - p.printStringContent(&e.head, '`'); + switch (e.head) { + .raw => |raw| p.print(raw), + .cooked => |*cooked| { + if (cooked.isPresent()) { + cooked.resolveRopeIfNeeded(p.options.allocator); + p.printStringContent(cooked, '`'); + } + }, } for (e.parts) |*part| { p.print("${"); p.printExpr(part.value, .lowest, ExprFlag.None()); p.print("}"); - if (part.tail.isPresent()) { - part.tail.resolveRopeIfNeeded(p.options.allocator); - p.printStringContent(&part.tail, '`'); + switch (part.tail) { + .raw => |raw| p.print(raw), + .cooked => |*cooked| { + if (cooked.isPresent()) { + cooked.resolveRopeIfNeeded(p.options.allocator); + p.printStringContent(cooked, '`'); + } + }, } } p.print("`"); |
