@fraczak/k 6.3.3 → 6.4.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.
Files changed (155) hide show
  1. package/DOCS/CODECS.md +258 -0
  2. package/DOCS/DICTIONARY.md +1172 -0
  3. package/DOCS/KIR_SPECIALIZATION_DESIGN.md +51 -46
  4. package/DOCS/KIR_V1.md +221 -0
  5. package/DOCS/KVM_EXECUTION_MODEL.md +56 -19
  6. package/DOCS/OBJECT_FILE_AND_PATTERN.md +2 -1
  7. package/DOCS/PROFILING_AND_TRACING.md +196 -0
  8. package/DOCS/REPL.md +61 -4
  9. package/DOCS/WEB_REPL_REQUIREMENTS.md +135 -0
  10. package/DOCS/dependency-graph.md +144 -0
  11. package/Examples/arithmetics.k +51 -48
  12. package/Examples/bnat.k +1 -1
  13. package/Examples/buda.k +44 -0
  14. package/Examples/ieee.k +300 -8671
  15. package/Examples/poly.k +67 -0
  16. package/README.md +68 -19
  17. package/augmentation.mjs +9 -7
  18. package/backend-api.mjs +11 -2
  19. package/backends/arm64/README.md +104 -0
  20. package/backends/arm64/bin/k-arm64-compile.mjs +101 -0
  21. package/backends/arm64/bin/k-arm64-run.mjs +58 -0
  22. package/backends/arm64/bin/k-arm64.mjs +88 -0
  23. package/backends/arm64/scripts/run-tests.mjs +41 -0
  24. package/backends/arm64/src/arm64.mjs +229 -0
  25. package/backends/arm64/src/cli.mjs +155 -0
  26. package/backends/arm64/src/kvm2arm64.mjs +906 -0
  27. package/backends/arm64/src/runtime.c +728 -0
  28. package/backends/arm64/tests/integration.sh +112 -0
  29. package/backends/arm64/tests/test-arm64.mjs +240 -0
  30. package/backends/llvm/README.md +135 -0
  31. package/backends/llvm/bin/k-llvm-build.mjs +71 -0
  32. package/backends/llvm/bin/k-llvm-compile.mjs +68 -0
  33. package/backends/llvm/bin/k-llvm-jit.mjs +160 -0
  34. package/backends/llvm/bin/k-llvm-run.mjs +90 -0
  35. package/backends/llvm/runtime/krt.c +1508 -0
  36. package/backends/llvm/runtime/krt.h +109 -0
  37. package/backends/llvm/scripts/conformance.mjs +76 -0
  38. package/backends/llvm/scripts/run-tests.mjs +28 -0
  39. package/backends/llvm/src/executable.mjs +562 -0
  40. package/backends/llvm/src/kvm2llvm.mjs +998 -0
  41. package/backends/llvm/src/llvm.mjs +1092 -0
  42. package/backends/llvm/tests/composition-driver.c +22 -0
  43. package/backends/llvm/tests/identity-driver.c +27 -0
  44. package/backends/llvm/tests/integration.sh +153 -0
  45. package/backends/llvm/tests/perf-support.mjs +735 -0
  46. package/backends/llvm/tests/product-driver.c +23 -0
  47. package/backends/llvm/tests/projection-driver.c +19 -0
  48. package/backends/llvm/tests/relation-driver.c +29 -0
  49. package/backends/llvm/tests/test-llvm.mjs +240 -0
  50. package/backends/llvm/tests/union-driver.c +31 -0
  51. package/backends/llvm/tests/variant-driver.c +19 -0
  52. package/backends/llvm/tests/variant-projection-driver.c +21 -0
  53. package/backends/wasm/DOCS/USE-WEB-ASSEMBLY.md +233 -0
  54. package/backends/wasm/README.md +97 -0
  55. package/backends/wasm/TECHNICAL-DEBT.md +60 -0
  56. package/backends/wasm/bin/k-wasm-compile.mjs +50 -0
  57. package/backends/wasm/bin/k-wasm-run.mjs +140 -0
  58. package/backends/wasm/bin/k-wasm.mjs +49 -0
  59. package/backends/wasm/demo_ieee.mjs +319 -0
  60. package/backends/wasm/runtime.wat +102 -0
  61. package/backends/wasm/scripts/run-tests.mjs +41 -0
  62. package/backends/wasm/src/cli.mjs +133 -0
  63. package/backends/wasm/src/kvm2wasm.mjs +661 -0
  64. package/backends/wasm/src/wasm.mjs +1010 -0
  65. package/backends/wasm/tests/integration.sh +182 -0
  66. package/backends/wasm/tests/perf-support.mjs +336 -0
  67. package/backends/wasm/tests/test-wasm.mjs +617 -0
  68. package/browser/repl-browser.mjs +1584 -0
  69. package/browser/shims/assert.mjs +19 -0
  70. package/browser/shims/buffer.mjs +224 -0
  71. package/browser/shims/crypto.mjs +103 -0
  72. package/browser/shims/fs.mjs +159 -0
  73. package/browser/shims/os.mjs +17 -0
  74. package/browser/shims/path.mjs +54 -0
  75. package/browser/shims/process.mjs +44 -0
  76. package/browser/shims/readline.mjs +11 -0
  77. package/browser/shims/url.mjs +12 -0
  78. package/browser/vfs-data.mjs +19 -0
  79. package/codecs/README.md +3 -0
  80. package/codecs/runtime/show-value.mjs +2 -2
  81. package/conformance/helper-reuse/case.json +8 -0
  82. package/conformance/helper-reuse/expected.kv +1 -0
  83. package/conformance/helper-reuse/input.kv +1 -0
  84. package/conformance/helper-reuse/program.k +2 -0
  85. package/conformance/identity/case.json +8 -0
  86. package/conformance/identity/expected.kv +1 -0
  87. package/conformance/identity/input.kv +1 -0
  88. package/conformance/identity/program.k +1 -0
  89. package/conformance/product-projection/case.json +8 -0
  90. package/conformance/product-projection/expected.kv +1 -0
  91. package/conformance/product-projection/input.kv +1 -0
  92. package/conformance/product-projection/program.k +1 -0
  93. package/conformance/run.mjs +149 -0
  94. package/conformance/variant-construction/case.json +8 -0
  95. package/conformance/variant-construction/expected.kv +1 -0
  96. package/conformance/variant-construction/input.kv +1 -0
  97. package/conformance/variant-construction/program.k +1 -0
  98. package/harness/adapters/arm64.mjs +105 -0
  99. package/harness/adapters/kvm.mjs +144 -0
  100. package/harness/adapters/llvm.mjs +118 -0
  101. package/harness/adapters/native.mjs +77 -0
  102. package/harness/adapters/wasm.mjs +169 -0
  103. package/harness/backend.mjs +95 -0
  104. package/harness/cli.mjs +130 -0
  105. package/harness/format.mjs +95 -0
  106. package/harness/index.mjs +25 -0
  107. package/harness/persistent.mjs +121 -0
  108. package/harness/registry.mjs +44 -0
  109. package/harness/suite.mjs +257 -0
  110. package/harness/suites/poly.mjs +156 -0
  111. package/harness/trace.mjs +50 -0
  112. package/harness/util.mjs +92 -0
  113. package/intrinsics.mjs +41 -0
  114. package/kir.mjs +399 -0
  115. package/kvm.mjs +585 -81
  116. package/objects/README.md +37 -3
  117. package/objects/compile.mjs +15 -22
  118. package/objects/inspect.mjs +96 -0
  119. package/objects/validate.mjs +415 -0
  120. package/package.json +51 -19
  121. package/repl-codecs.mjs +105 -6
  122. package/repl.mjs +335 -49
  123. package/run.mjs +2 -7
  124. package/scripts/benchmark.mjs +406 -22
  125. package/scripts/build-repl-html.mjs +1784 -0
  126. package/scripts/perf-ieee.mjs +586 -0
  127. package/scripts/perf-int.mjs +531 -0
  128. package/scripts/perf-poly.mjs +945 -0
  129. package/scripts/render-benchmark.mjs +12 -8
  130. package/scripts/run-tests.mjs +6 -0
  131. package/tests/integration.sh +19 -2
  132. package/tests/test-arithmetics.mjs +54 -0
  133. package/tests/test-ieee-arithmetic.mjs +4 -0
  134. package/tests/test-kir.mjs +85 -0
  135. package/tests/test-kvm-polymorphic.mjs +278 -0
  136. package/tests/test-kvm.mjs +25 -6
  137. package/tests/test-poly.mjs +129 -0
  138. package/tests/test-repl-html.mjs +303 -0
  139. package/tests/test-repl.mjs +144 -1
  140. package/tests/test-validate-object.mjs +40 -0
  141. package/a.klib +0 -1
  142. package/a.kvm +0 -142676
  143. package/a.wasm +0 -0
  144. package/benchmark-results.html +0 -532
  145. package/benchmark-results.json +0 -480
  146. package/k-vscode/.vscode/launch.json +0 -14
  147. package/k-vscode/README.md +0 -75
  148. package/k-vscode/extension.js +0 -273
  149. package/k-vscode/language-configuration.json +0 -42
  150. package/k-vscode/package.json +0 -79
  151. package/k-vscode/snippets/k.json +0 -92
  152. package/k-vscode/syntaxes/k.tmLanguage.json +0 -599
  153. package/k-vscode/test/background-ranges.test.js +0 -50
  154. package/k-vscode/themes/k-contrast-color-theme.json +0 -104
  155. 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 { decodeObject, loadLibrary } from "./object.mjs";
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(typePatternGraph) {
58
- this.typePatternGraph = typePatternGraph;
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.typePatternGraph || !exp.patterns) return null;
73
- const patternId = this.typePatternGraph.find(exp.patterns[index]);
74
- return patternToPropertyList(exportPatternGraph(this.typePatternGraph, patternId));
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 compile(exp, inputReg, builder) {
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.dot,
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.div,
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.vid,
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.startsWith("_")) {
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.comp) {
176
- currentReg = compile(subExp, currentReg, builder);
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, exp: fieldExp } of exp.product) {
184
- const branchBuilder = new KVMBuilder(builder.typePatternGraph);
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 = compile(fieldExp, branchInput, branchBuilder);
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.union) {
207
- const branchBuilder = new KVMBuilder(builder.typePatternGraph);
299
+ for (const branchExp of exp.items) {
300
+ const branchBuilder = new KVMBuilder(builder.patternGraph, builder.codes);
208
301
  const branchInput = "%in";
209
- const branchOutput = compile(branchExp, branchInput, branchBuilder);
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 AST operation: ${exp.op}`);
318
+ throw new Error(`Unsupported KIR operation: ${exp.op}`);
226
319
  }
227
320
  }
228
321
 
229
- export function lowerToKVM(relDef, name, options = {}) {
230
- const typePatternGraph = relDef.typePatternGraph || null;
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.getStaticPattern(relDef.def, 0);
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: relDef.def });
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 = compile(relDef.def, currentReg, builder);
334
+ const bodyOutput = compileKIRExp(kirRel.body, currentReg, builder);
243
335
 
244
336
  let finalReg = bodyOutput;
245
- const outputPattern = builder.getStaticPattern(relDef.def, 1);
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: relDef.def });
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: relDef.typeDerivation?.status === "converged",
350
+ isConverged: kirRel.typeDerivation?.status === "converged",
259
351
  body: builder.instructions
260
352
  };
261
353
  }
262
354
 
263
- function executeBlock(instructions, inputVal, context) {
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 (const inst of instructions) {
268
- const res = executeInstruction(inst, registers, context);
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
- const val = registers.get(inst.src);
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
- for (const branch of inst.branches) {
429
- const branchRes = executeBlock(branch.body, val, context);
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
- if (options.requireConverged && !kvmFunc.isConverged) {
448
- throw new Error(`Cannot run '${kvmFunc.name}' without envelopes: type derivation is not converged`);
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
- const result = executeBlock(kvmFunc.body, inputVal, context);
452
- if (result === undefined) return undefined;
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
- if (options.envelopeFree && kvmFunc.isConverged) {
455
- return withPattern(result, kvmFunc.outputPattern);
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
- executeKVM
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", defs: { rels: object.rels }, main: object.main };
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 defs = programInput.kind === "object"
550
- ? programInput.defs
551
- : annotate(programInput.source, { libraries });
552
- const mainRelName = programInput.kind === "object" ? programInput.main : "__main__";
553
- const mainRel = defs.rels[mainRelName];
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 = lowerToKVM(mainRel, mainRelName);
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: defs.rels,
568
- findCode: codes.find,
1070
+ rels: kir.rels,
1071
+ codes: kir.codes,
1072
+ findCode: kirFindCode(kir),
569
1073
  options: {
570
1074
  envelopeFree
571
1075
  }