[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