@samitouri / QOS-React / commits / 90e1265442

[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;