@samitouri / QOS-React / commits / e30a9e9258

[hir syntax] Handle TemplateLiteral syntax

Mofei Zhang committed Feb 3, 2023 at 15:44 UTC e30a9e9258cebdf4e017443aa1606eecb2143f9a
9 files changed +131 -1
compiler/forget/src/HIR/BuildHIR.ts
+34
@@ -1420,6 +1420,40 @@ function lowerExpression(
1420 loc: exprLoc,
1421 };
1422 }
1423 + case "TemplateLiteral": {
1424 + const expr = exprPath as NodePath<t.TemplateLiteral>;
1425 + const subexprs = expr.get("expressions");
1426 + const quasis = expr.get("quasis");
1427 +
1428 + if (subexprs.length !== quasis.length - 1) {
1429 + builder.errors.push({
1430 + reason: `(BuildHIR::lowerAssignment) Unexpected quasi and subexpression lengths in TemplateLiteral.`,
1431 + severity: ErrorSeverity.InvalidInput,
1432 + nodePath: exprPath,
1433 + });
1434 + return { kind: "UnsupportedNode", node: exprNode, loc: exprLoc };
1435 + }
1436 +
1437 + if (subexprs.some((e) => !e.isExpression())) {
1438 + builder.errors.push({
1439 + reason: `(BuildHIR::lowerAssignment) Handle TSType in TemplateLiteral.`,
1440 + severity: ErrorSeverity.Todo,
1441 + nodePath: exprPath,
1442 + });
1443 + return { kind: "UnsupportedNode", node: exprNode, loc: exprLoc };
1444 + }
1445 +
1446 + const subexprPlaces = subexprs.map((e) =>
1447 + lowerExpressionToPlace(builder, e as NodePath<t.Expression>)
1448 + );
1449 +
1450 + return {
1451 + kind: "TemplateLiteral",
1452 + subexprs: subexprPlaces,
1453 + quasis: expr.get("quasis").map((q) => q.node.value),
1454 + loc: exprLoc,
1455 + };
1456 + }
1457 case "UnaryExpression": {
1458 let expr = exprPath as NodePath<t.UnaryExpression>;
1459 return {
compiler/forget/src/HIR/HIR.ts
+5 -1
@@ -446,7 +446,11 @@ export type InstructionData =
446 tag: Place;
447 value: { raw: string; cooked?: string };
448 }
449 -
449 + | {
450 + kind: "TemplateLiteral";
451 + subexprs: Array<Place>;
452 + quasis: Array<{ raw: string; cooked?: string }>;
453 + }
454 /**
455 * Catch-all for statements such as type imports, nested class declarations, etc
456 * which are not directly represented, but included for completeness and to allow
compiler/forget/src/HIR/PrintHIR.ts
+14
@@ -6,6 +6,7 @@
6 */
7
8 import generate from "@babel/generator";
9 +import invariant from "invariant";
10 import DisjointSet from "../Utils/DisjointSet";
11 import { assertExhaustive } from "../Utils/utils";
12 import {
@@ -370,6 +371,19 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
371 )} : ${printInstructionValue(instrValue.alternate)}`;
372 break;
373 }
374 + case "TemplateLiteral": {
375 + value = "`";
376 + invariant(
377 + instrValue.subexprs.length === instrValue.quasis.length - 1,
378 + "Bad assumption about quasi length."
379 + );
380 + for (let i = 0; i < instrValue.subexprs.length; i++) {
381 + value += instrValue.quasis[i].raw;
382 + value += `\${${printPlace(instrValue.subexprs[i])}}`;
383 + }
384 + value += instrValue.quasis.at(-1)!.raw + "`";
385 + break;
386 + }
387 default: {
388 assertExhaustive(
389 instrValue,
compiler/forget/src/HIR/visitors.ts
+8
@@ -127,6 +127,10 @@ export function* eachInstructionValueOperand(
127 yield instrValue.value;
128 break;
129 }
130 + case "TemplateLiteral": {
131 + yield* instrValue.subexprs;
132 + break;
133 + }
134 case "UnsupportedNode":
135 case "Primitive":
136 case "JSXText": {
@@ -251,6 +255,10 @@ export function mapInstructionOperands(
255 instrValue.value = fn(instrValue.value);
256 break;
257 }
258 + case "TemplateLiteral": {
259 + instrValue.subexprs = instrValue.subexprs.map(fn);
260 + break;
261 + }
262 case "UnsupportedNode":
263 case "Primitive":
264 case "JSXText": {
compiler/forget/src/Inference/InferReferenceEffects.ts
+7
@@ -599,6 +599,13 @@ function inferBlock(env: Environment, block: BasicBlock) {
599 effectKind = Effect.Mutate;
600 break;
601 }
602 + case "TemplateLiteral": {
603 + // template literal (with no tag function) always produces
604 + // an immutable string
605 + valueKind = ValueKind.Immutable;
606 + effectKind = Effect.Read;
607 + break;
608 + }
609 case "JSXText":
610 case "Primitive": {
611 valueKind = ValueKind.Immutable;
compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts
+9
@@ -415,6 +415,7 @@ const createTaggedTemplateExpression = withLoc(t.taggedTemplateExpression);
415 const createLogicalExpression = withLoc(t.logicalExpression);
416 const createSequenceExpression = withLoc(t.sequenceExpression);
417 const createConditionalExpression = withLoc(t.conditionalExpression);
418 +const createTemplateLiteral = withLoc(t.templateLiteral);
419
420 type Temporaries = Map<IdentifierId, t.Expression>;
421
@@ -751,6 +752,14 @@ function codegenInstructionValue(
752 }
753 break;
754 }
755 + case "TemplateLiteral": {
756 + value = createTemplateLiteral(
757 + instrValue.loc,
758 + instrValue.quasis.map((q) => t.templateElement(q)),
759 + instrValue.subexprs.map((p) => codegenPlace(cx, p))
760 + );
761 + break;
762 + }
763 default: {
764 assertExhaustive(
765 instrValue,
compiler/forget/src/ReactiveScopes/InferReactiveScopeVariables.ts
+1
@@ -174,6 +174,7 @@ function mayAllocate(value: InstructionValue): boolean {
174 case "ComputedLoad":
175 case "JSXText":
176 case "UnaryExpression":
177 + case "TemplateLiteral":
178 case "Primitive": {
179 return false;
180 }
compiler/forget/src/__tests__/fixtures/hir/template-literal.expect.md new
+43
@@ -0,0 +1,43 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function componentA(props) {
6 + let t = `hello ${props.a}, ${props.b}!`;
7 + t += ``;
8 + return t;
9 +}
10 +
11 +function componentB(props) {
12 + let x = useFoo(`hello ${props.a}`);
13 + return x;
14 +}
15 +
16 +```
17 +
18 +## Code
19 +
20 +```javascript
21 +function componentA(props) {
22 + const t = `hello ${props.a}, ${props.b}!`;
23 + const t$0 = t + ``;
24 + return t$0;
25 +}
26 +
27 +function componentB(props) {
28 + const $ = React.useMemoCache();
29 + const t0 = `hello ${props.a}`;
30 + const c_0 = $[0] !== t0;
31 + let x;
32 + if (c_0) {
33 + x = useFoo(t0);
34 + $[0] = t0;
35 + $[1] = x;
36 + } else {
37 + x = $[1];
38 + }
39 + return x;
40 +}
41 +
42 +```
43 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/template-literal.js new
+10
@@ -0,0 +1,10 @@
1 +function componentA(props) {
2 + let t = `hello ${props.a}, ${props.b}!`;
3 + t += ``;
4 + return t;
5 +}
6 +
7 +function componentB(props) {
8 + let x = useFoo(`hello ${props.a}`);
9 + return x;
10 +}