[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.
This commit is contained in:
Joe Savona
2023-01-31 13:39:31 -08:00
parent c6a066af83
commit bb8ae86c24
10 changed files with 75 additions and 22 deletions
+1
View File
@@ -934,6 +934,7 @@ function lowerExpression(
id: makeInstructionId(0),
test: testBlock.id,
operator: expr.node.operator,
loc: exprLoc,
},
testBlock
);
+12 -1
View File
@@ -89,7 +89,17 @@ export type ReactiveTerminalStatement<
export type ReactiveInstruction = {
id: InstructionId;
lvalue: LValue | null;
value: InstructionValue;
value: ReactiveValue;
loc: SourceLocation;
};
export type ReactiveValue = InstructionValue | ReactiveLogicalValue;
export type ReactiveLogicalValue = {
kind: "LogicalExpression";
operator: t.LogicalExpression["operator"];
left: ReactiveValue;
right: ReactiveValue;
loc: SourceLocation;
};
@@ -298,6 +308,7 @@ export type LogicalTerminal = {
test: BlockId;
fallthrough: BlockId;
id: InstructionId;
loc: SourceLocation;
};
/**
+8 -1
View File
@@ -22,6 +22,7 @@ import {
Place,
ReactiveInstruction,
ReactiveScope,
ReactiveValue,
SourceLocation,
Terminal,
Type,
@@ -201,7 +202,7 @@ export function printTerminal(terminal: Terminal): Array<string> | string {
return value;
}
export function printInstructionValue(instrValue: InstructionValue): string {
export function printInstructionValue(instrValue: ReactiveValue): string {
let value = "";
switch (instrValue.kind) {
case "ArrayExpression": {
@@ -332,6 +333,12 @@ export function printInstructionValue(instrValue: InstructionValue): string {
value = `${instrValue.tag}\`${instrValue.value.raw}\``;
break;
}
case "LogicalExpression": {
value = `Logical ${printInstructionValue(instrValue.left)} ${
instrValue.operator
} ${printInstructionValue(instrValue.right)}`;
break;
}
default: {
assertExhaustive(
instrValue,
+1
View File
@@ -291,6 +291,7 @@ export function mapTerminalSuccessors(
fallthrough,
operator: terminal.operator,
id: makeInstructionId(0),
loc: terminal.loc,
};
}
case "return": {
@@ -19,9 +19,12 @@ import {
ReactiveValueBlock,
ScopeId,
} from "../HIR";
import { eachInstructionValueOperand } from "../HIR/visitors";
import { assertExhaustive } from "../Utils/utils";
import { eachTerminalBlock, mapTerminalBlocks } from "./visitors";
import {
eachReactiveValueOperand,
eachTerminalBlock,
mapTerminalBlocks,
} from "./visitors";
/**
* Note: this is the 4th of 4 passes that determine how to break a function into discrete
@@ -222,7 +225,7 @@ export function getInstructionScope({
if (lvalueScope !== null) {
return lvalueScope;
}
for (const operand of eachInstructionValueOperand(value)) {
for (const operand of eachReactiveValueOperand(value)) {
const operandScope = getPlaceScope(id, operand);
if (operandScope !== null) {
return operandScope;
@@ -13,7 +13,6 @@ import {
Identifier,
IdentifierId,
InstructionKind,
InstructionValue,
LValue,
Place,
ReactiveBlock,
@@ -22,6 +21,7 @@ import {
ReactiveScope,
ReactiveScopeDependency,
ReactiveTerminal,
ReactiveValue,
ReactiveValueBlock,
SourceLocation,
} from "../HIR/HIR";
@@ -430,6 +430,7 @@ const createLabelledStatement = withLoc(t.labeledStatement);
const createVariableDeclaration = withLoc(t.variableDeclaration);
const createWhileStatement = withLoc(t.whileStatement);
const createTaggedTemplateExpression = withLoc(t.taggedTemplateExpression);
const createLogicalExpression = withLoc(t.logicalExpression);
type Temporaries = Map<IdentifierId, t.Expression>;
@@ -482,7 +483,7 @@ function codegenInstruction(
function codegenInstructionValue(
temp: Temporaries,
instrValue: InstructionValue
instrValue: ReactiveValue
): t.Expression {
let value: t.Expression;
switch (instrValue.kind) {
@@ -685,6 +686,15 @@ function codegenInstructionValue(
);
break;
}
case "LogicalExpression": {
value = createLogicalExpression(
instrValue.loc,
instrValue.operator,
codegenInstructionValue(temp, instrValue.left),
codegenInstructionValue(temp, instrValue.right)
);
break;
}
default: {
assertExhaustive(
instrValue,
@@ -8,21 +8,24 @@
import invariant from "invariant";
import {
InstructionId,
InstructionValue,
makeInstructionId,
Place,
ReactiveBlock,
ReactiveFunction,
ReactiveInstruction,
ReactiveScope,
ReactiveValue,
ReactiveValueBlock,
ScopeId,
} from "../HIR";
import { eachInstructionValueOperand } from "../HIR/visitors";
import DisjointSet from "../Utils/DisjointSet";
import { retainWhere } from "../Utils/utils";
import { getPlaceScope } from "./BuildReactiveBlocks";
import { eachTerminalBlock, eachTerminalOperand } from "./visitors";
import {
eachReactiveValueOperand,
eachTerminalBlock,
eachTerminalOperand,
} from "./visitors";
/**
* Note: this is the 3rd of 4 passes that determine how to break a function into discrete
@@ -184,10 +187,10 @@ function visitInstruction(
function visitValue(
context: Context,
id: InstructionId,
value: InstructionValue
value: ReactiveValue
): void {
context.visitId(id);
for (const operand of eachInstructionValueOperand(value)) {
for (const operand of eachReactiveValueOperand(value)) {
context.visitPlace(id, operand);
}
}
@@ -9,7 +9,6 @@ import {
Identifier,
InstructionId,
InstructionKind,
InstructionValue,
isPrimitiveType,
LValue,
makeInstructionId,
@@ -19,10 +18,11 @@ import {
ReactiveInstruction,
ReactiveScope,
ReactiveScopeDependency,
ReactiveValue,
ReactiveValueBlock,
} from "../HIR/HIR";
import { eachInstructionValueOperand } from "../HIR/visitors";
import { assertExhaustive } from "../Utils/utils";
import { eachReactiveValueOperand } from "./visitors";
/**
* Infers the dependencies of each scope to include variables whose values
@@ -266,19 +266,19 @@ function visitValueBlock(context: Context, block: ReactiveValueBlock): void {
}
}
if (block.last !== null) {
visitInstructionValue(context, block.last.value, null);
visitReactiveValue(context, block.last.value, null);
}
}
function visitInstructionValue(
function visitReactiveValue(
context: Context,
value: InstructionValue,
value: ReactiveValue,
lvalue: LValue | null
): void {
if (value.kind === "PropertyLoad" && lvalue !== null) {
context.declareProperty(lvalue.place, value.object, value.property);
} else {
for (const operand of eachInstructionValueOperand(value)) {
for (const operand of eachReactiveValueOperand(value)) {
context.visitOperand(operand);
}
}
@@ -286,7 +286,7 @@ function visitInstructionValue(
function visitInstruction(context: Context, instr: ReactiveInstruction): void {
const { lvalue } = instr;
visitInstructionValue(context, instr.value, lvalue);
visitReactiveValue(context, instr.value, lvalue);
if (lvalue !== null && lvalue.kind !== InstructionKind.Reassign) {
const range = lvalue.place.identifier.mutableRange;
// TODO: only assign Const if the value is never reassigned
@@ -16,6 +16,7 @@ import {
} from "../HIR/HIR";
import { eachInstructionValueOperand } from "../HIR/visitors";
import { assertExhaustive } from "../Utils/utils";
import { eachReactiveValueOperand } from "./visitors";
/**
* Ensures that each named variable in the given function has a unique name
@@ -46,7 +47,7 @@ function visitBlockInner(scopes: Scopes, block: ReactiveBlock): void {
for (const stmt of block) {
switch (stmt.kind) {
case "instruction": {
for (const operand of eachInstructionValueOperand(
for (const operand of eachReactiveValueOperand(
stmt.instruction.value
)) {
scopes.visit(operand.identifier);
@@ -76,7 +77,7 @@ function visitValueBlock(scopes: Scopes, block: ReactiveValueBlock): void {
stmt.kind === "instruction",
"Value blocks may only contain instructions"
);
for (const operand of eachInstructionValueOperand(stmt.instruction.value)) {
for (const operand of eachReactiveValueOperand(stmt.instruction.value)) {
scopes.visit(operand.identifier);
}
if (stmt.instruction.lvalue !== null) {
+17 -1
View File
@@ -13,6 +13,7 @@ import {
ReactiveInstruction,
ReactiveScope,
ReactiveTerminal,
ReactiveValue,
ReactiveValueBlock,
} from "../HIR/HIR";
import { eachInstructionValueOperand } from "../HIR/visitors";
@@ -33,7 +34,7 @@ export function visitFunction(
switch (item.kind) {
case "instruction": {
if (visitValue) {
for (const operand of eachInstructionValueOperand(
for (const operand of eachReactiveValueOperand(
item.instruction.value
)) {
visitValue(operand);
@@ -81,6 +82,21 @@ export function visitFunction(
visitBlock(fn.body);
}
export function* eachReactiveValueOperand(
instrValue: ReactiveValue
): Iterable<Place> {
switch (instrValue.kind) {
case "LogicalExpression": {
yield* eachReactiveValueOperand(instrValue.left);
yield* eachReactiveValueOperand(instrValue.right);
break;
}
default: {
yield* eachInstructionValueOperand(instrValue);
}
}
}
export function mapTerminalBlocks(
terminal: ReactiveTerminal,
fn: (block: ReactiveBlock) => ReactiveBlock