diff --git a/compiler/forget/src/HIR/BuildHIR.ts b/compiler/forget/src/HIR/BuildHIR.ts index b7a982b8d9..506862f332 100644 --- a/compiler/forget/src/HIR/BuildHIR.ts +++ b/compiler/forget/src/HIR/BuildHIR.ts @@ -934,6 +934,7 @@ function lowerExpression( id: makeInstructionId(0), test: testBlock.id, operator: expr.node.operator, + loc: exprLoc, }, testBlock ); diff --git a/compiler/forget/src/HIR/HIR.ts b/compiler/forget/src/HIR/HIR.ts index 633147ca38..67e96323c5 100644 --- a/compiler/forget/src/HIR/HIR.ts +++ b/compiler/forget/src/HIR/HIR.ts @@ -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; }; /** diff --git a/compiler/forget/src/HIR/PrintHIR.ts b/compiler/forget/src/HIR/PrintHIR.ts index 436d51a767..11db411664 100644 --- a/compiler/forget/src/HIR/PrintHIR.ts +++ b/compiler/forget/src/HIR/PrintHIR.ts @@ -22,6 +22,7 @@ import { Place, ReactiveInstruction, ReactiveScope, + ReactiveValue, SourceLocation, Terminal, Type, @@ -201,7 +202,7 @@ export function printTerminal(terminal: Terminal): Array | 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, diff --git a/compiler/forget/src/HIR/visitors.ts b/compiler/forget/src/HIR/visitors.ts index be7300e2a2..aa690410da 100644 --- a/compiler/forget/src/HIR/visitors.ts +++ b/compiler/forget/src/HIR/visitors.ts @@ -291,6 +291,7 @@ export function mapTerminalSuccessors( fallthrough, operator: terminal.operator, id: makeInstructionId(0), + loc: terminal.loc, }; } case "return": { diff --git a/compiler/forget/src/ReactiveScopes/BuildReactiveBlocks.ts b/compiler/forget/src/ReactiveScopes/BuildReactiveBlocks.ts index 58c0f024a7..9a99675aa6 100644 --- a/compiler/forget/src/ReactiveScopes/BuildReactiveBlocks.ts +++ b/compiler/forget/src/ReactiveScopes/BuildReactiveBlocks.ts @@ -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; diff --git a/compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts b/compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts index a3b173d2d0..8eb59e19be 100644 --- a/compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts +++ b/compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts @@ -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; @@ -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, diff --git a/compiler/forget/src/ReactiveScopes/MergeOverlappingReactiveScopes.ts b/compiler/forget/src/ReactiveScopes/MergeOverlappingReactiveScopes.ts index ce62779834..9ba757e295 100644 --- a/compiler/forget/src/ReactiveScopes/MergeOverlappingReactiveScopes.ts +++ b/compiler/forget/src/ReactiveScopes/MergeOverlappingReactiveScopes.ts @@ -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); } } diff --git a/compiler/forget/src/ReactiveScopes/PropagateScopeDependencies.ts b/compiler/forget/src/ReactiveScopes/PropagateScopeDependencies.ts index 7c7de061f1..6c9400fbe1 100644 --- a/compiler/forget/src/ReactiveScopes/PropagateScopeDependencies.ts +++ b/compiler/forget/src/ReactiveScopes/PropagateScopeDependencies.ts @@ -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 diff --git a/compiler/forget/src/ReactiveScopes/RenameVariables.ts b/compiler/forget/src/ReactiveScopes/RenameVariables.ts index 1049f850f8..9f66ec35c6 100644 --- a/compiler/forget/src/ReactiveScopes/RenameVariables.ts +++ b/compiler/forget/src/ReactiveScopes/RenameVariables.ts @@ -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) { diff --git a/compiler/forget/src/ReactiveScopes/visitors.ts b/compiler/forget/src/ReactiveScopes/visitors.ts index 6b7f172b33..0f275d3417 100644 --- a/compiler/forget/src/ReactiveScopes/visitors.ts +++ b/compiler/forget/src/ReactiveScopes/visitors.ts @@ -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 { + 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