@fraczak/k 6.3.3 → 6.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/DOCS/CODECS.md +258 -0
- package/DOCS/DICTIONARY.md +1172 -0
- package/DOCS/KIR_SPECIALIZATION_DESIGN.md +51 -46
- package/DOCS/KIR_V1.md +221 -0
- package/DOCS/KVM_EXECUTION_MODEL.md +56 -19
- package/DOCS/OBJECT_FILE_AND_PATTERN.md +2 -1
- package/DOCS/PROFILING_AND_TRACING.md +196 -0
- package/DOCS/REPL.md +61 -4
- package/DOCS/WEB_REPL_REQUIREMENTS.md +135 -0
- package/DOCS/dependency-graph.md +144 -0
- package/Examples/arithmetics.k +108 -82
- package/Examples/bnat.k +1 -1
- package/Examples/buda.k +44 -0
- package/Examples/ieee.k +300 -8671
- package/Examples/poly.k +67 -0
- package/README.md +68 -19
- package/Value.mjs +8 -2
- package/augmentation.mjs +9 -7
- package/backend-api.mjs +11 -2
- package/backends/arm64/README.md +104 -0
- package/backends/arm64/bin/k-arm64-compile.mjs +101 -0
- package/backends/arm64/bin/k-arm64-run.mjs +58 -0
- package/backends/arm64/bin/k-arm64.mjs +88 -0
- package/backends/arm64/scripts/run-tests.mjs +41 -0
- package/backends/arm64/src/arm64.mjs +229 -0
- package/backends/arm64/src/cli.mjs +155 -0
- package/backends/arm64/src/kvm2arm64.mjs +906 -0
- package/backends/arm64/src/runtime.c +728 -0
- package/backends/arm64/tests/integration.sh +112 -0
- package/backends/arm64/tests/test-arm64.mjs +240 -0
- package/backends/llvm/README.md +135 -0
- package/backends/llvm/bin/k-llvm-build.mjs +71 -0
- package/backends/llvm/bin/k-llvm-compile.mjs +68 -0
- package/backends/llvm/bin/k-llvm-jit.mjs +160 -0
- package/backends/llvm/bin/k-llvm-run.mjs +90 -0
- package/backends/llvm/runtime/krt.c +1508 -0
- package/backends/llvm/runtime/krt.h +109 -0
- package/backends/llvm/scripts/conformance.mjs +76 -0
- package/backends/llvm/scripts/run-tests.mjs +28 -0
- package/backends/llvm/src/executable.mjs +562 -0
- package/backends/llvm/src/kvm2llvm.mjs +998 -0
- package/backends/llvm/src/llvm.mjs +1092 -0
- package/backends/llvm/tests/composition-driver.c +22 -0
- package/backends/llvm/tests/identity-driver.c +27 -0
- package/backends/llvm/tests/integration.sh +153 -0
- package/backends/llvm/tests/perf-support.mjs +735 -0
- package/backends/llvm/tests/product-driver.c +23 -0
- package/backends/llvm/tests/projection-driver.c +19 -0
- package/backends/llvm/tests/relation-driver.c +29 -0
- package/backends/llvm/tests/test-llvm.mjs +240 -0
- package/backends/llvm/tests/union-driver.c +31 -0
- package/backends/llvm/tests/variant-driver.c +19 -0
- package/backends/llvm/tests/variant-projection-driver.c +21 -0
- package/backends/wasm/DOCS/USE-WEB-ASSEMBLY.md +233 -0
- package/backends/wasm/README.md +97 -0
- package/backends/wasm/TECHNICAL-DEBT.md +58 -0
- package/backends/wasm/bin/k-wasm-compile.mjs +50 -0
- package/backends/wasm/bin/k-wasm-run.mjs +140 -0
- package/backends/wasm/bin/k-wasm.mjs +49 -0
- package/backends/wasm/demo_ieee.mjs +319 -0
- package/backends/wasm/runtime.wat +475 -0
- package/backends/wasm/scripts/run-tests.mjs +41 -0
- package/backends/wasm/src/cli.mjs +133 -0
- package/backends/wasm/src/kvm2wasm.mjs +697 -0
- package/backends/wasm/src/wasm.mjs +1054 -0
- package/backends/wasm/tests/integration.sh +182 -0
- package/backends/wasm/tests/perf-support.mjs +336 -0
- package/backends/wasm/tests/test-wasm.mjs +625 -0
- package/browser/repl-browser.mjs +1584 -0
- package/browser/shims/assert.mjs +19 -0
- package/browser/shims/buffer.mjs +224 -0
- package/browser/shims/crypto.mjs +103 -0
- package/browser/shims/fs.mjs +159 -0
- package/browser/shims/os.mjs +17 -0
- package/browser/shims/path.mjs +54 -0
- package/browser/shims/process.mjs +44 -0
- package/browser/shims/readline.mjs +11 -0
- package/browser/shims/url.mjs +12 -0
- package/browser/vfs-data.mjs +19 -0
- package/codecs/README.md +3 -0
- package/codecs/runtime/show-value.mjs +12 -3
- package/conformance/helper-reuse/case.json +8 -0
- package/conformance/helper-reuse/expected.kv +1 -0
- package/conformance/helper-reuse/input.kv +1 -0
- package/conformance/helper-reuse/program.k +2 -0
- package/conformance/identity/case.json +8 -0
- package/conformance/identity/expected.kv +1 -0
- package/conformance/identity/input.kv +1 -0
- package/conformance/identity/program.k +1 -0
- package/conformance/product-projection/case.json +8 -0
- package/conformance/product-projection/expected.kv +1 -0
- package/conformance/product-projection/input.kv +1 -0
- package/conformance/product-projection/program.k +1 -0
- package/conformance/run.mjs +149 -0
- package/conformance/variant-construction/case.json +8 -0
- package/conformance/variant-construction/expected.kv +1 -0
- package/conformance/variant-construction/input.kv +1 -0
- package/conformance/variant-construction/program.k +1 -0
- package/harness/adapters/arm64.mjs +105 -0
- package/harness/adapters/kvm.mjs +144 -0
- package/harness/adapters/llvm.mjs +118 -0
- package/harness/adapters/native.mjs +77 -0
- package/harness/adapters/wasm.mjs +169 -0
- package/harness/backend.mjs +95 -0
- package/harness/cli.mjs +130 -0
- package/harness/format.mjs +95 -0
- package/harness/index.mjs +25 -0
- package/harness/persistent.mjs +121 -0
- package/harness/registry.mjs +44 -0
- package/harness/suite.mjs +257 -0
- package/harness/suites/poly.mjs +156 -0
- package/harness/trace.mjs +50 -0
- package/harness/util.mjs +92 -0
- package/intrinsics.mjs +41 -0
- package/kir.mjs +399 -0
- package/kvm.mjs +585 -81
- package/objects/README.md +37 -3
- package/objects/compile.mjs +15 -22
- package/objects/inspect.mjs +96 -0
- package/objects/validate.mjs +415 -0
- package/package.json +51 -19
- package/repl-codecs.mjs +105 -6
- package/repl.mjs +335 -49
- package/run.mjs +2 -7
- package/scripts/benchmark.mjs +406 -22
- package/scripts/build-repl-html.mjs +1784 -0
- package/scripts/perf-ieee.mjs +586 -0
- package/scripts/perf-int.mjs +531 -0
- package/scripts/perf-poly.mjs +945 -0
- package/scripts/render-benchmark.mjs +12 -8
- package/scripts/run-tests.mjs +6 -0
- package/tests/integration.sh +19 -2
- package/tests/test-arithmetics.mjs +54 -0
- package/tests/test-ieee-arithmetic.mjs +4 -0
- package/tests/test-kir.mjs +85 -0
- package/tests/test-kvm-polymorphic.mjs +278 -0
- package/tests/test-kvm.mjs +25 -6
- package/tests/test-poly.mjs +129 -0
- package/tests/test-repl-html.mjs +303 -0
- package/tests/test-repl.mjs +144 -1
- package/tests/test-validate-object.mjs +40 -0
- package/a.klib +0 -1
- package/a.kvm +0 -142676
- package/a.wasm +0 -0
- package/benchmark-results.html +0 -532
- package/benchmark-results.json +0 -480
- package/k-vscode/.vscode/launch.json +0 -14
- package/k-vscode/README.md +0 -75
- package/k-vscode/extension.js +0 -273
- package/k-vscode/language-configuration.json +0 -42
- package/k-vscode/package.json +0 -79
- package/k-vscode/snippets/k.json +0 -92
- package/k-vscode/syntaxes/k.tmLanguage.json +0 -599
- package/k-vscode/test/background-ranges.test.js +0 -50
- package/k-vscode/themes/k-contrast-color-theme.json +0 -104
- package/scripts/git-hooks/pre-commit +0 -10
package/kvm.mjs
CHANGED
|
@@ -1,21 +1,31 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import { argv, stdin, exit, stdout } from "node:process";
|
|
4
|
-
import { Value, composePattern, withPattern, isProduct, isVariant } from "./Value.mjs";
|
|
5
|
-
import { exportPatternGraph } from "./codecs/runtime/codec.mjs";
|
|
6
|
-
import { patternToPropertyList } from "./codecs/runtime/pattern-json.mjs";
|
|
4
|
+
import { Value, composePattern, withPattern, isProduct, isVariant, edgeSubpattern, isValue } from "./Value.mjs";
|
|
7
5
|
import {
|
|
8
6
|
constrainWithPattern,
|
|
9
|
-
intersectPatterns,
|
|
10
7
|
projectionPattern,
|
|
11
|
-
verify
|
|
12
|
-
run
|
|
8
|
+
verify
|
|
13
9
|
} from "./run.mjs";
|
|
14
|
-
import { annotate } from "./index.mjs";
|
|
15
10
|
import { decodeWire, encodeToWire } from "./codecs/runtime/prefix-codec.mjs";
|
|
16
|
-
import {
|
|
11
|
+
import { intersectPropertyListPatterns } from "./codecs/runtime/codec.mjs";
|
|
12
|
+
import { compileObjectBuffer, decodeObject, loadLibrary } from "./object.mjs";
|
|
17
13
|
import codes from "./codes.mjs";
|
|
18
14
|
import { isMainEntrypoint } from "./codecs/runtime/cli-entry.mjs";
|
|
15
|
+
import { isIntrinsic, unsupportedIntrinsic } from "./intrinsics.mjs";
|
|
16
|
+
import { objectToKIRP, retypeObjectRelationForBackend } from "./kir.mjs";
|
|
17
|
+
|
|
18
|
+
export const KVM_FORMAT = "k-vm";
|
|
19
|
+
export const KVM_VERSION = 1;
|
|
20
|
+
|
|
21
|
+
const KVM_SINGLETON_INPUT_KINDS = new Set(["closed-product", "closed-union", "type"]);
|
|
22
|
+
const KIR_PATTERN_KIND_TO_PROPERTY_LIST_KIND = Object.freeze({
|
|
23
|
+
any: "any",
|
|
24
|
+
"open-product": "open-product",
|
|
25
|
+
"closed-product": "closed-product",
|
|
26
|
+
"open-union": "open-union",
|
|
27
|
+
"closed-union": "closed-union"
|
|
28
|
+
});
|
|
19
29
|
|
|
20
30
|
function isFile(filePath) {
|
|
21
31
|
try {
|
|
@@ -53,9 +63,85 @@ function buildExportPreamble(exports, libraries) {
|
|
|
53
63
|
return lines.join("\n") + "\n";
|
|
54
64
|
}
|
|
55
65
|
|
|
66
|
+
function kirFindCode(kir) {
|
|
67
|
+
return (hash) => kir.codes?.[hash] || codes.find(hash);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function kirTypePatternKind(node, codesTable = {}) {
|
|
71
|
+
const code = codesTable[node.code];
|
|
72
|
+
if (code?.code === "product") return "closed-product";
|
|
73
|
+
if (code?.code === "union") return "closed-union";
|
|
74
|
+
throw new Error(`KIR type pattern ${node.code || "<missing>"} is missing a product/union code definition`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function kirPatternToPropertyList(patternGraph, codesTable, rootId) {
|
|
78
|
+
if (rootId == null) return null;
|
|
79
|
+
const nodes = patternGraph?.nodes || [];
|
|
80
|
+
const nodeById = new Map(nodes.map((node) => [node.id, node]));
|
|
81
|
+
const ordered = [];
|
|
82
|
+
const idToIndex = new Map();
|
|
83
|
+
|
|
84
|
+
function visit(nodeId) {
|
|
85
|
+
if (idToIndex.has(nodeId)) return;
|
|
86
|
+
const node = nodeById.get(nodeId);
|
|
87
|
+
if (!node) throw new Error(`KIR pattern graph references missing node ${nodeId}`);
|
|
88
|
+
|
|
89
|
+
idToIndex.set(nodeId, ordered.length);
|
|
90
|
+
ordered.push(node);
|
|
91
|
+
for (const edge of [...(node.edges || [])].sort((a, b) => a.label.localeCompare(b.label) || a.target - b.target)) {
|
|
92
|
+
visit(edge.target);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
visit(rootId);
|
|
97
|
+
|
|
98
|
+
return ordered.map((node) => {
|
|
99
|
+
const kind = node.kind === "type"
|
|
100
|
+
? kirTypePatternKind(node, codesTable)
|
|
101
|
+
: KIR_PATTERN_KIND_TO_PROPERTY_LIST_KIND[node.kind];
|
|
102
|
+
if (!kind) throw new Error(`Unsupported KIR pattern kind: ${node.kind}`);
|
|
103
|
+
const edges = [...(node.edges || [])]
|
|
104
|
+
.sort((a, b) => a.label.localeCompare(b.label) || a.target - b.target)
|
|
105
|
+
.map((edge) => [edge.label, idToIndex.get(edge.target)]);
|
|
106
|
+
return [kind, edges];
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function isKIRRelation(relDef) {
|
|
111
|
+
return relDef?.patternGraph && relDef?.body;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function objectRelationToKIRRelation(relDef, name, options = {}) {
|
|
115
|
+
const kir = objectToKIRP({
|
|
116
|
+
format: "k-object",
|
|
117
|
+
codes: options.codes || {},
|
|
118
|
+
rels: { [name]: relDef },
|
|
119
|
+
relAlias: {},
|
|
120
|
+
compileStats: {},
|
|
121
|
+
meta: {},
|
|
122
|
+
main: name
|
|
123
|
+
});
|
|
124
|
+
const relation = kir.rels[name];
|
|
125
|
+
const codesTable = { ...codesFromTypePatternGraph(relDef.typePatternGraph, relation), ...(options.codes || {}) };
|
|
126
|
+
return { relation, codesTable };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function codesFromTypePatternGraph(typePatternGraph, kirRelation) {
|
|
130
|
+
if (!typePatternGraph || typeof typePatternGraph.findCode !== "function") return {};
|
|
131
|
+
const result = {};
|
|
132
|
+
for (const node of kirRelation.patternGraph?.nodes || []) {
|
|
133
|
+
if (node.kind !== "type" || !node.code || result[node.code]) continue;
|
|
134
|
+
const code = typePatternGraph.findCode(node.code);
|
|
135
|
+
if (code) result[node.code] = code;
|
|
136
|
+
}
|
|
137
|
+
return result;
|
|
138
|
+
}
|
|
139
|
+
|
|
56
140
|
class KVMBuilder {
|
|
57
|
-
constructor(
|
|
58
|
-
this.
|
|
141
|
+
constructor(patternGraph, codesTable = {}) {
|
|
142
|
+
this.patternGraph = patternGraph;
|
|
143
|
+
this.codes = codesTable;
|
|
144
|
+
this.patternCache = new Map();
|
|
59
145
|
this.regCount = 0;
|
|
60
146
|
this.instructions = [];
|
|
61
147
|
}
|
|
@@ -69,13 +155,20 @@ class KVMBuilder {
|
|
|
69
155
|
}
|
|
70
156
|
|
|
71
157
|
getStaticPattern(exp, index) {
|
|
72
|
-
if (!this.
|
|
73
|
-
|
|
74
|
-
|
|
158
|
+
if (!this.patternGraph || !exp.patterns) return null;
|
|
159
|
+
return this.getPattern(exp.patterns[index]);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
getPattern(patternId) {
|
|
163
|
+
if (patternId == null) return null;
|
|
164
|
+
if (!this.patternCache.has(patternId)) {
|
|
165
|
+
this.patternCache.set(patternId, kirPatternToPropertyList(this.patternGraph, this.codes, patternId));
|
|
166
|
+
}
|
|
167
|
+
return this.patternCache.get(patternId);
|
|
75
168
|
}
|
|
76
169
|
}
|
|
77
170
|
|
|
78
|
-
function
|
|
171
|
+
function compileKIRExp(exp, inputReg, builder) {
|
|
79
172
|
switch (exp.op) {
|
|
80
173
|
case "identity": {
|
|
81
174
|
const dest = builder.nextReg();
|
|
@@ -114,7 +207,7 @@ function compile(exp, inputReg, builder) {
|
|
|
114
207
|
op: "project_field",
|
|
115
208
|
dest,
|
|
116
209
|
src: inputReg,
|
|
117
|
-
label: exp.
|
|
210
|
+
label: exp.label,
|
|
118
211
|
pattern: builder.getStaticPattern(exp, 1),
|
|
119
212
|
exp
|
|
120
213
|
});
|
|
@@ -126,7 +219,7 @@ function compile(exp, inputReg, builder) {
|
|
|
126
219
|
op: "project_variant",
|
|
127
220
|
dest,
|
|
128
221
|
src: inputReg,
|
|
129
|
-
tag: exp.
|
|
222
|
+
tag: exp.tag,
|
|
130
223
|
pattern: builder.getStaticPattern(exp, 1),
|
|
131
224
|
exp
|
|
132
225
|
});
|
|
@@ -137,14 +230,14 @@ function compile(exp, inputReg, builder) {
|
|
|
137
230
|
builder.emit({
|
|
138
231
|
op: "make_variant",
|
|
139
232
|
dest,
|
|
140
|
-
tag: exp.
|
|
233
|
+
tag: exp.tag,
|
|
141
234
|
src: inputReg
|
|
142
235
|
});
|
|
143
236
|
return dest;
|
|
144
237
|
}
|
|
145
238
|
case "ref": {
|
|
146
239
|
const dest = builder.nextReg();
|
|
147
|
-
if (exp.ref
|
|
240
|
+
if (isIntrinsic(exp.ref)) {
|
|
148
241
|
builder.emit({
|
|
149
242
|
op: "call_intrinsic",
|
|
150
243
|
dest,
|
|
@@ -172,18 +265,18 @@ function compile(exp, inputReg, builder) {
|
|
|
172
265
|
builder.emit({ op: "guard_pattern", dest: guarded, src: currentReg, pattern: compInputPattern, exp });
|
|
173
266
|
currentReg = guarded;
|
|
174
267
|
}
|
|
175
|
-
for (const subExp of exp.
|
|
176
|
-
currentReg =
|
|
268
|
+
for (const subExp of exp.items) {
|
|
269
|
+
currentReg = compileKIRExp(subExp, currentReg, builder);
|
|
177
270
|
}
|
|
178
271
|
return currentReg;
|
|
179
272
|
}
|
|
180
273
|
case "product": {
|
|
181
274
|
const dest = builder.nextReg();
|
|
182
275
|
const branches = [];
|
|
183
|
-
for (const { label,
|
|
184
|
-
const branchBuilder = new KVMBuilder(builder.
|
|
276
|
+
for (const { label, expr: fieldExp } of exp.fields) {
|
|
277
|
+
const branchBuilder = new KVMBuilder(builder.patternGraph, builder.codes);
|
|
185
278
|
const branchInput = "%in";
|
|
186
|
-
const branchOutput =
|
|
279
|
+
const branchOutput = compileKIRExp(fieldExp, branchInput, branchBuilder);
|
|
187
280
|
branchBuilder.emit({ op: "return", src: branchOutput });
|
|
188
281
|
branches.push({
|
|
189
282
|
label,
|
|
@@ -203,10 +296,10 @@ function compile(exp, inputReg, builder) {
|
|
|
203
296
|
case "union": {
|
|
204
297
|
const dest = builder.nextReg();
|
|
205
298
|
const branches = [];
|
|
206
|
-
for (const branchExp of exp.
|
|
207
|
-
const branchBuilder = new KVMBuilder(builder.
|
|
299
|
+
for (const branchExp of exp.items) {
|
|
300
|
+
const branchBuilder = new KVMBuilder(builder.patternGraph, builder.codes);
|
|
208
301
|
const branchInput = "%in";
|
|
209
|
-
const branchOutput =
|
|
302
|
+
const branchOutput = compileKIRExp(branchExp, branchInput, branchBuilder);
|
|
210
303
|
branchBuilder.emit({ op: "return", src: branchOutput });
|
|
211
304
|
branches.push({
|
|
212
305
|
body: branchBuilder.instructions
|
|
@@ -222,30 +315,29 @@ function compile(exp, inputReg, builder) {
|
|
|
222
315
|
return dest;
|
|
223
316
|
}
|
|
224
317
|
default:
|
|
225
|
-
throw new Error(`Unsupported
|
|
318
|
+
throw new Error(`Unsupported KIR operation: ${exp.op}`);
|
|
226
319
|
}
|
|
227
320
|
}
|
|
228
321
|
|
|
229
|
-
export function
|
|
230
|
-
const
|
|
231
|
-
const builder = new KVMBuilder(typePatternGraph);
|
|
322
|
+
export function lowerKIRRelationToKVM(kirRel, name, options = {}) {
|
|
323
|
+
const builder = new KVMBuilder(kirRel.patternGraph, options.codes || {});
|
|
232
324
|
const inputReg = "%in";
|
|
233
325
|
|
|
234
326
|
let currentReg = inputReg;
|
|
235
|
-
const inputPattern = builder.
|
|
327
|
+
const inputPattern = builder.getPattern(kirRel.inputPattern);
|
|
236
328
|
if (inputPattern) {
|
|
237
329
|
const guarded = builder.nextReg();
|
|
238
|
-
builder.emit({ op: "guard_pattern", dest: guarded, src: currentReg, pattern: inputPattern, exp:
|
|
330
|
+
builder.emit({ op: "guard_pattern", dest: guarded, src: currentReg, pattern: inputPattern, exp: kirRel.body });
|
|
239
331
|
currentReg = guarded;
|
|
240
332
|
}
|
|
241
333
|
|
|
242
|
-
const bodyOutput =
|
|
334
|
+
const bodyOutput = compileKIRExp(kirRel.body, currentReg, builder);
|
|
243
335
|
|
|
244
336
|
let finalReg = bodyOutput;
|
|
245
|
-
const outputPattern = builder.
|
|
337
|
+
const outputPattern = builder.getPattern(kirRel.outputPattern);
|
|
246
338
|
if (outputPattern) {
|
|
247
339
|
const guarded = builder.nextReg();
|
|
248
|
-
builder.emit({ op: "guard_pattern", dest: guarded, src: finalReg, pattern: outputPattern, exp:
|
|
340
|
+
builder.emit({ op: "guard_pattern", dest: guarded, src: finalReg, pattern: outputPattern, exp: kirRel.body });
|
|
249
341
|
finalReg = guarded;
|
|
250
342
|
}
|
|
251
343
|
|
|
@@ -255,20 +347,414 @@ export function lowerToKVM(relDef, name, options = {}) {
|
|
|
255
347
|
name,
|
|
256
348
|
inputPattern,
|
|
257
349
|
outputPattern,
|
|
258
|
-
isConverged:
|
|
350
|
+
isConverged: kirRel.typeDerivation?.status === "converged",
|
|
259
351
|
body: builder.instructions
|
|
260
352
|
};
|
|
261
353
|
}
|
|
262
354
|
|
|
263
|
-
function
|
|
355
|
+
export function lowerKIRToKVM(kir) {
|
|
356
|
+
if (kir?.format !== "k-ir" || kir.layer !== "KIR-P") {
|
|
357
|
+
throw new Error("kVM lowering requires a KIR-P object");
|
|
358
|
+
}
|
|
359
|
+
const kvmProgram = {};
|
|
360
|
+
for (const [name, relDef] of Object.entries(kir.rels || {})) {
|
|
361
|
+
kvmProgram[name] = lowerKIRRelationToKVM(relDef, name, { codes: kir.codes || {} });
|
|
362
|
+
}
|
|
363
|
+
return kvmProgram;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
export function lowerToKVM(relDef, name, options = {}) {
|
|
367
|
+
if (isKIRRelation(relDef)) {
|
|
368
|
+
return lowerKIRRelationToKVM(relDef, name, options);
|
|
369
|
+
}
|
|
370
|
+
const { relation, codesTable } = objectRelationToKIRRelation(relDef, name, options);
|
|
371
|
+
return lowerKIRRelationToKVM(relation, name, { ...options, codes: codesTable });
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
export function isSingletonKVMInputPattern(pattern) {
|
|
375
|
+
return Array.isArray(pattern)
|
|
376
|
+
&& pattern.length > 0
|
|
377
|
+
&& pattern.every((node) => Array.isArray(node) && KVM_SINGLETON_INPUT_KINDS.has(node[0]));
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function assertSingletonKVMInputPattern(pattern) {
|
|
381
|
+
if (!isSingletonKVMInputPattern(pattern)) {
|
|
382
|
+
throw new Error(
|
|
383
|
+
".kvm emission requires a singleton input pattern; use a closed product/union pattern or a type/code input pattern"
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
export function objectToKVMArtifact(object, relationName, inputPattern, options = {}) {
|
|
389
|
+
if (!inputPattern) {
|
|
390
|
+
const kir = objectToKIRP(object);
|
|
391
|
+
const entry = relationName || kir.main || object.main;
|
|
392
|
+
const functions = lowerKIRToKVM(kir);
|
|
393
|
+
const entryFunc = functions[entry];
|
|
394
|
+
if (!entryFunc) throw new Error(`kVM entry relation '${entry}' was not produced`);
|
|
395
|
+
|
|
396
|
+
return {
|
|
397
|
+
format: KVM_FORMAT,
|
|
398
|
+
version: KVM_VERSION,
|
|
399
|
+
layer: "KVM-P",
|
|
400
|
+
isPolymorphic: true,
|
|
401
|
+
sourceFormat: object.format,
|
|
402
|
+
relation: entry,
|
|
403
|
+
entry,
|
|
404
|
+
inputPattern: entryFunc.inputPattern,
|
|
405
|
+
outputPattern: entryFunc.outputPattern,
|
|
406
|
+
isConverged: entryFunc.isConverged,
|
|
407
|
+
functions,
|
|
408
|
+
codes: kir.codes || {},
|
|
409
|
+
kir
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
assertSingletonKVMInputPattern(inputPattern);
|
|
414
|
+
const { relation, kir, entryName } = retypeObjectRelationForBackend(
|
|
415
|
+
object,
|
|
416
|
+
relationName || object.main,
|
|
417
|
+
inputPattern,
|
|
418
|
+
options
|
|
419
|
+
);
|
|
420
|
+
const entry = entryName;
|
|
421
|
+
const functions = lowerKIRToKVM(kir);
|
|
422
|
+
const entryFunc = functions[entry];
|
|
423
|
+
if (!entryFunc) throw new Error(`Retyped kVM entry relation '${entry}' was not produced`);
|
|
424
|
+
|
|
425
|
+
return {
|
|
426
|
+
format: KVM_FORMAT,
|
|
427
|
+
version: KVM_VERSION,
|
|
428
|
+
layer: "KVM",
|
|
429
|
+
sourceFormat: object.format,
|
|
430
|
+
relation,
|
|
431
|
+
entry,
|
|
432
|
+
inputPattern: entryFunc.inputPattern,
|
|
433
|
+
outputPattern: entryFunc.outputPattern,
|
|
434
|
+
isConverged: entryFunc.isConverged,
|
|
435
|
+
functions,
|
|
436
|
+
kir
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
export function checkEnvelopeCompatibility(targetPattern, inputPattern) {
|
|
441
|
+
if (!targetPattern || targetPattern.length === 0) return inputPattern;
|
|
442
|
+
if (!inputPattern || inputPattern.length === 0) return targetPattern;
|
|
443
|
+
|
|
444
|
+
const [targetKind, targetEdges] = targetPattern[0];
|
|
445
|
+
const [inputKind, inputEdges] = inputPattern[0];
|
|
446
|
+
|
|
447
|
+
if (targetKind === "any") {
|
|
448
|
+
return inputPattern;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if (targetKind === "open-product" || targetKind === "closed-product") {
|
|
452
|
+
if (inputKind !== "open-product" && inputKind !== "closed-product") {
|
|
453
|
+
throw new TypeError(`Type Error: expected product envelope, got ${inputKind}`);
|
|
454
|
+
}
|
|
455
|
+
const inputLabels = new Set(inputEdges.map(([label]) => label));
|
|
456
|
+
const missing = targetEdges.filter(([label]) => !inputLabels.has(label));
|
|
457
|
+
if (missing.length > 0) {
|
|
458
|
+
throw new TypeError(`Type Error: input product missing required fields: ${missing.map(([l]) => l).join(", ")}`);
|
|
459
|
+
}
|
|
460
|
+
if (targetKind === "closed-product" && inputKind === "closed-product") {
|
|
461
|
+
if (inputEdges.length !== targetEdges.length) {
|
|
462
|
+
throw new TypeError(`Type Error: closed product field mismatch`);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
return inputPattern;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if (targetKind === "open-union" || targetKind === "closed-union") {
|
|
469
|
+
if (inputKind !== "open-union" && inputKind !== "closed-union") {
|
|
470
|
+
throw new TypeError(`Type Error: expected union envelope, got ${inputKind}`);
|
|
471
|
+
}
|
|
472
|
+
const targetTags = new Set(targetEdges.map(([tag]) => tag));
|
|
473
|
+
const unhandled = inputEdges.filter(([tag]) => !targetTags.has(tag));
|
|
474
|
+
if (unhandled.length > 0) {
|
|
475
|
+
throw new TypeError(`Type Error: unhandled variant tags: ${unhandled.map(([t]) => t).join(", ")}`);
|
|
476
|
+
}
|
|
477
|
+
return inputPattern;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
try {
|
|
481
|
+
return intersectPropertyListPatterns(targetPattern, inputPattern);
|
|
482
|
+
} catch (err) {
|
|
483
|
+
throw new TypeError(`Type Error: input envelope does not match function's input pattern: ${err.message}`);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
export function specializeKVM(artifactOrFunc, inputEnvelope, options = {}) {
|
|
488
|
+
let inputPattern = inputEnvelope;
|
|
489
|
+
if (inputEnvelope && isValue(inputEnvelope)) {
|
|
490
|
+
inputPattern = inputEnvelope.pattern;
|
|
491
|
+
}
|
|
492
|
+
if (inputPattern && !Array.isArray(inputPattern) && typeof inputPattern.toPropertyList === "function") {
|
|
493
|
+
inputPattern = inputPattern.toPropertyList();
|
|
494
|
+
}
|
|
495
|
+
if (!Array.isArray(inputPattern) || inputPattern.length === 0) {
|
|
496
|
+
throw new Error("specializeKVM requires a valid input envelope pattern property list");
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
const isArtifact = artifactOrFunc && typeof artifactOrFunc === "object" && artifactOrFunc.format === KVM_FORMAT;
|
|
500
|
+
const targetFunc = isArtifact ? artifactOrFunc.functions[artifactOrFunc.entry] : artifactOrFunc;
|
|
501
|
+
if (!targetFunc || !Array.isArray(targetFunc.body)) {
|
|
502
|
+
throw new Error("specializeKVM requires a valid kVM function or artifact");
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// 1. Boundary Type Check & Pattern Intersection
|
|
506
|
+
let specializedInputPattern;
|
|
507
|
+
if (targetFunc.inputPattern) {
|
|
508
|
+
specializedInputPattern = checkEnvelopeCompatibility(targetFunc.inputPattern, inputPattern);
|
|
509
|
+
} else {
|
|
510
|
+
specializedInputPattern = inputPattern;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function specializeInstructionList(instructions, initialEnv) {
|
|
514
|
+
const regPatterns = new Map(initialEnv);
|
|
515
|
+
const specialized = [];
|
|
516
|
+
|
|
517
|
+
for (const inst of instructions) {
|
|
518
|
+
switch (inst.op) {
|
|
519
|
+
case "id": {
|
|
520
|
+
const srcPattern = regPatterns.get(inst.src);
|
|
521
|
+
if (srcPattern) regPatterns.set(inst.dest, srcPattern);
|
|
522
|
+
specialized.push({ ...inst });
|
|
523
|
+
break;
|
|
524
|
+
}
|
|
525
|
+
case "fail": {
|
|
526
|
+
specialized.push({ ...inst });
|
|
527
|
+
break;
|
|
528
|
+
}
|
|
529
|
+
case "project_field": {
|
|
530
|
+
const srcPattern = regPatterns.get(inst.src);
|
|
531
|
+
let fieldPattern = null;
|
|
532
|
+
if (srcPattern) {
|
|
533
|
+
fieldPattern = edgeSubpattern(srcPattern, inst.label);
|
|
534
|
+
if (fieldPattern) regPatterns.set(inst.dest, fieldPattern);
|
|
535
|
+
}
|
|
536
|
+
specialized.push({
|
|
537
|
+
...inst,
|
|
538
|
+
pattern: fieldPattern || inst.pattern
|
|
539
|
+
});
|
|
540
|
+
break;
|
|
541
|
+
}
|
|
542
|
+
case "project_variant": {
|
|
543
|
+
const srcPattern = regPatterns.get(inst.src);
|
|
544
|
+
let variantPattern = null;
|
|
545
|
+
if (srcPattern) {
|
|
546
|
+
variantPattern = edgeSubpattern(srcPattern, inst.tag);
|
|
547
|
+
if (variantPattern) regPatterns.set(inst.dest, variantPattern);
|
|
548
|
+
}
|
|
549
|
+
specialized.push({
|
|
550
|
+
...inst,
|
|
551
|
+
pattern: variantPattern || inst.pattern
|
|
552
|
+
});
|
|
553
|
+
break;
|
|
554
|
+
}
|
|
555
|
+
case "make_variant": {
|
|
556
|
+
const srcPattern = regPatterns.get(inst.src);
|
|
557
|
+
if (srcPattern) {
|
|
558
|
+
const vp = composePattern("closed-union", [[inst.tag, srcPattern]]);
|
|
559
|
+
regPatterns.set(inst.dest, vp);
|
|
560
|
+
}
|
|
561
|
+
specialized.push({ ...inst });
|
|
562
|
+
break;
|
|
563
|
+
}
|
|
564
|
+
case "guard_pattern": {
|
|
565
|
+
const srcPattern = regPatterns.get(inst.src);
|
|
566
|
+
if (srcPattern && inst.pattern) {
|
|
567
|
+
try {
|
|
568
|
+
const intersected = checkEnvelopeCompatibility(inst.pattern, srcPattern);
|
|
569
|
+
if (JSON.stringify(intersected) === JSON.stringify(srcPattern)) {
|
|
570
|
+
// Redundant guard: pattern is already satisfied
|
|
571
|
+
specialized.push({ op: "id", dest: inst.dest, src: inst.src });
|
|
572
|
+
regPatterns.set(inst.dest, srcPattern);
|
|
573
|
+
break;
|
|
574
|
+
} else {
|
|
575
|
+
regPatterns.set(inst.dest, intersected);
|
|
576
|
+
specialized.push({ ...inst, pattern: intersected });
|
|
577
|
+
break;
|
|
578
|
+
}
|
|
579
|
+
} catch {
|
|
580
|
+
// Disjoint guard: can never succeed
|
|
581
|
+
specialized.push({ op: "fail", dest: inst.dest });
|
|
582
|
+
break;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
specialized.push({ ...inst });
|
|
586
|
+
break;
|
|
587
|
+
}
|
|
588
|
+
case "guard_code": {
|
|
589
|
+
const srcPattern = regPatterns.get(inst.src);
|
|
590
|
+
if (srcPattern) regPatterns.set(inst.dest, srcPattern);
|
|
591
|
+
specialized.push({ ...inst });
|
|
592
|
+
break;
|
|
593
|
+
}
|
|
594
|
+
case "product": {
|
|
595
|
+
const srcPattern = regPatterns.get(inst.src);
|
|
596
|
+
const specializedBranches = [];
|
|
597
|
+
const fieldEntries = [];
|
|
598
|
+
|
|
599
|
+
for (const branch of inst.branches) {
|
|
600
|
+
const branchEnv = new Map(regPatterns);
|
|
601
|
+
branchEnv.set("%in", srcPattern);
|
|
602
|
+
const { body: branchBody, returnPattern } = specializeInstructionList(branch.body, branchEnv);
|
|
603
|
+
specializedBranches.push({
|
|
604
|
+
label: branch.label,
|
|
605
|
+
body: branchBody
|
|
606
|
+
});
|
|
607
|
+
if (returnPattern) {
|
|
608
|
+
fieldEntries.push([branch.label, returnPattern]);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
let productPattern = null;
|
|
613
|
+
if (fieldEntries.length === inst.branches.length) {
|
|
614
|
+
fieldEntries.sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));
|
|
615
|
+
specializedBranches.sort((a, b) => (a.label < b.label ? -1 : a.label > b.label ? 1 : 0));
|
|
616
|
+
productPattern = composePattern("closed-product", fieldEntries);
|
|
617
|
+
regPatterns.set(inst.dest, productPattern);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
specialized.push({
|
|
621
|
+
...inst,
|
|
622
|
+
branches: specializedBranches,
|
|
623
|
+
pattern: productPattern || inst.pattern
|
|
624
|
+
});
|
|
625
|
+
break;
|
|
626
|
+
}
|
|
627
|
+
case "union": {
|
|
628
|
+
const srcPattern = regPatterns.get(inst.src);
|
|
629
|
+
const specializedBranches = [];
|
|
630
|
+
let unionResultPattern = null;
|
|
631
|
+
|
|
632
|
+
for (let i = 0; i < inst.branches.length; i++) {
|
|
633
|
+
const branch = inst.branches[i];
|
|
634
|
+
const branchEnv = new Map(regPatterns);
|
|
635
|
+
branchEnv.set("%in", srcPattern);
|
|
636
|
+
|
|
637
|
+
// Dead branch check: if branch begins with project_variant for tag not in srcPattern
|
|
638
|
+
const firstInst = branch.body?.[0];
|
|
639
|
+
if (firstInst?.op === "project_variant" && srcPattern) {
|
|
640
|
+
const tagPattern = edgeSubpattern(srcPattern, firstInst.tag);
|
|
641
|
+
if (srcPattern[0]?.[0]?.endsWith("union") && !tagPattern) {
|
|
642
|
+
continue;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
const { body: branchBody, returnPattern } = specializeInstructionList(branch.body, branchEnv);
|
|
647
|
+
if (branchBody.length > 0 && branchBody[0].op === "fail") {
|
|
648
|
+
continue;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
specializedBranches.push({
|
|
652
|
+
label: branch.label,
|
|
653
|
+
body: branchBody
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
if (returnPattern && !unionResultPattern) {
|
|
657
|
+
unionResultPattern = returnPattern;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
if (unionResultPattern) {
|
|
662
|
+
regPatterns.set(inst.dest, unionResultPattern);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
specialized.push({
|
|
666
|
+
...inst,
|
|
667
|
+
branches: specializedBranches
|
|
668
|
+
});
|
|
669
|
+
break;
|
|
670
|
+
}
|
|
671
|
+
case "call": {
|
|
672
|
+
specialized.push({ ...inst });
|
|
673
|
+
break;
|
|
674
|
+
}
|
|
675
|
+
case "call_intrinsic": {
|
|
676
|
+
specialized.push({ ...inst });
|
|
677
|
+
break;
|
|
678
|
+
}
|
|
679
|
+
case "return": {
|
|
680
|
+
const retPattern = regPatterns.get(inst.src);
|
|
681
|
+
specialized.push({ ...inst });
|
|
682
|
+
return { body: specialized, returnPattern: retPattern };
|
|
683
|
+
}
|
|
684
|
+
default:
|
|
685
|
+
specialized.push({ ...inst });
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
return { body: specialized, returnPattern: regPatterns.get("%out") };
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
const { body: specializedBody, returnPattern } = specializeInstructionList(
|
|
692
|
+
targetFunc.body,
|
|
693
|
+
new Map([["%in", specializedInputPattern]])
|
|
694
|
+
);
|
|
695
|
+
|
|
696
|
+
const derivedOutputPattern = returnPattern || targetFunc.outputPattern;
|
|
697
|
+
|
|
698
|
+
const specializedFunc = {
|
|
699
|
+
name: `${targetFunc.name}__spec`,
|
|
700
|
+
inputPattern: specializedInputPattern,
|
|
701
|
+
outputPattern: derivedOutputPattern,
|
|
702
|
+
isConverged: true,
|
|
703
|
+
isSpecialized: true,
|
|
704
|
+
body: specializedBody
|
|
705
|
+
};
|
|
706
|
+
|
|
707
|
+
if (isArtifact) {
|
|
708
|
+
return {
|
|
709
|
+
...artifactOrFunc,
|
|
710
|
+
layer: "KVM-M",
|
|
711
|
+
isPolymorphic: false,
|
|
712
|
+
isSpecialized: true,
|
|
713
|
+
inputPattern: specializedInputPattern,
|
|
714
|
+
outputPattern: derivedOutputPattern,
|
|
715
|
+
functions: {
|
|
716
|
+
...artifactOrFunc.functions,
|
|
717
|
+
[specializedFunc.name]: specializedFunc
|
|
718
|
+
},
|
|
719
|
+
entry: specializedFunc.name
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
return specializedFunc;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
const TAIL_CALL = Symbol("TAIL_CALL");
|
|
727
|
+
|
|
728
|
+
function tailValueAfter(insts, startIndex, valueReg) {
|
|
729
|
+
let current = valueReg;
|
|
730
|
+
for (let i = startIndex; i < insts.length; i++) {
|
|
731
|
+
const inst = insts[i];
|
|
732
|
+
if (inst.op === "guard_pattern" || inst.op === "guard_code" || inst.op === "id") {
|
|
733
|
+
if (inst.src !== current) return false;
|
|
734
|
+
current = inst.dest;
|
|
735
|
+
continue;
|
|
736
|
+
}
|
|
737
|
+
if (inst.op === "return" && inst.src === current) {
|
|
738
|
+
return true;
|
|
739
|
+
}
|
|
740
|
+
return false;
|
|
741
|
+
}
|
|
742
|
+
return false;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
function executeBlock(instructions, inputVal, context, isTail = false) {
|
|
264
746
|
const registers = new Map();
|
|
265
747
|
registers.set("%in", inputVal);
|
|
266
748
|
|
|
267
|
-
for (
|
|
268
|
-
const
|
|
749
|
+
for (let i = 0; i < instructions.length; i++) {
|
|
750
|
+
const inst = instructions[i];
|
|
751
|
+
const res = executeInstruction(inst, registers, context, instructions, i, isTail);
|
|
269
752
|
if (res === undefined) {
|
|
270
753
|
return undefined;
|
|
271
754
|
}
|
|
755
|
+
if (res && res[TAIL_CALL]) {
|
|
756
|
+
return res;
|
|
757
|
+
}
|
|
272
758
|
if (res.type === "return") {
|
|
273
759
|
return res.value;
|
|
274
760
|
}
|
|
@@ -276,7 +762,7 @@ function executeBlock(instructions, inputVal, context) {
|
|
|
276
762
|
return undefined;
|
|
277
763
|
}
|
|
278
764
|
|
|
279
|
-
function executeInstruction(inst, registers, context) {
|
|
765
|
+
function executeInstruction(inst, registers, context, instructions = null, instIndex = -1, isTail = false) {
|
|
280
766
|
const options = context.options || {};
|
|
281
767
|
switch (inst.op) {
|
|
282
768
|
case "id": {
|
|
@@ -366,7 +852,10 @@ function executeInstruction(inst, registers, context) {
|
|
|
366
852
|
console.log(`[Trace] Calling ${inst.func} with:`, val ? val.toString() : "null");
|
|
367
853
|
}
|
|
368
854
|
if (!relDef._kvmFunc) {
|
|
369
|
-
relDef._kvmFunc = lowerToKVM(relDef, inst.func);
|
|
855
|
+
relDef._kvmFunc = lowerToKVM(relDef, inst.func, { codes: context.codes || {} });
|
|
856
|
+
}
|
|
857
|
+
if (!options.trace && isTail && instructions && tailValueAfter(instructions, instIndex + 1, inst.dest)) {
|
|
858
|
+
return { [TAIL_CALL]: true, func: relDef._kvmFunc, val };
|
|
370
859
|
}
|
|
371
860
|
const res = executeKVM(relDef._kvmFunc, val, context);
|
|
372
861
|
if (options.trace) {
|
|
@@ -377,32 +866,14 @@ function executeInstruction(inst, registers, context) {
|
|
|
377
866
|
return { type: "continue" };
|
|
378
867
|
}
|
|
379
868
|
case "call_intrinsic": {
|
|
380
|
-
|
|
381
|
-
const builtinFunc = run.builtin[inst.symbol];
|
|
382
|
-
if (!builtinFunc) {
|
|
383
|
-
throw new Error(`Unknown builtin: '${inst.symbol}'`);
|
|
384
|
-
}
|
|
385
|
-
if (!options.envelopeFree && inst.pattern) {
|
|
386
|
-
try {
|
|
387
|
-
const constrained = constrainWithPattern(val, inst.pattern, inst.exp);
|
|
388
|
-
const res = builtinFunc(constrained);
|
|
389
|
-
registers.set(inst.dest, res);
|
|
390
|
-
return { type: "continue" };
|
|
391
|
-
} catch (err) {
|
|
392
|
-
throw err;
|
|
393
|
-
}
|
|
394
|
-
} else {
|
|
395
|
-
const res = builtinFunc(val);
|
|
396
|
-
registers.set(inst.dest, res);
|
|
397
|
-
return { type: "continue" };
|
|
398
|
-
}
|
|
869
|
+
throw unsupportedIntrinsic("kVM interpreter", inst.symbol);
|
|
399
870
|
}
|
|
400
871
|
case "product": {
|
|
401
872
|
const val = registers.get(inst.src);
|
|
402
873
|
const result = {};
|
|
403
874
|
const patternEntries = [];
|
|
404
875
|
for (const branch of inst.branches) {
|
|
405
|
-
const branchRes = executeBlock(branch.body, val, context);
|
|
876
|
+
const branchRes = executeBlock(branch.body, val, context, false);
|
|
406
877
|
if (branchRes === undefined) return undefined;
|
|
407
878
|
result[branch.label] = branchRes;
|
|
408
879
|
if (!options.envelopeFree) {
|
|
@@ -425,9 +896,16 @@ function executeInstruction(inst, registers, context) {
|
|
|
425
896
|
}
|
|
426
897
|
case "union": {
|
|
427
898
|
const val = registers.get(inst.src);
|
|
428
|
-
|
|
429
|
-
|
|
899
|
+
const unionIsTail = isTail && instructions && tailValueAfter(instructions, instIndex + 1, inst.dest);
|
|
900
|
+
for (let i = 0; i < inst.branches.length; i++) {
|
|
901
|
+
const branch = inst.branches[i];
|
|
902
|
+
const isLastBranch = i === inst.branches.length - 1;
|
|
903
|
+
const branchIsTail = unionIsTail && isLastBranch;
|
|
904
|
+
const branchRes = executeBlock(branch.body, val, context, branchIsTail);
|
|
430
905
|
if (branchRes !== undefined) {
|
|
906
|
+
if (branchRes && branchRes[TAIL_CALL]) {
|
|
907
|
+
return branchRes;
|
|
908
|
+
}
|
|
431
909
|
registers.set(inst.dest, branchRes);
|
|
432
910
|
return { type: "continue" };
|
|
433
911
|
}
|
|
@@ -444,22 +922,46 @@ function executeInstruction(inst, registers, context) {
|
|
|
444
922
|
|
|
445
923
|
export function executeKVM(kvmFunc, inputVal, context) {
|
|
446
924
|
const options = context.options || {};
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
925
|
+
let currentFunc = kvmFunc;
|
|
926
|
+
let currentVal = inputVal;
|
|
927
|
+
|
|
928
|
+
while (true) {
|
|
929
|
+
if (context.profile) {
|
|
930
|
+
let name = currentFunc.name || "anon";
|
|
931
|
+
if (context.hashToName && context.hashToName.has(name)) {
|
|
932
|
+
name = context.hashToName.get(name);
|
|
933
|
+
}
|
|
934
|
+
context.profile[name] = (context.profile[name] || 0) + 1;
|
|
935
|
+
}
|
|
936
|
+
if (options.requireConverged && !currentFunc.isConverged) {
|
|
937
|
+
throw new Error(`Cannot run '${currentFunc.name}' without envelopes: type derivation is not converged`);
|
|
938
|
+
}
|
|
450
939
|
|
|
451
|
-
|
|
452
|
-
|
|
940
|
+
const result = executeBlock(currentFunc.body, currentVal, context, true);
|
|
941
|
+
if (result === undefined) return undefined;
|
|
942
|
+
if (result && result[TAIL_CALL]) {
|
|
943
|
+
currentFunc = result.func;
|
|
944
|
+
currentVal = result.val;
|
|
945
|
+
continue;
|
|
946
|
+
}
|
|
453
947
|
|
|
454
|
-
|
|
455
|
-
|
|
948
|
+
if (options.envelopeFree && currentFunc.isConverged) {
|
|
949
|
+
return withPattern(result, currentFunc.outputPattern);
|
|
950
|
+
}
|
|
951
|
+
return result;
|
|
456
952
|
}
|
|
457
|
-
return result;
|
|
458
953
|
}
|
|
459
954
|
|
|
460
955
|
export default {
|
|
956
|
+
KVM_FORMAT,
|
|
957
|
+
KVM_VERSION,
|
|
461
958
|
lowerToKVM,
|
|
462
|
-
|
|
959
|
+
lowerKIRRelationToKVM,
|
|
960
|
+
lowerKIRToKVM,
|
|
961
|
+
executeKVM,
|
|
962
|
+
isSingletonKVMInputPattern,
|
|
963
|
+
objectToKVMArtifact,
|
|
964
|
+
specializeKVM
|
|
463
965
|
};
|
|
464
966
|
|
|
465
967
|
function usage() {
|
|
@@ -521,7 +1023,7 @@ async function main() {
|
|
|
521
1023
|
if (object.main == null) {
|
|
522
1024
|
throw new Error("Cannot run a .klib library without a main relation; load it with --lib.");
|
|
523
1025
|
}
|
|
524
|
-
return { kind: "object",
|
|
1026
|
+
return { kind: "object", object };
|
|
525
1027
|
} catch (error) {
|
|
526
1028
|
if (error.message === "Cannot run a .klib library without a main relation; load it with --lib.") {
|
|
527
1029
|
throw error;
|
|
@@ -546,16 +1048,17 @@ async function main() {
|
|
|
546
1048
|
throw new Error(`Unexpected argument: ${args[0]}`);
|
|
547
1049
|
}
|
|
548
1050
|
|
|
549
|
-
const
|
|
550
|
-
? programInput.
|
|
551
|
-
:
|
|
552
|
-
const
|
|
553
|
-
const
|
|
1051
|
+
const object = programInput.kind === "object"
|
|
1052
|
+
? programInput.object
|
|
1053
|
+
: decodeObject(compileObjectBuffer(programInput.source, { libraries }));
|
|
1054
|
+
const kir = objectToKIRP(object);
|
|
1055
|
+
const mainRelName = kir.main;
|
|
1056
|
+
const mainRel = kir.rels[mainRelName];
|
|
554
1057
|
if (!mainRel) {
|
|
555
1058
|
throw new Error(`No main relation (${mainRelName}) defined in script`);
|
|
556
1059
|
}
|
|
557
1060
|
|
|
558
|
-
const kvmFunc =
|
|
1061
|
+
const kvmFunc = lowerKIRRelationToKVM(mainRel, mainRelName, { codes: kir.codes });
|
|
559
1062
|
|
|
560
1063
|
const buffer = [];
|
|
561
1064
|
inputStream.on("data", (data) => buffer.push(Buffer.isBuffer(data) ? data : Buffer.from(data)));
|
|
@@ -564,8 +1067,9 @@ async function main() {
|
|
|
564
1067
|
const inputBuffer = Buffer.concat(buffer);
|
|
565
1068
|
const { pattern: inputPattern, value } = decodeWire(inputBuffer);
|
|
566
1069
|
const context = {
|
|
567
|
-
rels:
|
|
568
|
-
|
|
1070
|
+
rels: kir.rels,
|
|
1071
|
+
codes: kir.codes,
|
|
1072
|
+
findCode: kirFindCode(kir),
|
|
569
1073
|
options: {
|
|
570
1074
|
envelopeFree
|
|
571
1075
|
}
|