@fraczak/k 6.1.2 → 6.2.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/AGENTS.md +4 -4
- package/CLAUDE.md +1 -1
- package/DOCS/BIG_PICTURE.md +10 -10
- package/DOCS/DIVERGENCE_DETECTION.md +1 -1
- package/DOCS/OBJECT_FILE_AND_PATTERN.md +6 -0
- package/DOCS/REPL.md +22 -17
- package/DOCS/TYPE_DERIVATION.md +12 -12
- package/DOCS/book/01-introduction.md +2 -2
- package/DOCS/book/02-syntax-and-values.md +8 -8
- package/DOCS/book/03-types-as-finite-automata.md +2 -2
- package/DOCS/book/04-partial-functions-and-composition.md +2 -2
- package/DOCS/book/05-typing-filters-and-normalization.md +3 -3
- package/DOCS/book/06-values-in-memory.md +3 -3
- package/DOCS/book/07-the-partial-function-abi.md +1 -1
- package/DOCS/book/08-operational-semantics-and-execution.md +1 -1
- package/DOCS/book/09-compiler-architecture.md +2 -2
- package/DOCS/book/appendix-b-incorporating-external-predefined-types-and-functions.md +1 -0
- package/README.md +13 -13
- package/codecs/BINARY_FORMAT.md +1 -1
- package/codecs/POLYMORPHIC_BINARY_FORMAT.md +1 -1
- package/codes.mjs +14 -33
- package/export.mjs +13 -3
- package/k-vscode/README.md +10 -1
- package/k-vscode/extension.js +273 -0
- package/k-vscode/package.json +22 -0
- package/k-vscode/syntaxes/k.tmLanguage.json +493 -137
- package/k-vscode/test/background-ranges.test.js +50 -0
- package/k-vscode/themes/k-contrast-color-theme.json +104 -0
- package/object.mjs +20 -7
- package/package.json +3 -2
- package/repl.mjs +43 -2
- package/scripts/benchmark-idempotency.mjs +72 -0
- package/scripts/run-tests.mjs +54 -0
- package/{Code-derivation-tests → tests/code-derivation}/codec-witness-pattern.mjs +3 -3
- package/{Code-derivation-tests → tests/code-derivation}/convergence-strategy.mjs +1 -1
- package/{Code-derivation-tests → tests/code-derivation}/dot.mjs +1 -1
- package/{Code-derivation-tests → tests/code-derivation}/filter-syntax.mjs +1 -1
- package/{Code-derivation-tests → tests/code-derivation}/index.mjs +1 -1
- package/{Code-derivation-tests → tests/code-derivation}/pattern-envelope.mjs +5 -5
- package/{Code-derivation-tests → tests/code-derivation}/product.mjs +1 -1
- package/{tests.sh → tests/integration.sh} +3 -1
- package/{test-fingerprint.mjs → tests/test-fingerprint.mjs} +3 -3
- package/{test-hash-fuzz.mjs → tests/test-hash-fuzz.mjs} +1 -1
- package/{test-hash-normalization.mjs → tests/test-hash-normalization.mjs} +2 -2
- package/{test-ieee-arithmetic.mjs → tests/test-ieee-arithmetic.mjs} +14 -27
- package/{test-k-object.mjs → tests/test-k-object.mjs} +26 -7
- package/{test-repl.mjs → tests/test-repl.mjs} +22 -4
- package/{test.mjs → tests/test.mjs} +1 -1
- package/.codex +0 -0
- package/DOCS/a.html +0 -398
- package/DOCS/dependency-graph.html +0 -140
- package/DOCS/dependency-graph.md +0 -144
- package/NAT.k~ +0 -452
- package/UU/ta.k +0 -1
- package/UU/ta.k~ +0 -0
- package/UU/tb.k +0 -0
- package/UU/tc.k +0 -0
- package/codecs/BINARY_FORMAT.html +0 -471
- package/codecs/POLYMORPHIC_BINARY_FORMAT.html +0 -355
- package/core.json +0 -1
- package/core.klib +0 -1
- package/core.klib~ +0 -0
- package/ieee.klib +0 -1
- package/k-vscode/package-lock.json +0 -4176
- package/kkk.k +0 -197
- package/kkk.klib +0 -0
- package/kkk.klib~ +0 -0
- package/p.json +0 -5475
- package/type_registry/registry.mjs~ +0 -105
- package/type_registry/typeParser.jison~ +0 -132
- /package/{Code-derivation-tests → tests/code-derivation}/blowup.mjs +0 -0
- /package/{Code-derivation-tests → tests/code-derivation}/bnat.mjs +0 -0
- /package/{Code-derivation-tests → tests/code-derivation}/loop.mjs +0 -0
- /package/{Code-derivation-tests → tests/code-derivation}/nat.mjs +0 -0
- /package/{Code-derivation-tests → tests/code-derivation}/not-not.mjs +0 -0
- /package/{Code-derivation-tests → tests/code-derivation}/rlz.mjs +0 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const assert = require("assert");
|
|
2
|
+
const Module = require("module");
|
|
3
|
+
|
|
4
|
+
const originalLoad = Module._load;
|
|
5
|
+
Module._load = function load(request, parent, isMain) {
|
|
6
|
+
if (request === "vscode") return {};
|
|
7
|
+
return originalLoad.call(this, request, parent, isMain);
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const { collectBackgroundRanges } = require("../extension");
|
|
11
|
+
|
|
12
|
+
function slices(text, ranges) {
|
|
13
|
+
return ranges.map(({ start, end }) => text.slice(start, end));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const source = [
|
|
17
|
+
"$ pair = { leaf left, leaf right };",
|
|
18
|
+
"pick = $pair ?{<$leaf some, ...>=X branch, ...} .branch;",
|
|
19
|
+
"-- ?<ignored tag>",
|
|
20
|
+
"/* $ignored */",
|
|
21
|
+
"$pair pick"
|
|
22
|
+
].join("\n");
|
|
23
|
+
|
|
24
|
+
const ranges = collectBackgroundRanges(source);
|
|
25
|
+
const type = slices(source, ranges.type);
|
|
26
|
+
const filter = slices(source, ranges.filter);
|
|
27
|
+
|
|
28
|
+
assert.deepStrictEqual(type, [
|
|
29
|
+
"$ pair = { leaf left, leaf right };",
|
|
30
|
+
"$pair",
|
|
31
|
+
"$leaf",
|
|
32
|
+
"$pair"
|
|
33
|
+
]);
|
|
34
|
+
assert.deepStrictEqual(filter, ["?{<$leaf some, ...>=X branch, ...}"]);
|
|
35
|
+
|
|
36
|
+
const nested = "add = ?{ $nat x, ... } = X <{.x pred x, .y succ y} ? X add, .y> ;";
|
|
37
|
+
const nestedRanges = collectBackgroundRanges(nested);
|
|
38
|
+
|
|
39
|
+
assert.deepStrictEqual(slices(nested, nestedRanges.filter), [
|
|
40
|
+
"?{ $nat x, ... } = X",
|
|
41
|
+
"? X"
|
|
42
|
+
]);
|
|
43
|
+
assert.deepStrictEqual(slices(nested, nestedRanges.type), ["$nat"]);
|
|
44
|
+
assert.deepStrictEqual(nestedRanges.filter, [
|
|
45
|
+
{ start: 6, end: 26 },
|
|
46
|
+
{ start: 51, end: 54 }
|
|
47
|
+
]);
|
|
48
|
+
assert.deepStrictEqual(nestedRanges.type, [{ start: 9, end: 13 }]);
|
|
49
|
+
|
|
50
|
+
console.log("background range scanner tests passed");
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "vscode://schemas/color-theme",
|
|
3
|
+
"name": "k Contrast",
|
|
4
|
+
"type": "dark",
|
|
5
|
+
"colors": {
|
|
6
|
+
"editor.background": "#161B22",
|
|
7
|
+
"editor.foreground": "#C9D1D9",
|
|
8
|
+
"editorLineNumber.foreground": "#6E7681",
|
|
9
|
+
"editorLineNumber.activeForeground": "#C9D1D9",
|
|
10
|
+
"editor.selectionBackground": "#264F78",
|
|
11
|
+
"editor.inactiveSelectionBackground": "#264F7855"
|
|
12
|
+
},
|
|
13
|
+
"tokenColors": [
|
|
14
|
+
{
|
|
15
|
+
"scope": [
|
|
16
|
+
"comment.block.k",
|
|
17
|
+
"comment.line.k"
|
|
18
|
+
],
|
|
19
|
+
"settings": {
|
|
20
|
+
"foreground": "#8B949E",
|
|
21
|
+
"fontStyle": "italic"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"scope": "entity.name.type.k",
|
|
26
|
+
"settings": {
|
|
27
|
+
"foreground": "#FF7B72",
|
|
28
|
+
"fontStyle": "bold"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"scope": "support.type.k",
|
|
33
|
+
"settings": {
|
|
34
|
+
"foreground": "#FFA657"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"scope": "support.function.identifier.k",
|
|
39
|
+
"settings": {
|
|
40
|
+
"foreground": "#79C0FF"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"scope": "support.type.canonical.k",
|
|
45
|
+
"settings": {
|
|
46
|
+
"foreground": "#F778BA"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"scope": "support.function.canonical.k",
|
|
51
|
+
"settings": {
|
|
52
|
+
"foreground": "#FF967D"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"scope": "entity.name.label.k",
|
|
57
|
+
"settings": {
|
|
58
|
+
"foreground": "#7EE787"
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"scope": "entity.name.tag.k",
|
|
63
|
+
"settings": {
|
|
64
|
+
"foreground": "#F2CC60"
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"scope": "variable.other.pattern.k",
|
|
69
|
+
"settings": {
|
|
70
|
+
"foreground": "#56D4DD"
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"scope": [
|
|
75
|
+
"keyword.operator.assignment.k",
|
|
76
|
+
"keyword.operator.binding.k",
|
|
77
|
+
"keyword.operator.filter.k",
|
|
78
|
+
"keyword.operator.projection.product.k",
|
|
79
|
+
"keyword.operator.projection.union.k",
|
|
80
|
+
"keyword.operator.type.k",
|
|
81
|
+
"keyword.operator.variant.k"
|
|
82
|
+
],
|
|
83
|
+
"settings": {
|
|
84
|
+
"foreground": "#FFDF5D"
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"scope": "punctuation.definition.canonical.k",
|
|
89
|
+
"settings": {
|
|
90
|
+
"foreground": "#B392F0"
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"scope": [
|
|
95
|
+
"constant.language.any.k",
|
|
96
|
+
"string.quoted.double.k",
|
|
97
|
+
"string.quoted.single.k"
|
|
98
|
+
],
|
|
99
|
+
"settings": {
|
|
100
|
+
"foreground": "#A5D6FF"
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
]
|
|
104
|
+
}
|
package/object.mjs
CHANGED
|
@@ -702,21 +702,29 @@ function codeToSource(code, aliases) {
|
|
|
702
702
|
return code.code === "product" ? `{${body}}` : `< ${body} >`;
|
|
703
703
|
}
|
|
704
704
|
|
|
705
|
-
function
|
|
706
|
-
|
|
707
|
-
|
|
705
|
+
function boundaryFilterSources(typePatternGraph, patternIds, aliases) {
|
|
706
|
+
return patterns2filters(typePatternGraph, ...patternIds)
|
|
707
|
+
.map((filter) => prettyRel({
|
|
708
|
+
op: "filter",
|
|
709
|
+
filter: rewriteCodesInFilter(filter, aliases)
|
|
710
|
+
}));
|
|
708
711
|
}
|
|
709
712
|
|
|
710
713
|
function prettyRelation(rel, aliases = {}, relAliases = {}) {
|
|
714
|
+
const [inputFilter, outputFilter] = boundaryFilterSources(
|
|
715
|
+
rel.typePatternGraph,
|
|
716
|
+
[rel.def.patterns[0], rel.def.patterns[1]],
|
|
717
|
+
aliases
|
|
718
|
+
);
|
|
711
719
|
const body = rewriteRefsInExp(
|
|
712
720
|
stripRelationBoundaryFilters(stripDebugFieldsFromExp(rel.def), rel.typePatternGraph),
|
|
713
721
|
aliases,
|
|
714
722
|
relAliases
|
|
715
723
|
);
|
|
716
724
|
return [
|
|
717
|
-
|
|
725
|
+
inputFilter,
|
|
718
726
|
prettyRel(body),
|
|
719
|
-
|
|
727
|
+
outputFilter
|
|
720
728
|
].join(" ");
|
|
721
729
|
}
|
|
722
730
|
|
|
@@ -768,10 +776,15 @@ function decompileObject(object) {
|
|
|
768
776
|
|
|
769
777
|
const mainRel = relAliases[hydrated.main] || hydrated.main;
|
|
770
778
|
const mainRelDef = rels[hydrated.main];
|
|
779
|
+
const [mainInputFilter, mainOutputFilter] = boundaryFilterSources(
|
|
780
|
+
mainRelDef.typePatternGraph,
|
|
781
|
+
[mainRelDef.def.patterns[0], mainRelDef.def.patterns[1]],
|
|
782
|
+
aliases
|
|
783
|
+
);
|
|
771
784
|
const main = [
|
|
772
|
-
|
|
785
|
+
mainInputFilter,
|
|
773
786
|
aliases[mainRel] || mainRel,
|
|
774
|
-
|
|
787
|
+
mainOutputFilter
|
|
775
788
|
].join(" ");
|
|
776
789
|
|
|
777
790
|
return [
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fraczak/k",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.2.0",
|
|
4
4
|
"description": "k — a language for composing first-order partial functions over algebraic data types (products and tagged unions); includes runtime, type derivation, REPL, and codec toolchain",
|
|
5
5
|
"main": "index.mjs",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"prepare": "jiwson -m es6 parser.jison -o parser.mjs; jiwson -m es6 valueParser.jison -o valueParser.mjs; ",
|
|
8
|
-
"test": "node
|
|
8
|
+
"test": "node scripts/run-tests.mjs",
|
|
9
|
+
"perf:idempotency": "node scripts/benchmark-idempotency.mjs",
|
|
9
10
|
"deps:graph": "node scripts/generate-dependency-graph.mjs"
|
|
10
11
|
},
|
|
11
12
|
"repository": {
|
package/repl.mjs
CHANGED
|
@@ -213,6 +213,40 @@ function mergeLibrary(state, lib, source = "<load>", options = {}) {
|
|
|
213
213
|
restoreCodes(state);
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
+
function reachableRelations(rels, roots) {
|
|
217
|
+
const reachable = new Set();
|
|
218
|
+
const queue = [...roots];
|
|
219
|
+
|
|
220
|
+
function walkExp(exp) {
|
|
221
|
+
if (!exp) return;
|
|
222
|
+
switch (exp.op) {
|
|
223
|
+
case "ref":
|
|
224
|
+
if (exp.ref in rels && !reachable.has(exp.ref)) queue.push(exp.ref);
|
|
225
|
+
break;
|
|
226
|
+
case "comp":
|
|
227
|
+
exp.comp.forEach(walkExp);
|
|
228
|
+
break;
|
|
229
|
+
case "union":
|
|
230
|
+
exp.union.forEach(walkExp);
|
|
231
|
+
break;
|
|
232
|
+
case "product":
|
|
233
|
+
exp.product.forEach(({ exp: child }) => walkExp(child));
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
while (queue.length > 0) {
|
|
239
|
+
const hash = queue.shift();
|
|
240
|
+
if (reachable.has(hash) || !(hash in rels)) continue;
|
|
241
|
+
reachable.add(hash);
|
|
242
|
+
walkExp(rels[hash].def);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return Object.fromEntries(
|
|
246
|
+
Object.entries(rels).filter(([hash]) => reachable.has(hash))
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
216
250
|
function savedLibrary(state) {
|
|
217
251
|
const meta = cloneJSON(state.meta);
|
|
218
252
|
const now = new Date().toISOString();
|
|
@@ -226,13 +260,20 @@ function savedLibrary(state) {
|
|
|
226
260
|
if (meta[hash].type == null) meta[hash].type = "rel";
|
|
227
261
|
meta[hash].origins.push({ source: "<repl>", name, compiledAt: now });
|
|
228
262
|
}
|
|
263
|
+
const rels = reachableRelations(state.rels, Object.values(state.relAliases));
|
|
264
|
+
const storedHashes = new Set([
|
|
265
|
+
...Object.keys(state.codes),
|
|
266
|
+
...Object.keys(rels)
|
|
267
|
+
]);
|
|
229
268
|
return {
|
|
230
269
|
format: "k-object",
|
|
231
270
|
codes: state.codes,
|
|
232
|
-
rels
|
|
271
|
+
rels,
|
|
233
272
|
relAlias: state.relAliases,
|
|
234
273
|
compileStats: { sccs: [], sccCount: 0 },
|
|
235
|
-
meta
|
|
274
|
+
meta: Object.fromEntries(
|
|
275
|
+
Object.entries(meta).filter(([hash]) => storedHashes.has(hash))
|
|
276
|
+
),
|
|
236
277
|
main: null
|
|
237
278
|
};
|
|
238
279
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import { mkdtempSync, statSync } from "node:fs";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { performance } from "node:perf_hooks";
|
|
8
|
+
|
|
9
|
+
const DEFAULT_EXAMPLES = [
|
|
10
|
+
"Examples/nat.k",
|
|
11
|
+
"Examples/byte.k",
|
|
12
|
+
"Examples/bnat.k",
|
|
13
|
+
"Examples/arithmetics.k",
|
|
14
|
+
"Examples/ieee.k"
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
const iterations = Number(process.env.K_BENCH_ITERATIONS || 5);
|
|
18
|
+
const examples = process.argv.slice(2);
|
|
19
|
+
const benchmarkExamples = examples.length > 0 ? examples : DEFAULT_EXAMPLES;
|
|
20
|
+
|
|
21
|
+
function run(args) {
|
|
22
|
+
const result = spawnSync("node", args, {
|
|
23
|
+
cwd: process.cwd(),
|
|
24
|
+
encoding: "utf8",
|
|
25
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
26
|
+
});
|
|
27
|
+
if (result.status !== 0) {
|
|
28
|
+
throw new Error(`${args.join(" ")} failed\n${result.stderr || result.stdout}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function median(values) {
|
|
33
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
34
|
+
return sorted[Math.floor(sorted.length / 2)];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function formatMs(value) {
|
|
38
|
+
return `${Math.round(value)} ms`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function formatBytes(value) {
|
|
42
|
+
if (value >= 1024 * 1024) return `${(value / (1024 * 1024)).toFixed(1)} MiB`;
|
|
43
|
+
if (value >= 1024) return `${Math.round(value / 1024)} KiB`;
|
|
44
|
+
return `${value} B`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
console.log(`compile-lib/decompile round-trip benchmark (${iterations} iterations)`);
|
|
48
|
+
console.log("example, median, min, max, object");
|
|
49
|
+
|
|
50
|
+
for (const example of benchmarkExamples) {
|
|
51
|
+
const times = [];
|
|
52
|
+
let size = 0;
|
|
53
|
+
|
|
54
|
+
for (let i = 0; i < iterations; i++) {
|
|
55
|
+
const out = mkdtempSync(join(tmpdir(), `k-idempotency-bench-${example.replaceAll("/", "_")}-`));
|
|
56
|
+
const t0 = performance.now();
|
|
57
|
+
run(["objects/compile-lib.mjs", example, join(out, "1.klib")]);
|
|
58
|
+
run(["objects/decompile.mjs", join(out, "1.klib"), join(out, "1.k")]);
|
|
59
|
+
run(["objects/compile-lib.mjs", join(out, "1.k"), join(out, "2.klib")]);
|
|
60
|
+
run(["objects/decompile.mjs", join(out, "2.klib"), join(out, "2.k")]);
|
|
61
|
+
times.push(performance.now() - t0);
|
|
62
|
+
size = statSync(join(out, "1.klib")).size;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
console.log([
|
|
66
|
+
example,
|
|
67
|
+
formatMs(median(times)),
|
|
68
|
+
formatMs(Math.min(...times)),
|
|
69
|
+
formatMs(Math.max(...times)),
|
|
70
|
+
formatBytes(size)
|
|
71
|
+
].join(", "));
|
|
72
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { spawnSync } from "node:child_process";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
9
|
+
const node = process.execPath;
|
|
10
|
+
const derivationTests = fs.readdirSync(path.join(root, "tests/code-derivation"))
|
|
11
|
+
.filter((name) => name.endsWith(".mjs"))
|
|
12
|
+
.sort()
|
|
13
|
+
.map((name) => [`tests/code-derivation/${name}`, node, [`tests/code-derivation/${name}`]]);
|
|
14
|
+
|
|
15
|
+
const tests = [
|
|
16
|
+
["tests/test.mjs", node, ["tests/test.mjs"]],
|
|
17
|
+
...derivationTests,
|
|
18
|
+
["tests/test-fingerprint.mjs", node, ["tests/test-fingerprint.mjs"]],
|
|
19
|
+
["tests/test-hash-fuzz.mjs", node, ["tests/test-hash-fuzz.mjs"]],
|
|
20
|
+
["tests/test-hash-normalization.mjs", node, ["tests/test-hash-normalization.mjs"]],
|
|
21
|
+
["tests/test-k-object.mjs", node, ["tests/test-k-object.mjs"]],
|
|
22
|
+
["tests/test-repl.mjs", node, ["tests/test-repl.mjs"]],
|
|
23
|
+
["tests/test-ieee-arithmetic.mjs", node, ["tests/test-ieee-arithmetic.mjs"]],
|
|
24
|
+
["tests/integration.sh", path.join(root, "tests/integration.sh"), []]
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
function formatDuration(milliseconds) {
|
|
28
|
+
if (milliseconds < 1000) return `${milliseconds.toFixed(0)} ms`;
|
|
29
|
+
return `${(milliseconds / 1000).toFixed(2)} s`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const suiteStarted = performance.now();
|
|
33
|
+
for (const [name, command, args] of tests) {
|
|
34
|
+
console.log(`\n==> ${name}`);
|
|
35
|
+
const started = performance.now();
|
|
36
|
+
const result = spawnSync(command, args, {
|
|
37
|
+
cwd: root,
|
|
38
|
+
stdio: "inherit"
|
|
39
|
+
});
|
|
40
|
+
const duration = formatDuration(performance.now() - started);
|
|
41
|
+
|
|
42
|
+
if (result.error) {
|
|
43
|
+
console.error(`<== ${name} FAILED (${duration})`);
|
|
44
|
+
console.error(result.error.message);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
if (result.status !== 0) {
|
|
48
|
+
console.error(`<== ${name} FAILED (${duration})`);
|
|
49
|
+
process.exit(result.status ?? 1);
|
|
50
|
+
}
|
|
51
|
+
console.log(`<== ${name} passed (${duration})`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
console.log(`\nAll tests passed (${formatDuration(performance.now() - suiteStarted)})`);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from 'assert';
|
|
2
|
-
import { parseValue } from '
|
|
3
|
-
import { encodeToWire, decodeWire } from '
|
|
4
|
-
import { Product, Variant } from '
|
|
2
|
+
import { parseValue } from '../../valueIO.mjs';
|
|
3
|
+
import { encodeToWire, decodeWire } from '../../codecs/runtime/prefix-codec.mjs';
|
|
4
|
+
import { Product, Variant } from '../../Value.mjs';
|
|
5
5
|
|
|
6
6
|
const value = parseValue('{a:{b:x,c:{}}}');
|
|
7
7
|
const repeatedClosedValue = parseValue('{a:{m:{},n:{}},b:{m:{},n:{}}}');
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
|
-
import k from "
|
|
3
|
-
import { Product, Variant, mergePatterns, withPattern } from "
|
|
4
|
-
import { decodeWire, encodeToWire } from "
|
|
5
|
-
import run from "
|
|
6
|
-
import codes from "
|
|
2
|
+
import k from "../../index.mjs";
|
|
3
|
+
import { Product, Variant, mergePatterns, withPattern } from "../../Value.mjs";
|
|
4
|
+
import { decodeWire, encodeToWire } from "../../codecs/runtime/prefix-codec.mjs";
|
|
5
|
+
import run from "../../run.mjs";
|
|
6
|
+
import codes from "../../codes.mjs";
|
|
7
7
|
|
|
8
8
|
const INT_PATTERN = [
|
|
9
9
|
["closed-union", [["+", 1], ["-", 1]]],
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
1
3
|
|
|
2
4
|
# This script is used to run the k.mjs interpreter with various test cases.
|
|
3
5
|
|
|
4
|
-
echo "Running './tests.sh' ..."
|
|
6
|
+
echo "Running './tests/integration.sh' ..."
|
|
5
7
|
|
|
6
8
|
echo '["zebara", "ela", "kupa", ala, owca]' | node ./codecs/k-parse.mjs | ./k.mjs '{.1 0,.3 1}' | node ./codecs/k-print.mjs
|
|
7
9
|
echo
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import k from '
|
|
2
|
-
import { encodeCodeToString, find } from '
|
|
3
|
-
import hash from '
|
|
1
|
+
import k from '../index.mjs';
|
|
2
|
+
import { encodeCodeToString, find } from '../codes.mjs';
|
|
3
|
+
import hash from '../hash.mjs';
|
|
4
4
|
[
|
|
5
5
|
`
|
|
6
6
|
$bits = < bits 0, bits 1, {} _ >;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import assert from "node:assert/strict";
|
|
4
|
-
import { annotate } from "
|
|
5
|
-
import { hash } from "
|
|
4
|
+
import { annotate } from "../index.mjs";
|
|
5
|
+
import { hash } from "../hash.mjs";
|
|
6
6
|
|
|
7
7
|
const nameRegex = /^[a-zA-Z0-9_+][a-zA-Z0-9_?!+]*$/;
|
|
8
8
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
|
|
3
|
-
import { createState, evaluateInput } from "
|
|
4
|
-
import { Product } from "
|
|
5
|
-
import codes from "
|
|
6
|
-
import { parse as parseFloat64, print as printFloat64 } from "
|
|
7
|
-
import { singletonPatternToCodeHash, valueForCode } from "
|
|
3
|
+
import { createState, evaluateInput } from "../repl.mjs";
|
|
4
|
+
import { Product } from "../Value.mjs";
|
|
5
|
+
import codes from "../codes.mjs";
|
|
6
|
+
import { parse as parseFloat64, print as printFloat64 } from "../codecs/ieee.mjs";
|
|
7
|
+
import { singletonPatternToCodeHash, valueForCode } from "../repl-codecs.mjs";
|
|
8
8
|
|
|
9
9
|
const state = createState();
|
|
10
10
|
await evaluateInput(":load Examples/ieee.k", state);
|
|
@@ -44,7 +44,7 @@ function assertFloatResult(value, expected, label) {
|
|
|
44
44
|
assert.equal(printFloat64(value), expected, label);
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
async function assertOperation({ op, x, y, result, flags = null }) {
|
|
47
|
+
async function assertOperation({ op, x, y, result, flags = null, projection = false }) {
|
|
48
48
|
const bundle = await runOperation(op, x, y);
|
|
49
49
|
assert.ok(bundle instanceof Product, `${op} should return a result bundle`);
|
|
50
50
|
assert.deepEqual(Object.keys(bundle.product).sort(), ["flags", "result"]);
|
|
@@ -53,37 +53,24 @@ async function assertOperation({ op, x, y, result, flags = null }) {
|
|
|
53
53
|
assert.equal(bundle.product.flags.tag, flags, `${op}(${x}, ${y}).flags`);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
if (projection) {
|
|
57
|
+
const projected = await runProjectedResult(op, x, y);
|
|
58
|
+
assertFloatResult(projected, result, `${op}(${x}, ${y}).result projection`);
|
|
59
|
+
}
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
const cases = [
|
|
61
|
-
//
|
|
62
|
-
{ op: "add", x: "
|
|
63
|
-
{ op: "add", x: "-1", y: "1", result: "0", flags: "none" },
|
|
64
|
-
{ op: "add", x: "0.5", y: "0.25", result: "0.75", flags: "none" },
|
|
63
|
+
// Keep this as a fast smoke matrix. Examples/ieee.k has detailed relation-level coverage.
|
|
64
|
+
{ op: "add", x: "0.5", y: "0.25", result: "0.75", flags: "none", projection: true },
|
|
65
65
|
{ op: "add", x: "Infinity", y: "-Infinity", result: "NaN", flags: "invalid" },
|
|
66
|
-
{ op: "add", x: "NaN", y: "1", result: "NaN", flags: "none" },
|
|
67
66
|
|
|
68
|
-
//
|
|
69
|
-
{ op: "sub", x: "4", y: "2", result: "2", flags: "none" },
|
|
67
|
+
// Cover each finite arithmetic implementation, including sign and fractional results.
|
|
70
68
|
{ op: "sub", x: "2", y: "4", result: "-2", flags: "none" },
|
|
71
|
-
{ op: "sub", x: "0", y: "0", result: "0", flags: "none" },
|
|
72
|
-
{ op: "sub", x: "Infinity", y: "Infinity", result: "NaN", flags: "invalid" },
|
|
73
|
-
|
|
74
|
-
// Multiplication: positive, signed, zero, and invalid infinity-by-zero.
|
|
75
|
-
{ op: "mul", x: "4", y: "2", result: "8", flags: "none" },
|
|
76
69
|
{ op: "mul", x: "-2", y: "4", result: "-8", flags: "none" },
|
|
77
|
-
{ op: "mul", x: "0", y: "5", result: "0", flags: "none" },
|
|
78
70
|
{ op: "mul", x: "Infinity", y: "0", result: "NaN", flags: "invalid" },
|
|
79
|
-
|
|
80
|
-
// Division: normalized exact cases, fractional quotient, divide-by-zero, invalid zero-over-zero, and infinity quotient.
|
|
81
|
-
{ op: "div", x: "4", y: "2", result: "2" },
|
|
82
|
-
{ op: "div", x: "1", y: "1", result: "1" },
|
|
83
71
|
{ op: "div", x: "1", y: "2", result: "0.5" },
|
|
84
72
|
{ op: "div", x: "1", y: "0", result: "Infinity", flags: "div_by_zero" },
|
|
85
|
-
{ op: "div", x: "0", y: "0", result: "NaN", flags: "invalid" }
|
|
86
|
-
{ op: "div", x: "Infinity", y: "2", result: "Infinity", flags: "none" }
|
|
73
|
+
{ op: "div", x: "0", y: "0", result: "NaN", flags: "invalid" }
|
|
87
74
|
];
|
|
88
75
|
|
|
89
76
|
for (const testCase of cases) {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import fs from "node:fs";
|
|
3
|
-
import k from "
|
|
4
|
-
import { parseValue } from "
|
|
5
|
-
import { decodeWire, encodeToWire, CORE_PATTERN_PROPERTY_LIST } from "
|
|
6
|
-
import { Product, Variant } from "
|
|
7
|
-
import { exportPatternGraph } from "
|
|
8
|
-
import { patternToPropertyList } from "
|
|
3
|
+
import k from "../index.mjs";
|
|
4
|
+
import { parseValue } from "../valueIO.mjs";
|
|
5
|
+
import { decodeWire, encodeToWire, CORE_PATTERN_PROPERTY_LIST } from "../codecs/runtime/prefix-codec.mjs";
|
|
6
|
+
import { Product, Variant } from "../Value.mjs";
|
|
7
|
+
import { exportPatternGraph } from "../codecs/runtime/codec.mjs";
|
|
8
|
+
import { patternToPropertyList } from "../codecs/runtime/pattern-json.mjs";
|
|
9
9
|
import {
|
|
10
10
|
compileObjectBuffer,
|
|
11
11
|
compileLibraryBuffer,
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
runConvergedObject,
|
|
15
15
|
decompileObjectBuffer,
|
|
16
16
|
extractAliasesFromObject
|
|
17
|
-
} from "
|
|
17
|
+
} from "../object.mjs";
|
|
18
18
|
|
|
19
19
|
const value = parseValue("{a:{b:x,c:{}}}");
|
|
20
20
|
const wire = encodeToWire(value, null);
|
|
@@ -91,6 +91,25 @@ assert.match(decompiledSource, /\$ Nws3 =/);
|
|
|
91
91
|
assert.match(decompiledSource, /^7jfi = /m);
|
|
92
92
|
assert.match(decompiledSource, /^----- main -----\n\?\(\.\.\.\) 7jfi \$Nws3$/m);
|
|
93
93
|
|
|
94
|
+
const natLibraryBuffer = compileLibraryBuffer(fs.readFileSync("Examples/nat.k", "utf8"), { source: "Examples/nat.k" });
|
|
95
|
+
const natDecompiledSource = decompileObjectBuffer(natLibraryBuffer);
|
|
96
|
+
const natRoundTripSource = decompileObjectBuffer(
|
|
97
|
+
compileLibraryBuffer(natDecompiledSource, { source: "Examples/nat.decompiled.k" })
|
|
98
|
+
);
|
|
99
|
+
const recursiveNatPattern = /\$dTww <\{\.x PgmQ x, \.y h4Wg y\} re64, \.y> \$e9WP;/;
|
|
100
|
+
assert.equal(natRoundTripSource, natDecompiledSource);
|
|
101
|
+
assert.match(natDecompiledSource, recursiveNatPattern);
|
|
102
|
+
|
|
103
|
+
const arithmeticsLibraryBuffer = compileLibraryBuffer(
|
|
104
|
+
fs.readFileSync("Examples/arithmetics.k", "utf8"),
|
|
105
|
+
{ source: "Examples/arithmetics.k" }
|
|
106
|
+
);
|
|
107
|
+
const arithmeticsDecompiledSource = decompileObjectBuffer(arithmeticsLibraryBuffer);
|
|
108
|
+
const arithmeticsRoundTripSource = decompileObjectBuffer(
|
|
109
|
+
compileLibraryBuffer(arithmeticsDecompiledSource, { source: "Examples/arithmetics.decompiled.k" })
|
|
110
|
+
);
|
|
111
|
+
assert.equal(arithmeticsRoundTripSource, arithmeticsDecompiledSource);
|
|
112
|
+
|
|
94
113
|
assert.throws(() => k.compile("@A = (); @A"), /Parse error/);
|
|
95
114
|
assert.throws(() => k.compile("$ @A = {}; $@A"), /Parse error/);
|
|
96
115
|
assert.deepEqual(k.compile("7jfi = ?X0; 7jfi")(new Product({})).toJSON(), {});
|