diff --git a/compiler/forget/src/HIR/BuildHIR.ts b/compiler/forget/src/HIR/BuildHIR.ts index 44565f7ae6..b5cec88baf 100644 --- a/compiler/forget/src/HIR/BuildHIR.ts +++ b/compiler/forget/src/HIR/BuildHIR.ts @@ -623,10 +623,6 @@ function lowerStatement( nodeKind === "let" ? InstructionKind.Let : InstructionKind.Const; for (const declaration of stmt.get("declarations")) { const id = declaration.get("id"); - invariant( - id.isIdentifier(), - "Support non-identifier variable declarations" - ); const init = declaration.get("init"); let value: InstructionValue; if (init.hasNode()) { @@ -942,45 +938,13 @@ function lowerExpression( if (operator === "=") { const left = expr.get("left"); - const leftNode = left.node; - switch (leftNode.type) { - case "Identifier": { - const lvalue = left as NodePath; - return lowerAssignment( - builder, - leftNode.loc ?? GeneratedSource, - InstructionKind.Reassign, - lvalue, - lowerExpression(builder, expr.get("right")) - ); - } - case "MemberExpression": { - const leftExpr = left as NodePath; - const property = leftExpr.get("property"); - invariant( - property.isIdentifier(), - "Assignment expression to dynamic properties is not yet supported" - ); - const right = lowerExpressionToPlace(builder, expr.get("right")); - const object = lowerExpressionToPlace( - builder, - leftExpr.get("object") - ); - return { - kind: "PropertyStore", - object, - property: property.node.name, - value: right, - loc: leftNode.loc ?? GeneratedSource, - }; - } - default: { - todoInvariant( - false, - "Support lvalues other than identifier and member expression" - ); - } - } + return lowerAssignment( + builder, + left.node.loc ?? GeneratedSource, + InstructionKind.Reassign, + left, + lowerExpression(builder, expr.get("right")) + ); } const operators: { [key: string]: t.BinaryExpression["operator"] } = { @@ -1399,15 +1363,131 @@ function lowerAssignment( builder: HIRBuilder, loc: SourceLocation, kind: InstructionKind, - lvalue: NodePath, + lvaluePath: NodePath, value: InstructionValue ): InstructionValue { - const id = lowerIdentifier(builder, lvalue); - builder.push({ - id: makeInstructionId(0), - lvalue: { place: id, kind }, - value, - loc, - }); - return id; + const lvalueNode = lvaluePath.node; + switch (lvalueNode.type) { + case "Identifier": { + const lvalue = lvaluePath as NodePath; + const place = lowerIdentifier(builder, lvalue); + builder.push({ + id: makeInstructionId(0), + lvalue: { place: { ...place }, kind }, + value, + loc, + }); + return place; + } + case "MemberExpression": { + const leftExpr = lvaluePath as NodePath; + const property = leftExpr.get("property"); + invariant( + property.isIdentifier(), + "Assignment expression to dynamic properties is not yet supported" + ); + const object = lowerExpressionToPlace(builder, leftExpr.get("object")); + let valuePlace: Place; + if (value.kind === "Identifier") { + valuePlace = value; + } else { + valuePlace = buildTemporaryPlace(builder, loc); + builder.push({ + id: makeInstructionId(0), + lvalue: { place: { ...valuePlace }, kind: InstructionKind.Const }, + value, + loc, + }); + } + return { + kind: "PropertyStore", + object, + property: property.node.name, + value: valuePlace, + loc, + }; + } + case "ArrayPattern": { + const lvalue = lvaluePath as NodePath; + const arrayPlace = buildTemporaryPlace(builder, loc); + builder.push({ + id: makeInstructionId(0), + lvalue: { place: { ...arrayPlace }, kind: InstructionKind.Const }, + value, + loc, + }); + const elements = lvalue.get("elements"); + for (let i = 0; i < elements.length; i++) { + const element = elements[i]; + if (!element.hasNode()) { + continue; + } + todoInvariant( + element.node.type !== "RestElement", + "Rest elements are not supported yet" + ); + const property = buildTemporaryPlace( + builder, + element.node.loc ?? GeneratedSource + ); + builder.push({ + id: makeInstructionId(0), + lvalue: { place: { ...property }, kind: InstructionKind.Const }, + value: { + kind: "Primitive", + value: i, + loc: element.node.loc ?? GeneratedSource, + }, + loc: element.node.loc ?? GeneratedSource, + }); + const value: InstructionValue = { + kind: "IndexLoad", + loc, + object: { ...arrayPlace }, + property, + }; + lowerAssignment(builder, loc, kind, element, value); + } + return arrayPlace; + } + case "ObjectPattern": { + const lvalue = lvaluePath as NodePath; + const objectPlace = buildTemporaryPlace(builder, loc); + builder.push({ + id: makeInstructionId(0), + lvalue: { place: { ...objectPlace }, kind }, + value, + loc, + }); + const properties = lvalue.get("properties"); + for (let i = 0; i < properties.length; i++) { + const property = properties[i]; + invariant( + property.isObjectProperty(), + "Rest elements are not supported yet" + ); + const key = property.get("key"); + invariant( + key.isIdentifier(), + "TODO: support non-identifier object property keys" + ); + const element = property.get("value"); + invariant( + element.isLVal(), + "Expected object property value to be an lvalue" + ); + const value: InstructionValue = { + kind: "PropertyLoad", + loc, + object: { ...objectPlace }, + property: key.node.name, + }; + lowerAssignment(builder, loc, kind, element, value); + } + return objectPlace; + } + default: { + todo("Support other lvalue types beyond identifier"); + } + } } diff --git a/compiler/forget/src/__tests__/fixtures/hir/destructuring-assignment.expect.md b/compiler/forget/src/__tests__/fixtures/hir/destructuring-assignment.expect.md new file mode 100644 index 0000000000..c24172b80b --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/destructuring-assignment.expect.md @@ -0,0 +1,40 @@ + +## Input + +```javascript +function foo(a, b, c) { + let d, g, n, o; + [ + d, + [ + { + e: { f: g }, + }, + ], + ] = a; + ({ + l: { + m: [[n]], + }, + o, + } = b); +} + +``` + +## Code + +```javascript +function foo(a, b, c) { + const d = undefined; + const g = undefined; + const n = undefined; + const o = undefined; + const d$0 = a[0]; + const g$1 = a[1][0].e.f; + const n$2 = b.l.m[0][0]; + const o$3 = b.o; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/destructuring-assignment.js b/compiler/forget/src/__tests__/fixtures/hir/destructuring-assignment.js new file mode 100644 index 0000000000..a228348a64 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/destructuring-assignment.js @@ -0,0 +1,17 @@ +function foo(a, b, c) { + let d, g, n, o; + [ + d, + [ + { + e: { f: g }, + }, + ], + ] = a; + ({ + l: { + m: [[n]], + }, + o, + } = b); +} diff --git a/compiler/forget/src/__tests__/fixtures/hir/destructuring.expect.md b/compiler/forget/src/__tests__/fixtures/hir/destructuring.expect.md new file mode 100644 index 0000000000..c8f7a270c2 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/destructuring.expect.md @@ -0,0 +1,35 @@ + +## Input + +```javascript +function foo(a, b, c) { + const [ + d, + [ + { + e: { f }, + }, + ], + ] = a; + const { + l: { + m: [[n]], + }, + o, + } = b; +} + +``` + +## Code + +```javascript +function foo(a, b, c) { + const d = a[0]; + const f = a[1][0].e.f; + const n = b.l.m[0][0]; + const o = b.o; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/destructuring.js b/compiler/forget/src/__tests__/fixtures/hir/destructuring.js new file mode 100644 index 0000000000..72ffef9759 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/destructuring.js @@ -0,0 +1,16 @@ +function foo(a, b, c) { + const [ + d, + [ + { + e: { f }, + }, + ], + ] = a; + const { + l: { + m: [[n]], + }, + o, + } = b; +}