@samitouri / QOS-React / commits / bb8ae86c24

[valueblocks] ReactiveFunction repr for logical expressions

Extends ReactiveInstruction's value type to be a regular InstructionValue *or* a LogicalValue. LogicalValue is operator, left, and right. It's really convenient that we've already distinguished Instruction/ReactiveInstruction now — while the _helpers_ here are updated to handle this new value type, the types ensure that HIR can never encounter a LogicalValue. The actual conversion of logical terminals into this value is complex and is later in the stack.

Joe Savona committed Jan 31, 2023 at 13:39 UTC bb8ae86c240cac6068e5cfddebc4252bfb886287
10 files changed +75 -22
compiler/forget/src/HIR/BuildHIR.ts
+1
@@ -934,6 +934,7 @@ function lowerExpression(
934 id: makeInstructionId(0),
935 test: testBlock.id,
936 operator: expr.node.operator,
937 + loc: exprLoc,
938 },
939 testBlock
940 );
compiler/forget/src/HIR/HIR.ts
+12 -1
@@ -89,7 +89,17 @@ export type ReactiveTerminalStatement<
89 export type ReactiveInstruction = {
90 id: InstructionId;
91 lvalue: LValue | null;
92 - value: InstructionValue;
92 + value: ReactiveValue;
93 + loc: SourceLocation;
94 +};
95 +
96 +export type ReactiveValue = InstructionValue | ReactiveLogicalValue;
97 +
98 +export type ReactiveLogicalValue = {
99 + kind: "LogicalExpression";
100 + operator: t.LogicalExpression["operator"];
101 + left: ReactiveValue;
102 + right: ReactiveValue;
103 loc: SourceLocation;
104 };
105
@@ -298,6 +308,7 @@ export type LogicalTerminal = {
308 test: BlockId;
309 fallthrough: BlockId;
310 id: InstructionId;
311 + loc: SourceLocation;
312 };
313
314 /**
compiler/forget/src/HIR/PrintHIR.ts
+8 -1
@@ -22,6 +22,7 @@ import {
22 Place,
23 ReactiveInstruction,
24 ReactiveScope,
25 + ReactiveValue,
26 SourceLocation,
27 Terminal,
28 Type,
@@ -201,7 +202,7 @@ export function printTerminal(terminal: Terminal): Array<string> | string {
202 return value;
203 }
204
204 -export function printInstructionValue(instrValue: InstructionValue): string {
205 +export function printInstructionValue(instrValue: ReactiveValue): string {
206 let value = "";
207 switch (instrValue.kind) {
208 case "ArrayExpression": {
@@ -332,6 +333,12 @@ export function printInstructionValue(instrValue: InstructionValue): string {
333 value = `${instrValue.tag}\`${instrValue.value.raw}\``;
334 break;
335 }
336 + case "LogicalExpression": {
337 + value = `Logical ${printInstructionValue(instrValue.left)} ${
338 + instrValue.operator
339 + } ${printInstructionValue(instrValue.right)}`;
340 + break;
341 + }
342 default: {
343 assertExhaustive(
344 instrValue,
compiler/forget/src/HIR/visitors.ts
+1
@@ -291,6 +291,7 @@ export function mapTerminalSuccessors(
291 fallthrough,
292 operator: terminal.operator,
293 id: makeInstructionId(0),
294 + loc: terminal.loc,
295 };
296 }
297 case "return": {
compiler/forget/src/ReactiveScopes/BuildReactiveBlocks.ts
+6 -3
@@ -19,9 +19,12 @@ import {
19 ReactiveValueBlock,
20 ScopeId,
21 } from "../HIR";
22 -import { eachInstructionValueOperand } from "../HIR/visitors";
22 import { assertExhaustive } from "../Utils/utils";
24 -import { eachTerminalBlock, mapTerminalBlocks } from "./visitors";
23 +import {
24 + eachReactiveValueOperand,
25 + eachTerminalBlock,
26 + mapTerminalBlocks,
27 +} from "./visitors";
28
29 /**
30 * Note: this is the 4th of 4 passes that determine how to break a function into discrete
@@ -222,7 +225,7 @@ export function getInstructionScope({
225 if (lvalueScope !== null) {
226 return lvalueScope;
227 }
225 - for (const operand of eachInstructionValueOperand(value)) {
228 + for (const operand of eachReactiveValueOperand(value)) {
229 const operandScope = getPlaceScope(id, operand);
230 if (operandScope !== null) {
231 return operandScope;
compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts
+12 -2
@@ -13,7 +13,6 @@ import {
13 Identifier,
14 IdentifierId,
15 InstructionKind,
16 - InstructionValue,
16 LValue,
17 Place,
18 ReactiveBlock,
@@ -22,6 +21,7 @@ import {
21 ReactiveScope,
22 ReactiveScopeDependency,
23 ReactiveTerminal,
24 + ReactiveValue,
25 ReactiveValueBlock,
26 SourceLocation,
27 } from "../HIR/HIR";
@@ -430,6 +430,7 @@ const createLabelledStatement = withLoc(t.labeledStatement);
430 const createVariableDeclaration = withLoc(t.variableDeclaration);
431 const createWhileStatement = withLoc(t.whileStatement);
432 const createTaggedTemplateExpression = withLoc(t.taggedTemplateExpression);
433 +const createLogicalExpression = withLoc(t.logicalExpression);
434
435 type Temporaries = Map<IdentifierId, t.Expression>;
436
@@ -482,7 +483,7 @@ function codegenInstruction(
483
484 function codegenInstructionValue(
485 temp: Temporaries,
485 - instrValue: InstructionValue
486 + instrValue: ReactiveValue
487 ): t.Expression {
488 let value: t.Expression;
489 switch (instrValue.kind) {
@@ -685,6 +686,15 @@ function codegenInstructionValue(
686 );
687 break;
688 }
689 + case "LogicalExpression": {
690 + value = createLogicalExpression(
691 + instrValue.loc,
692 + instrValue.operator,
693 + codegenInstructionValue(temp, instrValue.left),
694 + codegenInstructionValue(temp, instrValue.right)
695 + );
696 + break;
697 + }
698 default: {
699 assertExhaustive(
700 instrValue,
compiler/forget/src/ReactiveScopes/MergeOverlappingReactiveScopes.ts
+8 -5
@@ -8,21 +8,24 @@
8 import invariant from "invariant";
9 import {
10 InstructionId,
11 - InstructionValue,
11 makeInstructionId,
12 Place,
13 ReactiveBlock,
14 ReactiveFunction,
15 ReactiveInstruction,
16 ReactiveScope,
17 + ReactiveValue,
18 ReactiveValueBlock,
19 ScopeId,
20 } from "../HIR";
21 -import { eachInstructionValueOperand } from "../HIR/visitors";
21 import DisjointSet from "../Utils/DisjointSet";
22 import { retainWhere } from "../Utils/utils";
23 import { getPlaceScope } from "./BuildReactiveBlocks";
25 -import { eachTerminalBlock, eachTerminalOperand } from "./visitors";
24 +import {
25 + eachReactiveValueOperand,
26 + eachTerminalBlock,
27 + eachTerminalOperand,
28 +} from "./visitors";
29
30 /**
31 * Note: this is the 3rd of 4 passes that determine how to break a function into discrete
@@ -184,10 +187,10 @@ function visitInstruction(
187 function visitValue(
188 context: Context,
189 id: InstructionId,
187 - value: InstructionValue
190 + value: ReactiveValue
191 ): void {
192 context.visitId(id);
190 - for (const operand of eachInstructionValueOperand(value)) {
193 + for (const operand of eachReactiveValueOperand(value)) {
194 context.visitPlace(id, operand);
195 }
196 }
compiler/forget/src/ReactiveScopes/PropagateScopeDependencies.ts
+7 -7
@@ -9,7 +9,6 @@ import {
9 Identifier,
10 InstructionId,
11 InstructionKind,
12 - InstructionValue,
12 isPrimitiveType,
13 LValue,
14 makeInstructionId,
@@ -19,10 +18,11 @@ import {
18 ReactiveInstruction,
19 ReactiveScope,
20 ReactiveScopeDependency,
21 + ReactiveValue,
22 ReactiveValueBlock,
23 } from "../HIR/HIR";
24 -import { eachInstructionValueOperand } from "../HIR/visitors";
24 import { assertExhaustive } from "../Utils/utils";
25 +import { eachReactiveValueOperand } from "./visitors";
26
27 /**
28 * Infers the dependencies of each scope to include variables whose values
@@ -266,19 +266,19 @@ function visitValueBlock(context: Context, block: ReactiveValueBlock): void {
266 }
267 }
268 if (block.last !== null) {
269 - visitInstructionValue(context, block.last.value, null);
269 + visitReactiveValue(context, block.last.value, null);
270 }
271 }
272
273 -function visitInstructionValue(
273 +function visitReactiveValue(
274 context: Context,
275 - value: InstructionValue,
275 + value: ReactiveValue,
276 lvalue: LValue | null
277 ): void {
278 if (value.kind === "PropertyLoad" && lvalue !== null) {
279 context.declareProperty(lvalue.place, value.object, value.property);
280 } else {
281 - for (const operand of eachInstructionValueOperand(value)) {
281 + for (const operand of eachReactiveValueOperand(value)) {
282 context.visitOperand(operand);
283 }
284 }
@@ -286,7 +286,7 @@ function visitInstructionValue(
286
287 function visitInstruction(context: Context, instr: ReactiveInstruction): void {
288 const { lvalue } = instr;
289 - visitInstructionValue(context, instr.value, lvalue);
289 + visitReactiveValue(context, instr.value, lvalue);
290 if (lvalue !== null && lvalue.kind !== InstructionKind.Reassign) {
291 const range = lvalue.place.identifier.mutableRange;
292 // TODO: only assign Const if the value is never reassigned
compiler/forget/src/ReactiveScopes/RenameVariables.ts
+3 -2
@@ -16,6 +16,7 @@ import {
16 } from "../HIR/HIR";
17 import { eachInstructionValueOperand } from "../HIR/visitors";
18 import { assertExhaustive } from "../Utils/utils";
19 +import { eachReactiveValueOperand } from "./visitors";
20
21 /**
22 * Ensures that each named variable in the given function has a unique name
@@ -46,7 +47,7 @@ function visitBlockInner(scopes: Scopes, block: ReactiveBlock): void {
47 for (const stmt of block) {
48 switch (stmt.kind) {
49 case "instruction": {
49 - for (const operand of eachInstructionValueOperand(
50 + for (const operand of eachReactiveValueOperand(
51 stmt.instruction.value
52 )) {
53 scopes.visit(operand.identifier);
@@ -76,7 +77,7 @@ function visitValueBlock(scopes: Scopes, block: ReactiveValueBlock): void {
77 stmt.kind === "instruction",
78 "Value blocks may only contain instructions"
79 );
79 - for (const operand of eachInstructionValueOperand(stmt.instruction.value)) {
80 + for (const operand of eachReactiveValueOperand(stmt.instruction.value)) {
81 scopes.visit(operand.identifier);
82 }
83 if (stmt.instruction.lvalue !== null) {
compiler/forget/src/ReactiveScopes/visitors.ts
+17 -1
@@ -13,6 +13,7 @@ import {
13 ReactiveInstruction,
14 ReactiveScope,
15 ReactiveTerminal,
16 + ReactiveValue,
17 ReactiveValueBlock,
18 } from "../HIR/HIR";
19 import { eachInstructionValueOperand } from "../HIR/visitors";
@@ -33,7 +34,7 @@ export function visitFunction(
34 switch (item.kind) {
35 case "instruction": {
36 if (visitValue) {
36 - for (const operand of eachInstructionValueOperand(
37 + for (const operand of eachReactiveValueOperand(
38 item.instruction.value
39 )) {
40 visitValue(operand);
@@ -81,6 +82,21 @@ export function visitFunction(
82 visitBlock(fn.body);
83 }
84
85 +export function* eachReactiveValueOperand(
86 + instrValue: ReactiveValue
87 +): Iterable<Place> {
88 + switch (instrValue.kind) {
89 + case "LogicalExpression": {
90 + yield* eachReactiveValueOperand(instrValue.left);
91 + yield* eachReactiveValueOperand(instrValue.right);
92 + break;
93 + }
94 + default: {
95 + yield* eachInstructionValueOperand(instrValue);
96 + }
97 + }
98 +}
99 +
100 export function mapTerminalBlocks(
101 terminal: ReactiveTerminal,
102 fn: (block: ReactiveBlock) => ReactiveBlock