@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.
Files changed (76) hide show
  1. package/AGENTS.md +4 -4
  2. package/CLAUDE.md +1 -1
  3. package/DOCS/BIG_PICTURE.md +10 -10
  4. package/DOCS/DIVERGENCE_DETECTION.md +1 -1
  5. package/DOCS/OBJECT_FILE_AND_PATTERN.md +6 -0
  6. package/DOCS/REPL.md +22 -17
  7. package/DOCS/TYPE_DERIVATION.md +12 -12
  8. package/DOCS/book/01-introduction.md +2 -2
  9. package/DOCS/book/02-syntax-and-values.md +8 -8
  10. package/DOCS/book/03-types-as-finite-automata.md +2 -2
  11. package/DOCS/book/04-partial-functions-and-composition.md +2 -2
  12. package/DOCS/book/05-typing-filters-and-normalization.md +3 -3
  13. package/DOCS/book/06-values-in-memory.md +3 -3
  14. package/DOCS/book/07-the-partial-function-abi.md +1 -1
  15. package/DOCS/book/08-operational-semantics-and-execution.md +1 -1
  16. package/DOCS/book/09-compiler-architecture.md +2 -2
  17. package/DOCS/book/appendix-b-incorporating-external-predefined-types-and-functions.md +1 -0
  18. package/README.md +13 -13
  19. package/codecs/BINARY_FORMAT.md +1 -1
  20. package/codecs/POLYMORPHIC_BINARY_FORMAT.md +1 -1
  21. package/codes.mjs +14 -33
  22. package/export.mjs +13 -3
  23. package/k-vscode/README.md +10 -1
  24. package/k-vscode/extension.js +273 -0
  25. package/k-vscode/package.json +22 -0
  26. package/k-vscode/syntaxes/k.tmLanguage.json +493 -137
  27. package/k-vscode/test/background-ranges.test.js +50 -0
  28. package/k-vscode/themes/k-contrast-color-theme.json +104 -0
  29. package/object.mjs +20 -7
  30. package/package.json +3 -2
  31. package/repl.mjs +43 -2
  32. package/scripts/benchmark-idempotency.mjs +72 -0
  33. package/scripts/run-tests.mjs +54 -0
  34. package/{Code-derivation-tests → tests/code-derivation}/codec-witness-pattern.mjs +3 -3
  35. package/{Code-derivation-tests → tests/code-derivation}/convergence-strategy.mjs +1 -1
  36. package/{Code-derivation-tests → tests/code-derivation}/dot.mjs +1 -1
  37. package/{Code-derivation-tests → tests/code-derivation}/filter-syntax.mjs +1 -1
  38. package/{Code-derivation-tests → tests/code-derivation}/index.mjs +1 -1
  39. package/{Code-derivation-tests → tests/code-derivation}/pattern-envelope.mjs +5 -5
  40. package/{Code-derivation-tests → tests/code-derivation}/product.mjs +1 -1
  41. package/{tests.sh → tests/integration.sh} +3 -1
  42. package/{test-fingerprint.mjs → tests/test-fingerprint.mjs} +3 -3
  43. package/{test-hash-fuzz.mjs → tests/test-hash-fuzz.mjs} +1 -1
  44. package/{test-hash-normalization.mjs → tests/test-hash-normalization.mjs} +2 -2
  45. package/{test-ieee-arithmetic.mjs → tests/test-ieee-arithmetic.mjs} +14 -27
  46. package/{test-k-object.mjs → tests/test-k-object.mjs} +26 -7
  47. package/{test-repl.mjs → tests/test-repl.mjs} +22 -4
  48. package/{test.mjs → tests/test.mjs} +1 -1
  49. package/.codex +0 -0
  50. package/DOCS/a.html +0 -398
  51. package/DOCS/dependency-graph.html +0 -140
  52. package/DOCS/dependency-graph.md +0 -144
  53. package/NAT.k~ +0 -452
  54. package/UU/ta.k +0 -1
  55. package/UU/ta.k~ +0 -0
  56. package/UU/tb.k +0 -0
  57. package/UU/tc.k +0 -0
  58. package/codecs/BINARY_FORMAT.html +0 -471
  59. package/codecs/POLYMORPHIC_BINARY_FORMAT.html +0 -355
  60. package/core.json +0 -1
  61. package/core.klib +0 -1
  62. package/core.klib~ +0 -0
  63. package/ieee.klib +0 -1
  64. package/k-vscode/package-lock.json +0 -4176
  65. package/kkk.k +0 -197
  66. package/kkk.klib +0 -0
  67. package/kkk.klib~ +0 -0
  68. package/p.json +0 -5475
  69. package/type_registry/registry.mjs~ +0 -105
  70. package/type_registry/typeParser.jison~ +0 -132
  71. /package/{Code-derivation-tests → tests/code-derivation}/blowup.mjs +0 -0
  72. /package/{Code-derivation-tests → tests/code-derivation}/bnat.mjs +0 -0
  73. /package/{Code-derivation-tests → tests/code-derivation}/loop.mjs +0 -0
  74. /package/{Code-derivation-tests → tests/code-derivation}/nat.mjs +0 -0
  75. /package/{Code-derivation-tests → tests/code-derivation}/not-not.mjs +0 -0
  76. /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 boundaryFilterSource(typePatternGraph, patternId, aliases) {
706
- const [filter] = patterns2filters(typePatternGraph, patternId);
707
- return prettyRel({ op: "filter", filter: rewriteCodesInFilter(filter, aliases) });
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
- boundaryFilterSource(rel.typePatternGraph, rel.def.patterns[0], aliases),
725
+ inputFilter,
718
726
  prettyRel(body),
719
- boundaryFilterSource(rel.typePatternGraph, rel.def.patterns[1], aliases)
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
- boundaryFilterSource(mainRelDef.typePatternGraph, mainRelDef.def.patterns[0], aliases),
785
+ mainInputFilter,
773
786
  aliases[mainRel] || mainRel,
774
- boundaryFilterSource(mainRelDef.typePatternGraph, mainRelDef.def.patterns[1], aliases)
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.1.2",
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 test.mjs; for f in Code-derivation-tests/*.mjs; do echo $f; node $f; done; node test-fingerprint.mjs; node test-hash-fuzz.mjs; node test-hash-normalization.mjs; node test-k-object.mjs; node test-repl.mjs; node test-ieee-arithmetic.mjs; ./tests.sh",
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: state.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 '../valueIO.mjs';
3
- import { encodeToWire, decodeWire } from '../codecs/runtime/prefix-codec.mjs';
4
- import { Product, Variant } from '../Value.mjs';
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,5 +1,5 @@
1
1
  import assert from "assert";
2
- import { annotate } from "../index.mjs";
2
+ import { annotate } from "../../index.mjs";
3
3
 
4
4
  const script = `
5
5
  f = .x;
@@ -1,5 +1,5 @@
1
1
  import { t, in_out } from './index.mjs';
2
- import { hash } from '../hash.mjs';
2
+ import { hash } from '../../hash.mjs';
3
3
  import assert from 'assert';
4
4
 
5
5
  t(`
@@ -1,5 +1,5 @@
1
1
  import assert from 'assert';
2
- import { annotate } from '../index.mjs';
2
+ import { annotate } from '../../index.mjs';
3
3
 
4
4
  function succeeds(script) {
5
5
  assert.doesNotThrow(() => annotate(script));
@@ -1,4 +1,4 @@
1
- import { annotate } from "../index.mjs";
1
+ import { annotate } from "../../index.mjs";
2
2
 
3
3
  function in_out(annotated) {
4
4
  const [input,output] = annotated.rels.__main__.def.patterns.map( (pat) =>
@@ -1,9 +1,9 @@
1
1
  import assert from "node:assert/strict";
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";
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,6 +1,6 @@
1
1
  import { t, in_out } from './index.mjs';
2
2
  import assert from 'assert';
3
- import hash from "../hash.mjs";
3
+ import hash from "../../hash.mjs";
4
4
 
5
5
  const unitCode = hash('$C0={};');
6
6
 
@@ -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 './index.mjs';
2
- import { encodeCodeToString, find } from './codes.mjs';
3
- import hash from './hash.mjs';
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,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import assert from "node:assert/strict";
4
- import { annotate } from "./index.mjs";
4
+ import { annotate } from "../index.mjs";
5
5
 
6
6
  function mulberry32(seed) {
7
7
  let t = seed >>> 0;
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import assert from "node:assert/strict";
4
- import { annotate } from "./index.mjs";
5
- import { hash } from "./hash.mjs";
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 "./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";
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
- const projected = await runProjectedResult(op, x, y);
57
- assertFloatResult(projected, result, `${op}(${x}, ${y}).result projection`);
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
- // Addition: same sign, cancellation, exact fractional add, and specials.
62
- { op: "add", x: "1", y: "1", result: "2", flags: "none" },
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
- // Subtraction: positive result, negative result, signed-zero-equivalent, and invalid infinity subtraction.
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 "./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";
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 "./object.mjs";
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(), {});