Skip to content

Commit b8d78a9

Browse files
committed
Improve "value block" handling
This is a pre-req to construct reactive scopes in #857. "Value blocks" such as `for` init/test/update and `while` test need to be consistently wrapped in enter/leave calls so that we can extend the range of values properly. We also need to handle `for` init blocks a bit differently, since they allow variable declarations but not other types of statements. This PR ensures that we use consistent methods for handling value blocks (`for` test/update and `while` test) and treats `for` init as a new type with its own enter/append/leave visitor functions.
1 parent c8bb9ea commit b8d78a9

File tree

6 files changed

+319
-151
lines changed

6 files changed

+319
-151
lines changed

compiler/forget/src/HIR/Codegen.ts

Lines changed: 95 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,14 @@ type Temporaries = Map<IdentifierId, t.Expression>;
9191

9292
class CodegenVisitor
9393
implements
94-
Visitor<Array<t.Statement>, t.Expression, t.Statement, t.SwitchCase>
94+
Visitor<
95+
Array<t.Statement>,
96+
Array<t.Statement>,
97+
Array<t.Statement>,
98+
t.Expression,
99+
t.Statement,
100+
t.SwitchCase
101+
>
95102
{
96103
depth: number = 0;
97104
temp: Map<IdentifierId, t.Expression> = new Map();
@@ -100,10 +107,77 @@ class CodegenVisitor
100107
this.depth++;
101108
return [];
102109
}
110+
appendBlock(
111+
block: t.Statement[],
112+
item: t.Statement,
113+
blockId?: BlockId | undefined
114+
): void {
115+
if (item.type === "EmptyStatement") {
116+
return;
117+
}
118+
if (blockId !== undefined) {
119+
block.push(
120+
createLabelledStatement(
121+
item.loc,
122+
t.identifier(codegenLabel(blockId)),
123+
item
124+
)
125+
);
126+
} else {
127+
block.push(item);
128+
}
129+
}
130+
leaveBlock(block: t.Statement[]): t.Statement {
131+
this.depth--;
132+
return t.blockStatement(block);
133+
}
103134
enterValueBlock(): t.Statement[] {
104-
this.depth++;
105-
return [];
135+
return this.enterBlock();
136+
}
137+
appendValueBlock(block: t.Statement[], item: t.Statement): void {
138+
this.appendBlock(block, item);
139+
}
140+
leaveValueBlock(block: t.Statement[], place: t.Expression): t.Expression {
141+
this.depth--;
142+
if (block.length === 0) {
143+
return place;
144+
}
145+
const expressions = block.map((stmt) => {
146+
switch (stmt.type) {
147+
case "ExpressionStatement":
148+
return stmt.expression;
149+
default:
150+
todoInvariant(
151+
false,
152+
`Handle conversion of ${stmt.type} to expression`
153+
);
154+
}
155+
});
156+
expressions.push(place);
157+
return t.sequenceExpression(expressions);
158+
}
159+
160+
enterInitBlock(block: t.Statement[]): t.Statement[] {
161+
return this.enterBlock();
162+
}
163+
164+
appendInitBlock(block: t.Statement[], item: t.Statement): void {
165+
this.appendBlock(block, item);
166+
}
167+
leaveInitBlock(block: t.Statement[]): t.Statement[] {
168+
switch (block.length) {
169+
case 0: {
170+
return [t.emptyStatement()];
171+
}
172+
case 1: {
173+
return [block[0]];
174+
}
175+
default: {
176+
return [t.blockStatement(block)];
177+
}
178+
}
106179
}
180+
107181
visitValue(value: InstructionValue): t.Expression {
108182
return codegenInstructionValue(this.temp, value);
109183
}
@@ -191,8 +265,25 @@ class CodegenVisitor
191265
return createWhileStatement(terminal.loc, terminal.test, terminal.loop);
192266
}
193267
case "for": {
268+
const initBlock = terminal.init;
269+
invariant(
270+
initBlock.length === 1,
271+
"Expected for init to be a single expression or statement"
272+
);
273+
const initStatement = initBlock[0]!;
274+
let init;
275+
if (initStatement.type === "VariableDeclaration") {
276+
init = initStatement;
277+
} else if (initStatement.type === "ExpressionStatement") {
278+
init = initStatement.expression;
279+
} else {
280+
invariant(
281+
false,
282+
`Expected 'for' init block to contain variable declaration or an expression, got '${initStatement.type}'.`
283+
);
284+
}
194285
return t.forStatement(
195-
terminal.init as any, // TODO: make sure it's a variable declaration
286+
init,
196287
terminal.test,
197288
terminal.update,
198289
terminal.loop
@@ -225,49 +316,6 @@ class CodegenVisitor
225316
visitCase(test: t.Expression | null, block: t.Statement): t.SwitchCase {
226317
return t.switchCase(test, [block]);
227318
}
228-
appendBlock(
229-
block: t.Statement[],
230-
item: t.Statement,
231-
blockId?: BlockId | undefined
232-
): void {
233-
if (item.type === "EmptyStatement") {
234-
return;
235-
}
236-
if (blockId !== undefined) {
237-
block.push(
238-
createLabelledStatement(
239-
item.loc,
240-
t.identifier(codegenLabel(blockId)),
241-
item
242-
)
243-
);
244-
} else {
245-
block.push(item);
246-
}
247-
}
248-
leaveBlock(block: t.Statement[]): t.Statement {
249-
this.depth--;
250-
return t.blockStatement(block);
251-
}
252-
leaveValueBlock(block: t.Statement[], place: t.Expression): t.Expression {
253-
this.depth--;
254-
if (block.length === 0) {
255-
return place;
256-
}
257-
const expressions = block.map((stmt) => {
258-
switch (stmt.type) {
259-
case "ExpressionStatement":
260-
return stmt.expression;
261-
default:
262-
todoInvariant(
263-
false,
264-
`Handle conversion of ${stmt.type} to expression`
265-
);
266-
}
267-
});
268-
expressions.push(place);
269-
return t.sequenceExpression(expressions);
270-
}
271319
}
272320

273321
function codegenLabel(id: BlockId): string {

0 commit comments

Comments
 (0)