[valueblocks] While.test is a proper value block
Changes ReactiveWhileTerminal’s test to use the new value block representation. This means logical and condition expressions will work as while test values now.
Joe Savona committed
Jan 31, 2023 at 13:39 UTC
90e1265442fdce86c1de517c2bd8c53fa585e814
8 files changed
+90
-25
compiler/forget/src/HIR/HIR.ts
+1
-1
@@ -165,7 +165,7 @@ export type ReactiveSwitchTerminal = {
165
};
166
export type ReactiveWhileTerminal = {
167
kind: "while";
168
- test: ReactiveValueBlock;
168
+ test: ReactiveValue;
169
loop: ReactiveBlock;
170
id: InstructionId;
171
};
compiler/forget/src/ReactiveScopes/BuildReactiveFunction.ts
+5
-10
@@ -253,16 +253,11 @@ class Driver {
253
);
254
scheduleIds.push(scheduleId);
255
256
- const testBlock = this.cx.ir.blocks.get(terminal.test)!;
257
- const testTerminal = testBlock.terminal;
258
- invariant(
259
- testTerminal.kind === "branch",
260
- "Expected while loop test block to end in an if"
261
- );
262
- const testValue = this.visitValueBlock(blockValue, testBlock, {
263
- value: testTerminal.test,
264
- id: testTerminal.id,
265
- });
256
+ const testValue = this.visitValueBlockNew(
257
+ terminal.test,
258
+ terminal.loc
259
+ ).value;
260
+
261
let loopBody: ReactiveBlock;
262
if (loopId) {
263
loopBody = this.traverseBlock(this.cx.ir.blocks.get(loopId)!);
compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts
+1
-1
@@ -299,7 +299,7 @@ function codegenTerminal(
299
return t.throwStatement(codegenPlace(cx, terminal.value));
300
}
301
case "while": {
302
- const test = codegenValueBlock(cx, terminal.test);
302
+ const test = codegenInstructionValue(cx, terminal.test);
303
return t.whileStatement(test, codegenBlock(cx, terminal.loop));
304
}
305
default: {
compiler/forget/src/ReactiveScopes/PrintReactiveFunction.ts
+1
-1
@@ -192,7 +192,7 @@ function printTerminal(writer: Writer, terminal: ReactiveTerminal): void {
192
}
193
case "while": {
194
writer.writeLine(`[${terminal.id}] while (`);
195
- printValueBlock(writer, terminal.test);
195
+ printReactiveValue(writer, terminal.test);
196
writer.writeLine(") {");
197
printReactiveInstructions(writer, terminal.loop);
198
writer.writeLine("}");
compiler/forget/src/ReactiveScopes/PropagateScopeDependencies.ts
+53
-6
@@ -21,6 +21,7 @@ import {
21
ReactiveValue,
22
ReactiveValueBlock,
23
} from "../HIR/HIR";
24
+import { eachInstructionValueOperand } from "../HIR/visitors";
25
import { assertExhaustive } from "../Utils/utils";
26
import { eachReactiveValueOperand } from "./visitors";
27
@@ -101,6 +102,20 @@ class Context {
102
this.visitDependency({ place, path: null });
103
}
104
105
+ visitProperty(object: Place, property: string): void {
106
+ const objectDependency = this.#properties.get(object.identifier);
107
+ let nextDependency: ReactiveScopeDependency;
108
+ if (objectDependency === undefined) {
109
+ nextDependency = { place: object, path: [property] };
110
+ } else {
111
+ nextDependency = {
112
+ place: objectDependency.place,
113
+ path: [...(objectDependency.path ?? []), property],
114
+ };
115
+ }
116
+ this.visitDependency(nextDependency);
117
+ }
118
+
119
visitDependency(dependency: ReactiveScopeDependency): void {
120
let maybeDependency: ReactiveScopeDependency;
121
if (dependency.path !== null) {
@@ -222,7 +237,7 @@ function visit(context: Context, block: ReactiveBlock): void {
237
break;
238
}
239
case "while": {
225
- visitValueBlock(context, terminal.test);
240
+ visitReactiveValue(context, terminal.test);
241
visit(context, terminal.loop);
242
break;
243
}
@@ -266,17 +281,49 @@ function visitValueBlock(context: Context, block: ReactiveValueBlock): void {
281
}
282
}
283
if (block.last !== null) {
269
- visitReactiveValue(context, block.last.value, null);
284
+ visitInstructionValue(context, block.last.value, null);
285
+ }
286
+}
287
+
288
+function visitReactiveValue(context: Context, value: ReactiveValue): void {
289
+ switch (value.kind) {
290
+ case "LogicalExpression": {
291
+ visitReactiveValue(context, value.left);
292
+ visitReactiveValue(context, value.right);
293
+ break;
294
+ }
295
+ case "ConditionalExpression": {
296
+ visitReactiveValue(context, value.test);
297
+ visitReactiveValue(context, value.consequent);
298
+ visitReactiveValue(context, value.alternate);
299
+ break;
300
+ }
301
+ case "SequenceExpression": {
302
+ for (const instr of value.instructions) {
303
+ visitInstruction(context, instr);
304
+ }
305
+ visitInstructionValue(context, value.value, null);
306
+ break;
307
+ }
308
+ default: {
309
+ for (const operand of eachInstructionValueOperand(value)) {
310
+ context.visitOperand(operand);
311
+ }
312
+ }
313
}
314
}
315
273
-function visitReactiveValue(
316
+function visitInstructionValue(
317
context: Context,
318
value: ReactiveValue,
319
lvalue: LValue | null
320
): void {
278
- if (value.kind === "PropertyLoad" && lvalue !== null) {
279
- context.declareProperty(lvalue.place, value.object, value.property);
321
+ if (value.kind === "PropertyLoad") {
322
+ if (lvalue !== null) {
323
+ context.declareProperty(lvalue.place, value.object, value.property);
324
+ } else {
325
+ context.visitProperty(value.object, value.property);
326
+ }
327
} else {
328
for (const operand of eachReactiveValueOperand(value)) {
329
context.visitOperand(operand);
@@ -286,7 +333,7 @@ function visitReactiveValue(
333
334
function visitInstruction(context: Context, instr: ReactiveInstruction): void {
335
const { lvalue } = instr;
289
- visitReactiveValue(context, instr.value, lvalue);
336
+ visitInstructionValue(context, instr.value, lvalue);
337
if (lvalue !== null && lvalue.kind !== InstructionKind.Reassign) {
338
const range = lvalue.place.identifier.mutableRange;
339
// TODO: only assign Const if the value is never reassigned
compiler/forget/src/ReactiveScopes/visitors.ts
+27
-4
@@ -40,8 +40,30 @@ export class ReactiveFunctionVisitor<TState = void> {
40
this.traverseValue(id, value, state);
41
}
42
traverseValue(id: InstructionId, value: ReactiveValue, state: TState): void {
43
- for (const place of eachReactiveValueOperand(value)) {
44
- this.visitPlace(id, place, state);
43
+ switch (value.kind) {
44
+ case "LogicalExpression": {
45
+ this.visitValue(id, value.left, state);
46
+ this.visitValue(id, value.right, state);
47
+ break;
48
+ }
49
+ case "ConditionalExpression": {
50
+ this.visitValue(id, value.test, state);
51
+ this.visitValue(id, value.consequent, state);
52
+ this.visitValue(id, value.alternate, state);
53
+ break;
54
+ }
55
+ case "SequenceExpression": {
56
+ for (const instr of value.instructions) {
57
+ this.visitInstruction(instr, state);
58
+ }
59
+ this.visitValue(id, value.value, state);
60
+ break;
61
+ }
62
+ default: {
63
+ for (const place of eachReactiveValueOperand(value)) {
64
+ this.visitPlace(id, place, state);
65
+ }
66
+ }
67
}
68
}
69
@@ -87,7 +109,7 @@ export class ReactiveFunctionVisitor<TState = void> {
109
break;
110
}
111
case "while": {
90
- this.visitValueBlock(terminal.test, state);
112
+ this.visitValue(terminal.id, terminal.test, state);
113
this.visitBlock(terminal.loop, state);
114
break;
115
}
@@ -318,7 +340,8 @@ export function eachTerminalBlock(
340
break;
341
}
342
case "while": {
321
- visitValueBlock(terminal.test);
343
+ // TODO
344
+ // visitValueBlock(terminal.test);
345
visitBlock(terminal.loop);
346
break;
347
}
compiler/forget/src/__tests__/fixtures/hir/error.while-with-assignment-in-test.expect.md
+1
-1
@@ -19,7 +19,7 @@ function f(reader) {
19
## Error
20
21
```
22
-TODO: Handle conversion of VariableDeclaration to expression
22
+TODO: Cannot declare variables in a value block, tried to declare 'value$0'
23
```
24
25
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/ternary-expression.expect.md
+1
-1
@@ -39,7 +39,7 @@ function ternary(props) {
39
const c_5 = $[5] !== b;
40
let t6;
41
if (c_4 || c_5) {
42
- t6 = a ? b : null;
42
+ t6 = a ? b : (null, null);
43
$[4] = a;
44
$[5] = b;
45
$[6] = t6;