Make CompilerPipeline a generator
Turns CompilerPipeline into two functions: * `run()` is a generator and yields values that are a disjoint union of either AST/HIR/ReactiveFunction along with a name for that step. The idea is to use this in the playground so that it always matches the exact steps for compilation. I'll update playground in a follow-up. * `compile()` is ast in, ast out, and uses `run()` under the hood.
Joe Savona committed
Jan 10, 2023 at 10:15 UTC
77512024334e0cb12836b441b7c853d165abaa95
4 files changed
+110
-54
compiler/forget/src/Babel/BabelPlugin.ts
+2
-4
@@ -10,8 +10,7 @@
10
import type * as BabelCore from "@babel/core";
11
import type { PluginObj } from "@babel/core";
12
import jsx from "@babel/plugin-syntax-jsx";
13
-import Pipeline from "../CompilerPipeline";
14
-import { invariant } from "../Utils/CompilerError";
13
+import { compile } from "../CompilerPipeline";
14
15
/**
16
* The React Forget Babel Plugin
@@ -25,8 +24,7 @@ export default function (babel: typeof BabelCore): PluginObj {
24
visitor: {
25
FunctionDeclaration: {
26
enter(fn, pass) {
28
- const { ast } = Pipeline(fn);
29
- invariant(ast !== null, "Expected ast to be present");
27
+ const ast = compile(fn);
28
fn.replaceWith(ast);
29
},
30
},
compiler/forget/src/CompilerPipeline.ts
+97
-42
@@ -30,77 +30,132 @@ import {
30
import { eliminateRedundantPhi, enterSSA, leaveSSA } from "./SSA";
31
import { inferTypes } from "./TypeInference";
32
import { logHIRFunction, logReactiveFunction } from "./Utils/logger";
33
+import { assertExhaustive } from "./Utils/utils";
34
34
-export type CompilerResult = {
35
- ast: t.Function;
36
- ir: HIRFunction;
37
- reactiveFunction: ReactiveFunction;
38
-};
35
+export type CompilerPipelineValue =
36
+ | { kind: "ast"; name: string; value: t.Function }
37
+ | { kind: "hir"; name: string; value: HIRFunction }
38
+ | { kind: "reactive"; name: string; value: ReactiveFunction };
39
40
-export default function (
40
+export function* run(
41
func: NodePath<t.FunctionDeclaration>
42
-): CompilerResult {
42
+): Iterator<CompilerPipelineValue, t.Function> {
43
const env = new Environment();
44
45
- const ir = lower(func, env);
46
- logHIRFunction("HIR", ir);
45
+ const hir = lower(func, env);
46
+ yield log({ kind: "hir", name: "HIR", value: hir });
47
48
- mergeConsecutiveBlocks(ir);
49
- logHIRFunction("mergeConsecutiveBlocks", ir);
48
+ mergeConsecutiveBlocks(hir);
49
+ yield log({ kind: "hir", name: "MergeConsecutiveBlocks", value: hir });
50
51
- enterSSA(ir, env);
52
- logHIRFunction("SSA", ir);
51
+ enterSSA(hir, env);
52
+ yield log({ kind: "hir", name: "SSA", value: hir });
53
54
- eliminateRedundantPhi(ir);
55
- logHIRFunction("eliminateRedundantPhi", ir);
54
+ eliminateRedundantPhi(hir);
55
+ yield log({ kind: "hir", name: "EliminateRedundantPhi", value: hir });
56
57
- constantPropagation(ir);
58
- logHIRFunction("constantPropagation", ir);
57
+ constantPropagation(hir);
58
+ yield log({ kind: "hir", name: "ConstantPropagation", value: hir });
59
60
- inferTypes(ir);
61
- logHIRFunction("inferTypes", ir);
60
+ inferTypes(hir);
61
+ yield log({ kind: "hir", name: "InferTypes", value: hir });
62
63
- inferReferenceEffects(ir);
64
- logHIRFunction("inferReferenceEffects", ir);
63
+ inferReferenceEffects(hir);
64
+ yield log({ kind: "hir", name: "InferReferenceEffects", value: hir });
65
66
- inferMutableRanges(ir);
67
- logHIRFunction("inferMutableRanges", ir);
66
+ inferMutableRanges(hir);
67
+ yield log({ kind: "hir", name: "InferMutableRanges", value: hir });
68
69
- leaveSSA(ir);
70
- logHIRFunction("leaveSSA", ir);
69
+ leaveSSA(hir);
70
+ yield log({ kind: "hir", name: "LeaveSSA", value: hir });
71
72
- inferReactiveScopeVariables(ir);
73
- logHIRFunction("inferReactiveScopeVariables", ir);
72
+ inferReactiveScopeVariables(hir);
73
+ yield log({ kind: "hir", name: "InferReactiveScopeVariables", value: hir });
74
75
- inferReactiveScopes(ir);
76
- logHIRFunction("inferReactiveScopes", ir);
75
+ inferReactiveScopes(hir);
76
+ yield log({ kind: "hir", name: "InferReactiveScopes", value: hir });
77
78
- const reactiveFunction = buildReactiveFunction(ir);
79
- logReactiveFunction("buildReactiveFunction", reactiveFunction);
78
+ const reactiveFunction = buildReactiveFunction(hir);
79
+ yield log({
80
+ kind: "reactive",
81
+ name: "BuildReactiveFunction",
82
+ value: reactiveFunction,
83
+ });
84
85
pruneUnusedLabels(reactiveFunction);
82
- logReactiveFunction("pruneUnusedLabels", reactiveFunction);
86
+ yield log({
87
+ kind: "reactive",
88
+ name: "PruneUnusedLabels",
89
+ value: reactiveFunction,
90
+ });
91
92
flattenReactiveLoops(reactiveFunction);
85
- logReactiveFunction("flattenReactiveLoops", reactiveFunction);
93
+ yield log({
94
+ kind: "reactive",
95
+ name: "FlattenReactiveLoops",
96
+ value: reactiveFunction,
97
+ });
98
99
propagateScopeDependencies(reactiveFunction);
88
- logReactiveFunction("propagateScopeDependencies", reactiveFunction);
100
+ yield log({
101
+ kind: "reactive",
102
+ name: "PropagateScopeDependencies",
103
+ value: reactiveFunction,
104
+ });
105
106
pruneUnusedScopes(reactiveFunction);
91
- logReactiveFunction("pruneUnusedScopes", reactiveFunction);
107
+ yield log({
108
+ kind: "reactive",
109
+ name: "PruneUnusedScopes",
110
+ value: reactiveFunction,
111
+ });
112
113
pruneUnusedLValues(reactiveFunction);
94
- logReactiveFunction("pruneUnusedLValues", reactiveFunction);
114
+ yield log({
115
+ kind: "reactive",
116
+ name: "PruneUnusedLValues",
117
+ value: reactiveFunction,
118
+ });
119
120
renameVariables(reactiveFunction);
97
- logReactiveFunction("renameVariables", reactiveFunction);
121
+ yield log({
122
+ kind: "reactive",
123
+ name: "RenameVariables",
124
+ value: reactiveFunction,
125
+ });
126
127
const ast = codegenReactiveFunction(reactiveFunction);
128
+ yield log({ kind: "ast", name: "Codegen", value: ast });
129
101
- return {
102
- ast,
103
- ir,
104
- reactiveFunction,
105
- };
130
+ return ast;
131
+}
132
+
133
+export function compile(func: NodePath<t.FunctionDeclaration>): t.Function {
134
+ let generator = run(func);
135
+ while (true) {
136
+ const next = generator.next();
137
+ if (next.done) {
138
+ return next.value;
139
+ }
140
+ }
141
+}
142
+
143
+function log(value: CompilerPipelineValue): CompilerPipelineValue {
144
+ switch (value.kind) {
145
+ case "ast": {
146
+ break;
147
+ }
148
+ case "hir": {
149
+ logHIRFunction(value.name, value.value);
150
+ break;
151
+ }
152
+ case "reactive": {
153
+ logReactiveFunction(value.name, value.value);
154
+ break;
155
+ }
156
+ default: {
157
+ assertExhaustive(value, "Unexpected compilation kind");
158
+ }
159
+ }
160
+ return value;
161
}
compiler/forget/src/__tests__/hir-test.ts
+2
-2
@@ -13,7 +13,7 @@ import traverse from "@babel/traverse";
13
import { wasmFolder } from "@hpcc-js/wasm";
14
import path from "path";
15
import prettier from "prettier";
16
-import run from "../CompilerPipeline";
16
+import { compile } from "../CompilerPipeline";
17
import { toggleLogging } from "../Utils/logger";
18
import generateTestsFromFixtures from "./test-utils/generateTestsFromFixtures";
19
@@ -121,7 +121,7 @@ function transform(text: string, file: string): Array<TestOutput> {
121
traverse(ast, {
122
FunctionDeclaration: {
123
enter(nodePath) {
124
- const { ast } = run(nodePath);
124
+ const ast = compile(nodePath);
125
126
const text = prettier.format(generate(ast).code.replace("\n\n", "\n"), {
127
semi: true,
compiler/forget/src/index.ts
+9
-6
@@ -14,12 +14,12 @@ declare global {
14
import { parse } from "@babel/parser";
15
import traverse, { NodePath } from "@babel/traverse";
16
import * as t from "@babel/types";
17
+import { compile, run } from "./CompilerPipeline";
18
import { lower } from "./HIR/BuildHIR";
19
import codegen from "./HIR/Codegen";
20
import { Environment } from "./HIR/HIRBuilder";
20
-import { inferMutableRanges, inferReferenceEffects } from "./Inference";
21
-import { inferTypes } from "./TypeInference";
21
import printHIR, { printFunction } from "./HIR/PrintHIR";
22
+import { inferMutableRanges, inferReferenceEffects } from "./Inference";
23
import { buildReactiveFunction } from "./ReactiveScopes/BuildReactiveFunction";
24
import { codegenReactiveFunction } from "./ReactiveScopes/CodegenReactiveFunction";
25
import { flattenReactiveLoops } from "./ReactiveScopes/FlattenReactiveLoops";
@@ -32,6 +32,7 @@ import { pruneUnusedScopes } from "./ReactiveScopes/PruneUnusedScopes";
32
import { eliminateRedundantPhi } from "./SSA/EliminateRedundantPhi";
33
import enterSSA from "./SSA/EnterSSA";
34
import { leaveSSA } from "./SSA/LeaveSSA";
35
+import { inferTypes } from "./TypeInference";
36
37
function parseFunctions(
38
source: string
@@ -55,27 +56,29 @@ function parseFunctions(
56
}
57
58
export const HIR = {
58
- codegenReactiveFunction,
59
- flattenReactiveLoops,
60
- propagateScopeDependencies,
59
buildReactiveFunction,
60
codegen,
61
+ codegenReactiveFunction,
62
+ compile,
63
eliminateRedundantPhi,
64
enterSSA,
65
Environment,
66
- inferTypes,
66
+ flattenReactiveLoops,
67
inferMutableRanges,
68
inferReactiveScopes,
69
inferReactiveScopeVariables,
70
inferReferenceEffects,
71
+ inferTypes,
72
leaveSSA,
73
lower,
74
parseFunctions,
75
printFunction,
76
printHIR,
77
printReactiveFunction,
78
+ propagateScopeDependencies,
79
pruneUnusedLabels,
80
pruneUnusedScopes,
81
+ run,
82
};
83
84
export default BabelPlugin;