aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Dylan Conway <35280289+dylan-conway@users.noreply.github.com> 2023-05-18 10:52:34 -0700
committerGravatar GitHub <noreply@github.com> 2023-05-18 10:52:34 -0700
commit621232c197c0f6f562061e976ae39301cf85897f (patch)
tree3d282824b2cf6c15136f5096b4c898609949d4aa /src
parent642b3978a6ab518c717fce36acc66004b7dd09f5 (diff)
downloadbun-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.zig51
-rw-r--r--src/js_parser.zig23
-rw-r--r--src/js_printer.zig23
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("`");