6
*/
7
8
import {
9
+ InstructionId,
10
InstructionValue,
11
+ LValue,
12
Place,
13
ReactiveBlock,
14
ReactiveFunction,
15
ReactiveInstruction,
16
ReactiveScope,
17
+ ReactiveScopeBlock,
18
ReactiveTerminal,
19
+ ReactiveTerminalStatement,
20
ReactiveValue,
21
ReactiveValueBlock,
22
} from "../HIR/HIR";
23
import { eachInstructionValueOperand } from "../HIR/visitors";
24
import { assertExhaustive } from "../Utils/utils";
25
26
+export function visitReactiveFunction<TState>(
27
+ fn: ReactiveFunction,
28
+ visitor: ReactiveFunctionVisitor<TState>,
29
+ state: TState
30
+): void {
31
+ visitor.visitBlock(fn.body, state);
32
+}
33
+
34
+export class ReactiveFunctionVisitor<TState = void> {
35
+ visitID(id: InstructionId, state: TState): void {}
36
+ visitLValue(id: InstructionId, lvalue: LValue, state: TState): void {}
37
+ visitPlace(id: InstructionId, place: Place, state: TState): void {}
38
+
39
+ visitValue(id: InstructionId, value: ReactiveValue, state: 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);
45
+ }
46
+ }
47
+
48
+ visitInstruction(instruction: ReactiveInstruction, state: TState): void {
49
+ this.traverseInstruction(instruction, state);
50
+ }
51
+ traverseInstruction(instruction: ReactiveInstruction, state: TState): void {
52
+ this.visitID(instruction.id, state);
53
+ if (instruction.lvalue !== null) {
54
+ this.visitLValue(instruction.id, instruction.lvalue, state);
55
+ }
56
+ this.visitValue(instruction.id, instruction.value, state);
57
+ }
58
+
59
+ visitTerminal(stmt: ReactiveTerminalStatement, state: TState): void {
60
+ this.traverseTerminal(stmt, state);
61
+ }
62
+ traverseTerminal(stmt: ReactiveTerminalStatement, state: TState): void {
63
+ const { terminal } = stmt;
64
+ if (terminal.id !== null) {
65
+ this.visitID(terminal.id, state);
66
+ }
67
+ switch (terminal.kind) {
68
+ case "break":
69
+ case "continue": {
70
+ break;
71
+ }
72
+ case "return": {
73
+ if (terminal.value !== null) {
74
+ this.visitValue(terminal.id, terminal.value, state);
75
+ }
76
+ break;
77
+ }
78
+ case "throw": {
79
+ this.visitValue(terminal.id, terminal.value, state);
80
+ break;
81
+ }
82
+ case "for": {
83
+ this.visitValueBlock(terminal.init, state);
84
+ this.visitValueBlock(terminal.test, state);
85
+ this.visitValueBlock(terminal.update, state);
86
+ this.visitBlock(terminal.loop, state);
87
+ break;
88
+ }
89
+ case "while": {
90
+ this.visitValueBlock(terminal.test, state);
91
+ this.visitBlock(terminal.loop, state);
92
+ break;
93
+ }
94
+ case "if": {
95
+ this.visitValue(terminal.id, terminal.test, state);
96
+ this.visitBlock(terminal.consequent, state);
97
+ if (terminal.alternate !== null) {
98
+ this.visitBlock(terminal.alternate, state);
99
+ }
100
+ break;
101
+ }
102
+ case "switch": {
103
+ this.visitValue(terminal.id, terminal.test, state);
104
+ for (const case_ of terminal.cases) {
105
+ if (case_.test !== null) {
106
+ this.visitPlace(terminal.id, case_.test, state);
107
+ }
108
+ if (case_.block !== undefined) {
109
+ this.visitBlock(case_.block, state);
110
+ }
111
+ }
112
+ break;
113
+ }
114
+ default: {
115
+ assertExhaustive(
116
+ terminal,
117
+ `Unexpected terminal kind '${(terminal as any).kind}'`
118
+ );
119
+ }
120
+ }
121
+ }
122
+
123
+ visitValueBlock(block: ReactiveValueBlock, state: TState): void {
124
+ // NOTE: intentionally bypass calling visitBlock
125
+ this.traverseBlock(block.instructions, state);
126
+ if (block.last !== null) {
127
+ this.visitPlace(block.last.id, block.last.value, state);
128
+ }
129
+ }
130
+
131
+ visitScope(scope: ReactiveScopeBlock, state: TState): void {
132
+ this.traverseScope(scope, state);
133
+ }
134
+ traverseScope(scope: ReactiveScopeBlock, state: TState): void {
135
+ this.visitBlock(scope.instructions, state);
136
+ }
137
+
138
+ visitBlock(block: ReactiveBlock, state: TState): void {
139
+ this.traverseBlock(block, state);
140
+ }
141
+ traverseBlock(block: ReactiveBlock, state: TState): void {
142
+ for (const instr of block) {
143
+ switch (instr.kind) {
144
+ case "instruction": {
145
+ this.visitInstruction(instr.instruction, state);
146
+ break;
147
+ }
148
+ case "scope": {
149
+ this.visitScope(instr, state);
150
+ break;
151
+ }
152
+ case "terminal": {
153
+ this.visitTerminal(instr, state);
154
+ break;
155
+ }
156
+ default: {
157
+ assertExhaustive(
158
+ instr,
159
+ `Unexpected instruction kind '${(instr as any).kind}'`
160
+ );
161
+ }
162
+ }
163
+ }
164
+ }
165
+}
166
+
167
export function visitFunction(
168
fn: ReactiveFunction,
169
visitors: {