@fraczak/k 6.3.2 → 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.
- 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 +76 -14
- package/DOCS/WEB_REPL_REQUIREMENTS.md +135 -0
- package/DOCS/dependency-graph.md +144 -0
- package/Examples/arithmetics.k +51 -48
- 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/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 +60 -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 +102 -0
- package/backends/wasm/scripts/run-tests.mjs +41 -0
- package/backends/wasm/src/cli.mjs +133 -0
- package/backends/wasm/src/kvm2wasm.mjs +661 -0
- package/backends/wasm/src/wasm.mjs +1010 -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 +617 -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/json.mjs +16 -2
- package/codecs/runtime/show-value.mjs +2 -2
- 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 +133 -20
- package/repl.mjs +408 -59
- 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 +6 -2
- 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 +175 -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/harness/util.mjs
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
export function safeDeepEqual(a, b) {
|
|
6
|
+
const stack = [[a, b]];
|
|
7
|
+
while (stack.length > 0) {
|
|
8
|
+
const [x, y] = stack.pop();
|
|
9
|
+
if (x === y) continue;
|
|
10
|
+
if (x == null || y == null) {
|
|
11
|
+
if (x !== y) return false;
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
if (typeof x !== "object" || typeof y !== "object") {
|
|
15
|
+
if (x !== y) return false;
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
// Handle k Value objects
|
|
19
|
+
if (x.type !== undefined || y.type !== undefined) {
|
|
20
|
+
if (x.type !== y.type) return false;
|
|
21
|
+
if (x.tag !== y.tag) return false;
|
|
22
|
+
if (x.value !== undefined || y.value !== undefined) {
|
|
23
|
+
stack.push([x.value, y.value]);
|
|
24
|
+
}
|
|
25
|
+
if (x.product !== undefined || y.product !== undefined) {
|
|
26
|
+
const xp = x.product || {};
|
|
27
|
+
const yp = y.product || {};
|
|
28
|
+
const xk = Object.keys(xp);
|
|
29
|
+
const yk = Object.keys(yp);
|
|
30
|
+
if (xk.length !== yk.length) return false;
|
|
31
|
+
for (const k of xk) {
|
|
32
|
+
if (!Object.hasOwn(yp, k)) return false;
|
|
33
|
+
stack.push([xp[k], yp[k]]);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
// Generic object/record comparison
|
|
39
|
+
const xk = Object.keys(x);
|
|
40
|
+
const yk = Object.keys(y);
|
|
41
|
+
if (xk.length !== yk.length) return false;
|
|
42
|
+
for (const k of xk) {
|
|
43
|
+
if (!Object.hasOwn(y, k)) return false;
|
|
44
|
+
stack.push([x[k], y[k]]);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function toPlainObject(value) {
|
|
51
|
+
return JSON.parse(JSON.stringify(value));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function average(numbers) {
|
|
55
|
+
if (!numbers || numbers.length === 0) return 0;
|
|
56
|
+
return numbers.reduce((sum, n) => sum + n, 0) / numbers.length;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function stdDev(numbers) {
|
|
60
|
+
if (!numbers || numbers.length < 2) return 0;
|
|
61
|
+
const avg = average(numbers);
|
|
62
|
+
const variance = numbers.reduce((sum, n) => sum + Math.pow(n - avg, 2), 0) / (numbers.length - 1);
|
|
63
|
+
return Math.sqrt(variance);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function min(numbers) {
|
|
67
|
+
if (!numbers || numbers.length === 0) return 0;
|
|
68
|
+
return Math.min(...numbers);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function max(numbers) {
|
|
72
|
+
if (!numbers || numbers.length === 0) return 0;
|
|
73
|
+
return Math.max(...numbers);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function formatTiming(result) {
|
|
77
|
+
if (result == null) return "unavailable";
|
|
78
|
+
if (result.status === "overflow") return "stack overflow (no TCO)";
|
|
79
|
+
if (!result.iterationTimes) return "unavailable";
|
|
80
|
+
const samples = result.iterationTimes.map(time => time.toFixed(2)).join(", ");
|
|
81
|
+
return `(${samples}) ~ ${average(result.iterationTimes).toFixed(2)} ms/iteration`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function makeCacheDir(prefix = "k-harness-") {
|
|
85
|
+
if (process.env.K_HARNESS_CACHE_DIR) {
|
|
86
|
+
fs.mkdirSync(process.env.K_HARNESS_CACHE_DIR, { recursive: true });
|
|
87
|
+
return process.env.K_HARNESS_CACHE_DIR;
|
|
88
|
+
}
|
|
89
|
+
const defaultDir = path.join(os.tmpdir(), `${prefix}cache`);
|
|
90
|
+
fs.mkdirSync(defaultDir, { recursive: true });
|
|
91
|
+
return defaultDir;
|
|
92
|
+
}
|
package/intrinsics.mjs
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const intrinsicDefinitions = new Map([
|
|
2
|
+
["_log!", {
|
|
3
|
+
type: "identity",
|
|
4
|
+
targets: new Set(["js"]),
|
|
5
|
+
js(arg) {
|
|
6
|
+
console.error(`_log!: ${arg}`);
|
|
7
|
+
return arg;
|
|
8
|
+
}
|
|
9
|
+
}]
|
|
10
|
+
]);
|
|
11
|
+
|
|
12
|
+
const jsIntrinsicFunctions = Object.freeze(Object.fromEntries(
|
|
13
|
+
[...intrinsicDefinitions]
|
|
14
|
+
.filter(([, intrinsic]) => typeof intrinsic.js === "function")
|
|
15
|
+
.map(([name, intrinsic]) => [name, intrinsic.js])
|
|
16
|
+
));
|
|
17
|
+
|
|
18
|
+
function getIntrinsic(name) {
|
|
19
|
+
return intrinsicDefinitions.get(name) || null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function isIntrinsic(name) {
|
|
23
|
+
return intrinsicDefinitions.has(name);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function isIdentityIntrinsic(name) {
|
|
27
|
+
return getIntrinsic(name)?.type === "identity";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function unsupportedIntrinsic(target, name) {
|
|
31
|
+
return new Error(`${target}: unsupported intrinsic '${name}'`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export {
|
|
35
|
+
intrinsicDefinitions,
|
|
36
|
+
jsIntrinsicFunctions,
|
|
37
|
+
getIntrinsic,
|
|
38
|
+
isIntrinsic,
|
|
39
|
+
isIdentityIntrinsic,
|
|
40
|
+
unsupportedIntrinsic
|
|
41
|
+
};
|
package/kir.mjs
ADDED
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { argv, exit, stdin, stdout } from "node:process";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { compileObject, decodeObject, hydrateObject } from "./object.mjs";
|
|
8
|
+
import { exportPatternGraph } from "./codecs/runtime/codec.mjs";
|
|
9
|
+
import { patternToPropertyList } from "./codecs/runtime/pattern-json.mjs";
|
|
10
|
+
import { propertyListToFilter } from "./codecs/runtime/show-value.mjs";
|
|
11
|
+
|
|
12
|
+
const KIR_FORMAT = "k-ir";
|
|
13
|
+
const KIR_VERSION = 1;
|
|
14
|
+
|
|
15
|
+
const PATTERN_KIND = {
|
|
16
|
+
"(...)": "any",
|
|
17
|
+
"{...}": "open-product",
|
|
18
|
+
"{}": "closed-product",
|
|
19
|
+
"<...>": "open-union",
|
|
20
|
+
"<>": "closed-union",
|
|
21
|
+
"type": "type"
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function stableObject(value) {
|
|
25
|
+
if (Array.isArray(value)) return value.map(stableObject);
|
|
26
|
+
if (!value || typeof value !== "object") return value;
|
|
27
|
+
return Object.fromEntries(
|
|
28
|
+
Object.entries(value)
|
|
29
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
30
|
+
.map(([key, child]) => [key, stableObject(child)])
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function clone(value) {
|
|
35
|
+
return value == null ? value : stableObject(value);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function sourcePatternGraph(graph) {
|
|
39
|
+
if (!graph?.patterns?.nodes || !Array.isArray(graph.patterns.nodes)) {
|
|
40
|
+
throw new Error("KIR-P relation is missing a type-pattern graph");
|
|
41
|
+
}
|
|
42
|
+
return graph;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function findPattern(graph, id) {
|
|
46
|
+
if (typeof graph.find === "function") return graph.find(id);
|
|
47
|
+
const parents = graph.patterns?.parent || [];
|
|
48
|
+
let current = id;
|
|
49
|
+
const seen = new Set();
|
|
50
|
+
while (parents[current] != null) {
|
|
51
|
+
if (seen.has(current)) {
|
|
52
|
+
throw new Error(`KIR-P pattern graph has a parent cycle at node ${id}`);
|
|
53
|
+
}
|
|
54
|
+
seen.add(current);
|
|
55
|
+
current = parents[current];
|
|
56
|
+
}
|
|
57
|
+
return current;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function relationPatternIds(rel) {
|
|
61
|
+
const ids = new Set();
|
|
62
|
+
|
|
63
|
+
function addPatternIds(exp) {
|
|
64
|
+
for (const id of exp?.patterns || []) ids.add(id);
|
|
65
|
+
switch (exp?.op) {
|
|
66
|
+
case "comp":
|
|
67
|
+
exp.comp.forEach(addPatternIds);
|
|
68
|
+
break;
|
|
69
|
+
case "union":
|
|
70
|
+
exp.union.forEach(addPatternIds);
|
|
71
|
+
break;
|
|
72
|
+
case "product":
|
|
73
|
+
exp.product.forEach(({ exp: child }) => addPatternIds(child));
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
addPatternIds(rel.def);
|
|
79
|
+
return ids;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function normalizedPatternGraph(typePatternGraph, rootIds = []) {
|
|
83
|
+
const graph = sourcePatternGraph(typePatternGraph);
|
|
84
|
+
const representatives = new Set();
|
|
85
|
+
const queue = [];
|
|
86
|
+
|
|
87
|
+
const add = (id) => {
|
|
88
|
+
if (id == null) return;
|
|
89
|
+
const rep = findPattern(graph, id);
|
|
90
|
+
if (representatives.has(rep)) return;
|
|
91
|
+
representatives.add(rep);
|
|
92
|
+
queue.push(rep);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
for (const id of rootIds) add(id);
|
|
96
|
+
for (let id = 0; id < graph.patterns.nodes.length; id++) {
|
|
97
|
+
if (findPattern(graph, id) === id) add(id);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
for (let index = 0; index < queue.length; index++) {
|
|
101
|
+
const sourceId = queue[index];
|
|
102
|
+
for (const dests of Object.values(graph.edges?.[sourceId] || {})) {
|
|
103
|
+
for (const dest of dests || []) add(dest);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const orderedSourceIds = [...representatives].sort((a, b) => a - b);
|
|
108
|
+
const sourceToKirId = new Map(orderedSourceIds.map((id, index) => [id, index]));
|
|
109
|
+
const mapPatternId = (id) => sourceToKirId.get(findPattern(graph, id));
|
|
110
|
+
|
|
111
|
+
const nodes = orderedSourceIds.map((sourceId) => {
|
|
112
|
+
const pattern = graph.patterns.nodes[sourceId];
|
|
113
|
+
const kind = PATTERN_KIND[pattern?.pattern];
|
|
114
|
+
if (!kind) throw new Error(`Unsupported KIR-P pattern kind: ${pattern?.pattern}`);
|
|
115
|
+
|
|
116
|
+
const node = {
|
|
117
|
+
id: sourceToKirId.get(sourceId),
|
|
118
|
+
kind
|
|
119
|
+
};
|
|
120
|
+
if (kind === "type") node.code = pattern.type;
|
|
121
|
+
|
|
122
|
+
const edges = Object.entries(graph.edges?.[sourceId] || {})
|
|
123
|
+
.flatMap(([label, dests]) => [...new Set((dests || []).map((dest) => mapPatternId(dest)))]
|
|
124
|
+
.sort((a, b) => a - b)
|
|
125
|
+
.map((target) => ({ label, target })))
|
|
126
|
+
.sort((a, b) => a.label.localeCompare(b.label) || a.target - b.target);
|
|
127
|
+
if (edges.length > 0) node.edges = edges;
|
|
128
|
+
|
|
129
|
+
return node;
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
graph: {
|
|
134
|
+
nodes,
|
|
135
|
+
sourceNodeMap: Object.fromEntries(orderedSourceIds.map((sourceId) => [sourceId, sourceToKirId.get(sourceId)]))
|
|
136
|
+
},
|
|
137
|
+
mapPatternId
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function normalizePatterns(exp, mapPatternId) {
|
|
142
|
+
return exp?.patterns ? { patterns: exp.patterns.map((id) => mapPatternId(id)) } : {};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function normalizeFilter(filter) {
|
|
146
|
+
return clone(filter);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function normalizeExp(exp, mapPatternId) {
|
|
150
|
+
if (!exp) return exp;
|
|
151
|
+
|
|
152
|
+
switch (exp.op) {
|
|
153
|
+
case "identity":
|
|
154
|
+
return { op: "identity", ...normalizePatterns(exp, mapPatternId) };
|
|
155
|
+
case "empty":
|
|
156
|
+
return { op: "empty", ...normalizePatterns(exp, mapPatternId) };
|
|
157
|
+
case "dot":
|
|
158
|
+
return { op: "dot", label: exp.dot, ...normalizePatterns(exp, mapPatternId) };
|
|
159
|
+
case "div":
|
|
160
|
+
return { op: "div", tag: exp.div, ...normalizePatterns(exp, mapPatternId) };
|
|
161
|
+
case "vid":
|
|
162
|
+
return { op: "vid", tag: exp.vid, ...normalizePatterns(exp, mapPatternId) };
|
|
163
|
+
case "code":
|
|
164
|
+
return { op: "code", code: exp.code, ...normalizePatterns(exp, mapPatternId) };
|
|
165
|
+
case "filter":
|
|
166
|
+
return { op: "filter", filter: normalizeFilter(exp.filter), ...normalizePatterns(exp, mapPatternId) };
|
|
167
|
+
case "ref":
|
|
168
|
+
return { op: "ref", ref: exp.ref, ...normalizePatterns(exp, mapPatternId) };
|
|
169
|
+
case "comp":
|
|
170
|
+
return {
|
|
171
|
+
op: "comp",
|
|
172
|
+
items: exp.comp.map((child) => normalizeExp(child, mapPatternId)),
|
|
173
|
+
...normalizePatterns(exp, mapPatternId)
|
|
174
|
+
};
|
|
175
|
+
case "union":
|
|
176
|
+
return {
|
|
177
|
+
op: "union",
|
|
178
|
+
items: exp.union.map((child) => normalizeExp(child, mapPatternId)),
|
|
179
|
+
...normalizePatterns(exp, mapPatternId)
|
|
180
|
+
};
|
|
181
|
+
case "product":
|
|
182
|
+
return {
|
|
183
|
+
op: "product",
|
|
184
|
+
fields: exp.product.map(({ label, exp: child }) => ({
|
|
185
|
+
label,
|
|
186
|
+
expr: normalizeExp(child, mapPatternId)
|
|
187
|
+
})),
|
|
188
|
+
...normalizePatterns(exp, mapPatternId)
|
|
189
|
+
};
|
|
190
|
+
default:
|
|
191
|
+
throw new Error(`Unsupported KIR-P expression op: ${exp.op}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function normalizeRelation(rel) {
|
|
196
|
+
const { graph, mapPatternId } = normalizedPatternGraph(rel.typePatternGraph, relationPatternIds(rel));
|
|
197
|
+
const [inputPattern, outputPattern] = rel.def?.patterns || [];
|
|
198
|
+
return {
|
|
199
|
+
inputPattern: inputPattern == null ? null : mapPatternId(inputPattern),
|
|
200
|
+
outputPattern: outputPattern == null ? null : mapPatternId(outputPattern),
|
|
201
|
+
typeDerivation: { status: rel.typeDerivation?.status || "unknown" },
|
|
202
|
+
patternGraph: graph,
|
|
203
|
+
body: normalizeExp(rel.def, mapPatternId)
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function sortedEntries(object = {}) {
|
|
208
|
+
return Object.entries(object).sort(([a], [b]) => a.localeCompare(b));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function normalizeRelations(rels = {}) {
|
|
212
|
+
return Object.fromEntries(sortedEntries(rels).map(([name, rel]) => [name, normalizeRelation(rel)]));
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function objectToKIRP(object) {
|
|
216
|
+
if (object?.format !== "k-object") {
|
|
217
|
+
throw new Error("KIR-P export requires a k object");
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
format: KIR_FORMAT,
|
|
222
|
+
version: KIR_VERSION,
|
|
223
|
+
layer: "KIR-P",
|
|
224
|
+
sourceFormat: object.format,
|
|
225
|
+
kind: object.main == null ? "library" : "executable",
|
|
226
|
+
main: object.main ?? null,
|
|
227
|
+
codes: clone(object.codes || {}),
|
|
228
|
+
rels: normalizeRelations(object.rels || {}),
|
|
229
|
+
relAlias: clone(object.relAlias || {}),
|
|
230
|
+
compileStats: clone(object.compileStats || {}),
|
|
231
|
+
meta: clone(object.meta || {})
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function resolveRelation(object, relationName = null) {
|
|
236
|
+
const name = relationName || object.main;
|
|
237
|
+
if (!name) throw new Error("KIR retyping requires a relation name");
|
|
238
|
+
if (object.rels?.[name]) return { name, rel: object.rels[name] };
|
|
239
|
+
|
|
240
|
+
const alias = object.relAlias?.[name];
|
|
241
|
+
if (alias && object.rels?.[alias]) return { name: alias, rel: object.rels[alias] };
|
|
242
|
+
if (alias && object.rels?.[name]) return { name, rel: object.rels[name] };
|
|
243
|
+
|
|
244
|
+
throw new Error(`Relation '${name}' not found`);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function relationLibraryWithTarget(object, targetRel) {
|
|
248
|
+
const rels = { ...(object.rels || {}) };
|
|
249
|
+
for (const [alias, hash] of Object.entries(object.relAlias || {})) {
|
|
250
|
+
if (!(hash in rels) && alias in rels) rels[hash] = rels[alias];
|
|
251
|
+
}
|
|
252
|
+
rels.__kir_target__ = targetRel;
|
|
253
|
+
return {
|
|
254
|
+
format: "k-object",
|
|
255
|
+
codes: object.codes || {},
|
|
256
|
+
rels,
|
|
257
|
+
relAlias: {
|
|
258
|
+
...(object.relAlias || {}),
|
|
259
|
+
__kir_target__: "__kir_target__"
|
|
260
|
+
},
|
|
261
|
+
compileStats: object.compileStats || {},
|
|
262
|
+
meta: object.meta || {},
|
|
263
|
+
main: null
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function relationPatternPropertyList(rel, index) {
|
|
268
|
+
return expPatternPropertyList(rel, rel.def, index);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function expPatternPropertyList(rel, exp, index) {
|
|
272
|
+
const patternId = exp?.patterns?.[index];
|
|
273
|
+
if (patternId == null) return null;
|
|
274
|
+
const root = rel.typePatternGraph.find(patternId);
|
|
275
|
+
return patternToPropertyList(exportPatternGraph(rel.typePatternGraph, root));
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export function retypeObjectRelationForBackend(object, relationName, inputPattern, options = {}) {
|
|
279
|
+
if (object?.format !== "k-object") {
|
|
280
|
+
throw new Error("KIR retyping requires a k object");
|
|
281
|
+
}
|
|
282
|
+
if (!Array.isArray(inputPattern)) {
|
|
283
|
+
throw new Error("KIR retyping requires an input pattern property list");
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const target = resolveRelation(object, relationName);
|
|
287
|
+
const source = `?${propertyListToFilter(inputPattern)} __kir_target__`;
|
|
288
|
+
const retypedObject = hydrateObject(compileObject(source, {
|
|
289
|
+
source: options.source || "<kir-retype>",
|
|
290
|
+
libraries: [relationLibraryWithTarget(object, target.rel)]
|
|
291
|
+
}));
|
|
292
|
+
const kir = objectToKIRP(retypedObject);
|
|
293
|
+
const entryName = retypedObject.main || "__main__";
|
|
294
|
+
const retypedRel = retypedObject.rels[entryName];
|
|
295
|
+
const entryInputPattern = relationPatternPropertyList(retypedRel, 0);
|
|
296
|
+
|
|
297
|
+
return {
|
|
298
|
+
relation: relationName || object.main,
|
|
299
|
+
retypedObject,
|
|
300
|
+
kir,
|
|
301
|
+
entryName,
|
|
302
|
+
inputPattern: entryInputPattern,
|
|
303
|
+
outputPattern: relationPatternPropertyList(retypedRel, 1)
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export function retypeObjectRelation(object, relationName, inputPattern, options = {}) {
|
|
308
|
+
return retypeObjectRelationForBackend(object, relationName, inputPattern, options).kir;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export { KIR_FORMAT, KIR_VERSION };
|
|
312
|
+
|
|
313
|
+
export default {
|
|
314
|
+
KIR_FORMAT,
|
|
315
|
+
KIR_VERSION,
|
|
316
|
+
objectToKIRP,
|
|
317
|
+
retypeObjectRelation,
|
|
318
|
+
retypeObjectRelationForBackend
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
function helpText() {
|
|
322
|
+
return [
|
|
323
|
+
"Export the KIR-P JSON view from a k .ko or .klib object.",
|
|
324
|
+
"",
|
|
325
|
+
`Usage: ${argv[1]} [options] [object-file]`,
|
|
326
|
+
"",
|
|
327
|
+
"Arguments:",
|
|
328
|
+
" object-file Input .ko or .klib file. Reads from stdin when omitted.",
|
|
329
|
+
"",
|
|
330
|
+
"Options:",
|
|
331
|
+
" --retype rel Export retyped KIR-P for relation rel.",
|
|
332
|
+
" --input-pattern json Input pattern property-list JSON, or a file containing it.",
|
|
333
|
+
" -h, --help Show this help.",
|
|
334
|
+
"",
|
|
335
|
+
"KIR-P is an inspection/export view; retyping also emits KIR-P."
|
|
336
|
+
].join("\n");
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function usage(stream = console.error) {
|
|
340
|
+
stream(helpText());
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
async function readStdinBytes() {
|
|
344
|
+
const chunks = [];
|
|
345
|
+
for await (const chunk of stdin) {
|
|
346
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
347
|
+
}
|
|
348
|
+
return Buffer.concat(chunks);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function isMainModule() {
|
|
352
|
+
return argv[1] != null && path.resolve(argv[1]) === fileURLToPath(import.meta.url);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
async function main() {
|
|
356
|
+
const args = argv.slice(2);
|
|
357
|
+
if (args.includes("-h") || args.includes("--help")) {
|
|
358
|
+
usage(console.log);
|
|
359
|
+
exit(0);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
let retypeRelation = null;
|
|
363
|
+
let inputPatternArg = null;
|
|
364
|
+
while (args.length > 0 && args[0].startsWith("--")) {
|
|
365
|
+
const option = args.shift();
|
|
366
|
+
if (option === "--retype") {
|
|
367
|
+
retypeRelation = args.shift();
|
|
368
|
+
if (!retypeRelation) throw new Error("--retype requires a relation name");
|
|
369
|
+
} else if (option === "--input-pattern") {
|
|
370
|
+
inputPatternArg = args.shift();
|
|
371
|
+
if (!inputPatternArg) throw new Error("--input-pattern requires JSON or a file path");
|
|
372
|
+
} else {
|
|
373
|
+
throw new Error(`Unknown option: ${option}`);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const inputPath = args.shift() || null;
|
|
378
|
+
if (args.length > 0) {
|
|
379
|
+
throw new Error(`Unexpected argument: ${args[0]}`);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const buffer = inputPath == null ? await readStdinBytes() : fs.readFileSync(inputPath);
|
|
383
|
+
const object = decodeObject(buffer);
|
|
384
|
+
if (retypeRelation != null) {
|
|
385
|
+
if (inputPatternArg == null) throw new Error("--retype requires --input-pattern");
|
|
386
|
+
const inputPatternText = fs.existsSync(inputPatternArg) ? fs.readFileSync(inputPatternArg, "utf8") : inputPatternArg;
|
|
387
|
+
stdout.write(JSON.stringify(retypeObjectRelation(object, retypeRelation, JSON.parse(inputPatternText)), null, 2) + "\n");
|
|
388
|
+
} else {
|
|
389
|
+
stdout.write(JSON.stringify(objectToKIRP(object), null, 2) + "\n");
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
if (isMainModule()) {
|
|
394
|
+
main().catch((error) => {
|
|
395
|
+
console.error(error.message || String(error));
|
|
396
|
+
usage();
|
|
397
|
+
exit(1);
|
|
398
|
+
});
|
|
399
|
+
}
|