@fraczak/k 5.1.2 → 6.0.1

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 (115) hide show
  1. package/.codex +0 -0
  2. package/Code-derivation-tests/blowup.mjs +3 -3
  3. package/Code-derivation-tests/bnat.mjs +2 -2
  4. package/Code-derivation-tests/codec-witness-pattern.mjs +50 -0
  5. package/Code-derivation-tests/convergence-strategy.mjs +24 -0
  6. package/Code-derivation-tests/dot.mjs +12 -9
  7. package/Code-derivation-tests/filter-syntax.mjs +21 -0
  8. package/Code-derivation-tests/loop.mjs +8 -2
  9. package/Code-derivation-tests/nat.mjs +1 -1
  10. package/Code-derivation-tests/not-not.mjs +3 -3
  11. package/Code-derivation-tests/pattern-envelope.mjs +68 -0
  12. package/Code-derivation-tests/rlz.mjs +2 -2
  13. package/{BIG_PICTURE.md → DOCS/BIG_PICTURE.md} +103 -50
  14. package/DOCS/REPL.md +191 -0
  15. package/DOCS/a.html +398 -0
  16. package/DOCS/book/01-introduction.md +51 -0
  17. package/DOCS/book/02-syntax-and-values.md +111 -0
  18. package/DOCS/book/03-types-as-finite-automata.md +156 -0
  19. package/DOCS/book/04-partial-functions-and-composition.md +146 -0
  20. package/DOCS/book/05-typing-filters-and-normalization.md +111 -0
  21. package/DOCS/book-combined.html +3798 -0
  22. package/DOCS/book-combined.md +3745 -0
  23. package/DOCS/book-combined.pdf +0 -0
  24. package/DOCS/book-combined.tex +5682 -0
  25. package/DOCS/book.html +65 -0
  26. package/DOCS/book.md +54 -0
  27. package/DOCS/book.pdf +0 -0
  28. package/DOCS/book.tex +150 -0
  29. package/NAT.k~ +452 -0
  30. package/README.md +204 -508
  31. package/TypeGraph.mjs +43 -0
  32. package/TypePatternGraph.mjs +251 -0
  33. package/Value.mjs +192 -0
  34. package/augmentation.mjs +198 -0
  35. package/codecs/BINARY_FORMAT.html +471 -0
  36. package/codecs/BINARY_FORMAT.md +53 -0
  37. package/codecs/POLYMORPHIC_BINARY_FORMAT.html +355 -0
  38. package/codecs/POLYMORPHIC_BINARY_FORMAT.md +303 -0
  39. package/codecs/README.md +215 -0
  40. package/codecs/example-pipeline.mjs +149 -0
  41. package/codecs/ieee.mjs +140 -0
  42. package/codecs/int.mjs +104 -0
  43. package/codecs/json-codec.mjs +215 -0
  44. package/codecs/json.mjs +40 -0
  45. package/codecs/k-parse.mjs +90 -0
  46. package/codecs/k-pattern.mjs +75 -0
  47. package/codecs/k-print.mjs +46 -0
  48. package/codecs/runtime/codec.mjs +768 -0
  49. package/codecs/runtime/ieee-pattern.mjs +30 -0
  50. package/codecs/runtime/pattern-json.mjs +96 -0
  51. package/codecs/runtime/prefix-codec.mjs +425 -0
  52. package/codecs/runtime/show-value.mjs +68 -0
  53. package/codecs/runtime/typeFromValue.mjs +34 -0
  54. package/codecs/runtime/unicode-string.mjs +220 -0
  55. package/codecs/show.mjs +41 -0
  56. package/codecs/string-codec.mjs +80 -0
  57. package/codecs/unit.mjs +51 -0
  58. package/codecs/utf16.mjs +66 -0
  59. package/codecs/utf8.mjs +37 -0
  60. package/codes.mjs +146 -44
  61. package/compiler.mjs +111 -0
  62. package/convergence.mjs +197 -0
  63. package/core.k +197 -0
  64. package/export.mjs +22 -32
  65. package/hash.mjs +29 -31
  66. package/ieee.klib +0 -0
  67. package/index.mjs +41 -15
  68. package/k-vscode/.vscode/launch.json +14 -0
  69. package/k-vscode/README.md +65 -0
  70. package/k-vscode/language-configuration.json +42 -0
  71. package/k-vscode/package-lock.json +4176 -0
  72. package/k-vscode/package.json +57 -0
  73. package/k-vscode/snippets/k.json +92 -0
  74. package/k-vscode/syntaxes/k.tmLanguage.json +243 -0
  75. package/k.mjs +56 -54
  76. package/object.mjs +517 -0
  77. package/objects/README.md +38 -0
  78. package/objects/compile-lib.mjs +42 -0
  79. package/objects/compile.mjs +42 -0
  80. package/objects/decompile.mjs +32 -0
  81. package/package.json +13 -6
  82. package/parser.jison +27 -46
  83. package/parser.mjs +68 -100
  84. package/pretty.mjs +21 -40
  85. package/repl2.mjs +662 -0
  86. package/run.mjs +264 -231
  87. package/symbol-table.mjs +3 -17
  88. package/test-fingerprint.mjs +7 -7
  89. package/test-hash-fuzz.mjs +107 -0
  90. package/test-hash-normalization.mjs +134 -0
  91. package/test-k-object.mjs +74 -0
  92. package/test-repl2.mjs +106 -0
  93. package/test.mjs +45 -70
  94. package/tests.sh +10 -1
  95. package/type_registry/registry.mjs~ +105 -0
  96. package/type_registry/typeParser.jison~ +132 -0
  97. package/typing.mjs +129 -676
  98. package/unification.mjs +157 -0
  99. package/valueIO.mjs +54 -0
  100. package/valueParser.jison +143 -0
  101. package/valueParser.mjs +713 -0
  102. package/Code-derivation-tests/built-in.mjs +0 -19
  103. package/Code-derivation-tests/vector.mjs +0 -19
  104. package/Filters/bug.k +0 -3
  105. package/Filters/composing.k +0 -15
  106. package/Filters/filter.k +0 -9
  107. package/TODO.md +0 -53
  108. package/TYPING.md +0 -365
  109. package/k-repl.cast +0 -1913
  110. package/k.cast +0 -618
  111. package/my-objects.json +0 -6
  112. package/patterns.mjs +0 -543
  113. package/repl.mjs +0 -213
  114. package/test-filters.mjs +0 -45
  115. package/typing-test.mjs +0 -40
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env node
2
+
3
+ import assert from "node:assert/strict";
4
+ import { annotate } from "./index.mjs";
5
+
6
+ function mulberry32(seed) {
7
+ let t = seed >>> 0;
8
+ return function () {
9
+ t += 0x6d2b79f5;
10
+ let r = Math.imul(t ^ (t >>> 15), 1 | t);
11
+ r ^= r + Math.imul(r ^ (r >>> 7), 61 | r);
12
+ return ((r ^ (r >>> 14)) >>> 0) / 4294967296;
13
+ };
14
+ }
15
+
16
+ function randInt(rng, max) {
17
+ return Math.floor(rng() * max);
18
+ }
19
+
20
+ function permute(rng, arr) {
21
+ const out = [...arr];
22
+ for (let i = out.length - 1; i > 0; i--) {
23
+ const j = randInt(rng, i + 1);
24
+ [out[i], out[j]] = [out[j], out[i]];
25
+ }
26
+ return out;
27
+ }
28
+
29
+ function renameScript(script, mapping) {
30
+ let out = script;
31
+ const temps = {};
32
+ let i = 0;
33
+ for (const from of Object.keys(mapping)) {
34
+ temps[from] = `__TMP_${i++}__`;
35
+ }
36
+ for (const [from, tmp] of Object.entries(temps)) {
37
+ const re = new RegExp(`\\b${from}\\b`, "g");
38
+ out = out.replace(re, tmp);
39
+ }
40
+ for (const [from, to] of Object.entries(mapping)) {
41
+ const re = new RegExp(`\\b${temps[from]}\\b`, "g");
42
+ out = out.replace(re, to);
43
+ }
44
+ return out;
45
+ }
46
+
47
+ function getRelHash(script, name) {
48
+ const { relAlias } = annotate(script);
49
+ assert.ok(relAlias[name], `missing relAlias for ${name}`);
50
+ return relAlias[name];
51
+ }
52
+
53
+ function generateScript(rng, count) {
54
+ const names = Array.from({ length: count }, (_, i) => `f${i}`);
55
+ const defs = names.map((name) => {
56
+ const roll = randInt(rng, 2);
57
+ const refs = names;
58
+ if (roll === 0) {
59
+ return `${name} = ();`;
60
+ }
61
+ const ref1 = refs[randInt(rng, refs.length)];
62
+ const ref2 = refs[randInt(rng, refs.length)];
63
+ return `${name} = ${ref1} ${ref2};`;
64
+ });
65
+ const main = names[0];
66
+ return `${defs.join("\n")}\n${main}\n`;
67
+ }
68
+
69
+ function buildRenameMap(names, rng) {
70
+ const perm = permute(rng, names);
71
+ const map = {};
72
+ names.forEach((name, i) => {
73
+ map[name] = perm[i];
74
+ });
75
+ return map;
76
+ }
77
+
78
+ const rng = mulberry32(0xdeadbeef);
79
+ const iterations = 30;
80
+
81
+ for (let i = 0; i < iterations; i++) {
82
+ const count = 3 + randInt(rng, 4);
83
+ const script = generateScript(rng, count);
84
+ const names = Array.from({ length: count }, (_, j) => `f${j}`);
85
+
86
+ const baseHash = getRelHash(script, "f0");
87
+
88
+ const renameMap = buildRenameMap(names, rng);
89
+ const renamedScript = renameScript(script, renameMap);
90
+ const renamedMain = renameMap.f0;
91
+ const renamedHash = getRelHash(renamedScript, renamedMain);
92
+ assert.equal(baseHash, renamedHash, "renaming should not change hash");
93
+
94
+ const lines = script.trim().split("\n");
95
+ const defs = lines.slice(0, -1);
96
+ const main = lines[lines.length - 1];
97
+ const reordered = `${permute(rng, defs).join("\n")}\n${main}\n`;
98
+ const reorderedHash = getRelHash(reordered, "f0");
99
+ if (baseHash !== reorderedHash) {
100
+ console.error("Reorder mismatch:");
101
+ console.error("ORIGINAL:\n" + script);
102
+ console.error("REORDERED:\n" + reordered);
103
+ }
104
+ assert.equal(baseHash, reorderedHash, "reordering defs should not change hash");
105
+ }
106
+
107
+ console.log("test-hash-fuzz: ok");
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env node
2
+
3
+ import assert from "node:assert/strict";
4
+ import { annotate } from "./index.mjs";
5
+ import { hash } from "./hash.mjs";
6
+
7
+ const nameRegex = /^[a-zA-Z0-9_+][a-zA-Z0-9_?!+]*$/;
8
+
9
+ function hashBody(h) {
10
+ assert.ok(h.startsWith("@"), "hash must start with @");
11
+ return h.slice(1);
12
+ }
13
+
14
+ function getRelHash(script, name) {
15
+ const { relAlias } = annotate(script);
16
+ assert.ok(relAlias[name], `missing relAlias for ${name}`);
17
+ return relAlias[name];
18
+ }
19
+
20
+ // Hash format invariants
21
+ {
22
+ const h = hash("abc");
23
+ assert.ok(nameRegex.test(hashBody(h)), "hash body must match k-name regex");
24
+ }
25
+
26
+ // Alpha-renaming of local references should not change hash
27
+ {
28
+ const scriptA = `
29
+ g = ();
30
+ f = g;
31
+ f
32
+ `;
33
+
34
+ const scriptB = `
35
+ h = ();
36
+ f = h;
37
+ f
38
+ `;
39
+
40
+ assert.equal(
41
+ getRelHash(scriptA, "f"),
42
+ getRelHash(scriptB, "f"),
43
+ "alpha renaming should be stable"
44
+ );
45
+ }
46
+
47
+ // Direct alias vs inline definition should not change hash
48
+ {
49
+ const scriptAlias = `
50
+ g = ();
51
+ f = g;
52
+ f
53
+ `;
54
+
55
+ const scriptInline = `
56
+ f = ();
57
+ f
58
+ `;
59
+
60
+ assert.equal(
61
+ getRelHash(scriptAlias, "f"),
62
+ getRelHash(scriptInline, "f"),
63
+ "alias vs inline should be stable"
64
+ );
65
+ }
66
+
67
+ // SCC order should not change hash
68
+ {
69
+ const scriptA = `
70
+ $bit = < {} 0, {} 1 >;
71
+ f = $bit < /0 |0 g, /1 >;
72
+ g = $bit < /0 |0 f, /1 >;
73
+ f
74
+ `;
75
+
76
+ const scriptB = `
77
+ $bit = < {} 0, {} 1 >;
78
+ g = $bit < /0 |0 f, /1 >;
79
+ f = $bit < /0 |0 g, /1 >;
80
+ f
81
+ `;
82
+
83
+ assert.equal(
84
+ getRelHash(scriptA, "f"),
85
+ getRelHash(scriptB, "f"),
86
+ "SCC order should be stable"
87
+ );
88
+ }
89
+
90
+ // Union ordering should be significant
91
+ {
92
+ const scriptA = `
93
+ x = < x, y >;
94
+ y = x;
95
+ x
96
+ `;
97
+
98
+ const scriptB = `
99
+ x = < y, x >;
100
+ y = x;
101
+ x
102
+ `;
103
+
104
+ assert.notEqual(
105
+ getRelHash(scriptA, "x"),
106
+ getRelHash(scriptB, "x"),
107
+ "union ordering should be significant"
108
+ );
109
+ }
110
+
111
+ // Product ordering should be significant
112
+ {
113
+ const scriptA = `
114
+ a = ();
115
+ b = ();
116
+ x = { a a, b b };
117
+ x
118
+ `;
119
+
120
+ const scriptB = `
121
+ a = ();
122
+ b = ();
123
+ y = { b b, a a };
124
+ y
125
+ `;
126
+
127
+ assert.notEqual(
128
+ getRelHash(scriptA, "x"),
129
+ getRelHash(scriptB, "y"),
130
+ "product ordering should be significant"
131
+ );
132
+ }
133
+
134
+ console.log("test-hash-normalization: ok");
@@ -0,0 +1,74 @@
1
+ import assert from "node:assert";
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";
9
+ import { compileObjectBuffer, decodeObject, objectToFunction, decompileObjectBuffer } from "./object.mjs";
10
+
11
+ const value = parseValue("{a:{b:x,c:{}}}");
12
+ const wire = encodeToWire(value, null);
13
+ const decodedWire = decodeWire(wire);
14
+ assert.deepEqual(decodedWire.pattern, [
15
+ ["open-union", [["a", 1]]],
16
+ ["closed-product", [["b", 2], ["c", 3]]],
17
+ ["open-union", [["x", 3]]],
18
+ ["closed-product", []]
19
+ ]);
20
+ assert.deepEqual(decodedWire.value.toJSON(), value.toJSON());
21
+
22
+ const unicodeLabelValue = new Product({
23
+ "é": new Variant("🙂", new Product({})),
24
+ "𐐷": new Product({})
25
+ });
26
+ const unicodeWire = encodeToWire(unicodeLabelValue, null);
27
+ const decodedUnicode = decodeWire(unicodeWire);
28
+ assert.deepEqual(decodedUnicode.value.toJSON(), unicodeLabelValue.toJSON());
29
+ assert(decodedUnicode.pattern.some(([, edges]) => edges.some(([label]) => label === "é")));
30
+ assert(decodedUnicode.pattern.some(([, edges]) => edges.some(([label]) => label === "🙂")));
31
+ assert(decodedUnicode.pattern.some(([, edges]) => edges.some(([label]) => label === "𐐷")));
32
+
33
+ const core = k.annotate(fs.readFileSync("core.k", "utf8"));
34
+ const coreMain = core.rels.__main__;
35
+ const corePatternId = coreMain.typePatternGraph.find(coreMain.def.patterns[0]);
36
+ const corePattern = patternToPropertyList(exportPatternGraph(coreMain.typePatternGraph, corePatternId));
37
+ assert.deepEqual(CORE_PATTERN_PROPERTY_LIST, corePattern);
38
+
39
+ const INT_PATTERN = [
40
+ ["closed-union", [["+", 1], ["-", 1]]],
41
+ ["closed-union", [["0", 1], ["1", 1], ["_", 2]]],
42
+ ["closed-product", []]
43
+ ];
44
+
45
+ const objectBuffer = compileObjectBuffer(`
46
+ $ bits = < {} _, bits 0, bits 1 >;
47
+ $ int = < bits +, bits - >;
48
+ {}|_|0|1|+ $int
49
+ `);
50
+ const objectFn = objectToFunction(decodeObject(objectBuffer));
51
+ const objectResult = objectFn(new Product({}));
52
+ assert.deepEqual(objectResult.pattern, INT_PATTERN);
53
+ assert.deepEqual(objectResult.toJSON(), { "+": { "1": { "0": "_" } } });
54
+
55
+ const decompiledSource = decompileObjectBuffer(objectBuffer);
56
+ const decompiledResult = k.compile(decompiledSource)(new Product({}));
57
+ assert.deepEqual(decompiledResult.pattern, INT_PATTERN);
58
+ assert.deepEqual(decompiledResult.toJSON(), objectResult.toJSON());
59
+ assert.match(decompiledSource, /^----- codes -----$/m);
60
+ assert.match(decompiledSource, /^----- rels -----$/m);
61
+ assert.match(decompiledSource, /^----- main -----$/m);
62
+ assert.match(decompiledSource, /\$ Nws3 =/);
63
+ assert.match(decompiledSource, /^7jfi = /m);
64
+ assert.match(decompiledSource, /^----- main -----\n\?\(\.\.\.\) 7jfi \$Nws3$/m);
65
+
66
+ assert.throws(() => k.compile("@A = (); @A"), /Parse error/);
67
+ assert.throws(() => k.compile("$ @A = {}; $@A"), /Parse error/);
68
+ assert.deepEqual(k.compile("7jfi = ?X0; 7jfi")(new Product({})).toJSON(), {});
69
+
70
+ const sccSource = decompileObjectBuffer(compileObjectBuffer("c = {}; b = c |x; a = b |y; a"));
71
+ assert.match(sccSource, /----- rels -----\nPQgV = .+\n\naQAD = .+\n\nN9UH = /s);
72
+ assert.deepEqual(k.compile(sccSource)(new Product({})).toJSON(), { y: "x" });
73
+
74
+ console.log("OK");
package/test-repl2.mjs ADDED
@@ -0,0 +1,106 @@
1
+ import assert from "node:assert";
2
+ import fs from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ import { completeInput, createState, evaluateInput, isMainEntrypoint } from "./repl2.mjs";
8
+ import { decodeObject, objectToFunction } from "./object.mjs";
9
+ import { Product } from "./Value.mjs";
10
+
11
+ const state = createState();
12
+
13
+ let output = await evaluateInput(":type nat = <{} zero, nat succ>", state);
14
+ assert.match(output[0], /^\$ nat = @/);
15
+
16
+ output = await evaluateInput(":rel succ = |succ", state);
17
+ assert.match(output[0], /^succ = @/);
18
+
19
+ output = await evaluateInput(":codes", state);
20
+ assert.match(output[0], /^nat = @/m);
21
+
22
+ output = await evaluateInput(":rels", state);
23
+ assert.match(output[0], /^succ = @/m);
24
+
25
+ output = await evaluateInput(":run succ", state);
26
+ assert.equal(output[0], "{}|succ ?<{} succ, ...>");
27
+
28
+ output = await evaluateInput(":t succ", state);
29
+ assert.match(output[0], /^succ : /);
30
+
31
+ output = await evaluateInput(":d succ", state);
32
+ assert.match(output[0], /^succ = /);
33
+
34
+ let completions = completeInput(":he", state)[0];
35
+ assert.deepEqual(completions, [":help"]);
36
+
37
+ const canonicalPartial = state.typeAliases.nat.slice(0, 8);
38
+ completions = completeInput(`:run ${canonicalPartial}`, state)[0];
39
+ assert(completions.some((line) => line.endsWith(state.typeAliases.nat)));
40
+
41
+ completions = completeInput(`(${canonicalPartial}`, state)[0];
42
+ assert(completions.includes(`(${state.typeAliases.nat}`));
43
+
44
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "k-repl2-"));
45
+ const libPath = path.join(tmpDir, "session.klib");
46
+ const koPath = path.join(tmpDir, "succ.ko");
47
+ const sourcePath = path.join(tmpDir, "session.k");
48
+ const symlinkPath = path.resolve(".test-k-repl2-link");
49
+ output = await evaluateInput(`:save ${libPath}`, state);
50
+ assert.equal(output[0], `saved ${libPath}`);
51
+
52
+ completions = completeInput(`:load ${tmpDir}/ses`, state)[0];
53
+ assert(completions.includes(`:load ${libPath}`));
54
+
55
+ output = await evaluateInput(`:ko ${koPath} succ`, state);
56
+ assert.equal(output[0], `saved ${koPath} (succ)`);
57
+ const objectFn = objectToFunction(decodeObject(fs.readFileSync(koPath)));
58
+ const objectResult = objectFn(new Product({}, [["closed-product", []]]));
59
+ assert.equal(objectResult.toJSON(), "succ");
60
+
61
+ fs.writeFileSync(sourcePath, "$ nat = <{} zero, nat succ>;\nsucc = |succ;\n");
62
+ const loadedSource = createState();
63
+ output = await evaluateInput(`:load ${sourcePath}`, loadedSource);
64
+ assert.equal(output[0], `loaded ${sourcePath}`);
65
+ output = await evaluateInput(":codes", loadedSource);
66
+ assert.match(output[0], /^nat = @/m);
67
+ output = await evaluateInput(":rels", loadedSource);
68
+ assert.match(output[0], /^succ = @/m);
69
+
70
+ const replPath = fileURLToPath(new URL("./repl2.mjs", import.meta.url));
71
+ try {
72
+ fs.rmSync(symlinkPath, { force: true });
73
+ } catch {}
74
+ fs.symlinkSync(replPath, symlinkPath);
75
+ fs.chmodSync(replPath, 0o755);
76
+ assert.equal(isMainEntrypoint(symlinkPath), true);
77
+
78
+ const reloaded = createState();
79
+ output = await evaluateInput(`:load ${libPath}`, reloaded);
80
+ assert.equal(output[0], `loaded ${libPath}`);
81
+
82
+ output = await evaluateInput(":codes", reloaded);
83
+ assert.match(output[0], /^nat = @/m);
84
+
85
+ output = await evaluateInput(":rels", reloaded);
86
+ assert.match(output[0], /^succ = @/m);
87
+
88
+ output = await evaluateInput("succ", reloaded);
89
+ assert.equal(output[0], "{}|succ ?<{} succ, ...>");
90
+
91
+ const shorthand = createState();
92
+ output = await evaluateInput("$ nat = <{} zero, nat succ>;", shorthand);
93
+ assert.match(output[0], /^\$ nat = @/);
94
+ output = await evaluateInput("succ = |succ;", shorthand);
95
+ assert.match(output[0], /^succ = @/);
96
+ output = await evaluateInput("succ", shorthand);
97
+ assert.equal(output[0], "{}|succ ?<{} succ, ...>");
98
+
99
+ await assert.rejects(
100
+ () => evaluateInput("/type", createState()),
101
+ /Type Error|Unknown ref: 'type'|Parse error|Parse Error/
102
+ );
103
+
104
+ fs.rmSync(tmpDir, { recursive: true, force: true });
105
+ fs.rmSync(symlinkPath, { force: true });
106
+ console.log("OK");
package/test.mjs CHANGED
@@ -1,101 +1,76 @@
1
1
  import k from "./index.mjs";
2
2
 
3
3
  [
4
- ["ANYTHING...", "()"],
5
- ["ANYTHING...", '{"ala" name, 23 age}'],
4
+ [{"unit": {}}, "()"],
5
+ [{"name": {"ala": {}}, "age": {"twentythree": {}}}, '{.name name, .age age}'],
6
6
  [
7
7
  {
8
- year: 2002,
9
- age: 19,
8
+ "year": {"twothousandtwo": {}},
9
+ "age": {"nineteen": {}},
10
10
  },
11
- "[.year, .age]",
12
- ],
13
- ["duplicate me", "[(), ()]"],
14
- ["nesting", "[[[()]]]"],
15
- ["nesting and accessing", "[[()]] {() nested, .0.0 val}"],
16
- [
17
- {
18
- test: "parse integer",
19
- },
20
- "0000",
21
- ],
22
- [
23
- {
24
- x: 3,
25
- y: 4,
26
- },
27
- "[.y,.x] PLUS",
11
+ "{.year year, .age age}",
28
12
  ],
13
+ [{"duplicate": {}}, "()"],
14
+ [{"nesting": {}}, "()"],
15
+ [{"test": {"parseunit": {}}}, "()"],
29
16
  ].map(function ([data, script]) {
30
17
  console.log(`k_expression = '${script}';`);
31
- console.log(`k.run(k_expression,${JSON.stringify(data)});`);
18
+ console.log(`k.run(k_expression,data});`);
32
19
  console.log(`// RETURNS: ${JSON.stringify(k.run(script, data))}`);
33
20
  return console.log("");
34
21
  });
35
22
 
36
- let k_expression =
37
- '{.name nom, <[.age, 18] GT "old", [.age, 12] GT "ado", "enfant"> age}';
38
-
39
- let k_fn = k.compile(k_expression);
40
-
41
- console.log(`var k_fn = k.compile('${k_expression}');`);
42
-
43
- console.log("");
44
-
45
- [
46
- {
47
- age: 23,
48
- name: "Emily",
49
- },
50
- {
51
- age: 16,
52
- name: "Katrina",
53
- },
54
- {
55
- age: 2,
56
- name: "Mark",
57
- },
58
- ].map(function (data) {
59
- console.log(`k_fn(${JSON.stringify(data)});`);
60
- console.log(`// RETURNS: ${JSON.stringify(k_fn(data))}`);
61
- return console.log("");
62
- });
63
-
64
23
 
65
- k_expression = "$ < < [ @int ] ints, [ @bool ] bools > list, @string None>";
24
+ let k_expression = `
25
+ $ bool = < {} true, {} false >;
26
+ true = {} | true $bool;
27
+ false = {} | false $bool;
28
+
29
+ list? = ?< {} nil, {X car, Y cdr} cons > = Y;
30
+ nil = {}|nil list?;
31
+ nil? = list? /nil nil;
32
+ car = list? /cons .car;
33
+ list? {
34
+ < nil? true, false > nil_test,
35
+ < car, {}|none > car
36
+ }
37
+ `
38
+ ;
66
39
 
67
- k_fn = k.compile(k_expression);
40
+ let k_fn = k.compile(k_expression);
68
41
 
69
42
  console.log(`var k_fn = k.compile('${k_expression}');`);
70
43
 
71
44
  console.log("");
72
45
 
73
46
  [
47
+ {"None": {}},
48
+ {"nil": {}},
74
49
  {
75
- None: "None",
76
- },
77
- {
78
- list: {
79
- ints: [],
80
- },
81
- },
82
- {
83
- list: {
84
- ints: [1, 2, 3],
85
- },
86
- },
87
- {
88
- list: {
89
- bools: [true, false],
50
+ "cons": {
51
+ "car": {"unit": {}},
52
+ "cdr": {
53
+ "cons": {
54
+ "car": {"unit": {}},
55
+ "cdr": {"nil": {}}
56
+ }
57
+ }
90
58
  },
91
59
  },
92
60
  {
93
- list: {
94
- bools: [true, 0],
61
+ "cons": {
62
+ "car": {"on": {}},
63
+ "cdr": {
64
+ "cons": {
65
+ "car": {"off": {}},
66
+ "cdr": {"nil": {}}
67
+
68
+ }
69
+ }
95
70
  },
96
71
  },
97
72
  ].map(function (data) {
98
73
  console.log(`k_fn(${JSON.stringify(data)});`);
99
- console.log(`// RETURNS: ${JSON.stringify(k_fn(data))}`);
74
+ console.log(`// RETURNS: ${JSON.stringify(k_fn(k.fromObject(data)))}`);
100
75
  return console.log("");
101
76
  });
package/tests.sh CHANGED
@@ -3,4 +3,13 @@
3
3
 
4
4
  echo "Running './tests.sh' ..."
5
5
 
6
- echo '[4,2,19,1]' | ./k.mjs -k Examples/rec-code.k -1
6
+ echo '["zebara", "ela", "kupa", ala, owca]' | node ./codecs/k-parse.mjs | ./k.mjs '{.1 0,.3 1}' | node ./codecs/k-print.mjs
7
+ echo
8
+ echo '-21' | node ./codecs/int.mjs --parse | node ./codecs/int.mjs --print | grep -qx -- '-21'
9
+ echo
10
+ node ./codecs/unit.mjs --parse | node ./codecs/unit.mjs --print | grep -qx '{}'
11
+ echo
12
+ printf 'A🙂\nBé~\t' | ./codecs/utf8.mjs --parse | ./k.mjs -k Examples/byte.k |./codecs/utf8.mjs --print
13
+ echo
14
+ echo "1031" | ./codecs/ieee.mjs --parse | ./k.mjs -k Examples/ieee.k | ./codecs/ieee.mjs --print
15
+ echo
@@ -0,0 +1,105 @@
1
+ import http from 'http';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ import codes from '../codes.mjs';
6
+ import { parse } from './typeParser.mjs';
7
+
8
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
+ const REGISTRY_FILE = path.join(__dirname, 'registry.json');
10
+ const PORT = 3001;
11
+
12
+ // Load existing registry if it exists
13
+ if (fs.existsSync(REGISTRY_FILE)) {
14
+ console.log('Loading existing registry...');
15
+ try {
16
+ const data = fs.readFileSync(REGISTRY_FILE, 'utf8');
17
+ const json = JSON.parse(data);
18
+ codes.load(json);
19
+ console.log(`Loaded ${Object.keys(json).length} types.`);
20
+ } catch (err) {
21
+ console.error('Error loading registry:', err);
22
+ }
23
+ } else {
24
+ console.log('No existing registry found. Starting fresh.');
25
+ }
26
+
27
+ const server = http.createServer((req, res) => {
28
+ // Helper to send JSON response
29
+ const sendJSON = (data, status = 200) => {
30
+ res.writeHead(status, { 'Content-Type': 'application/json' });
31
+ res.end(JSON.stringify(data));
32
+ };
33
+
34
+ // Helper to send Error
35
+ const sendError = (message, status = 400) => {
36
+ res.writeHead(status, { 'Content-Type': 'application/json' });
37
+ res.end(JSON.stringify({ error: message }));
38
+ };
39
+
40
+ // Route: POST
41
+ if (req.method === 'POST') {
42
+ let body = '';
43
+ req.on('data', chunk => {
44
+ body += chunk.toString();
45
+ });
46
+ req.on('end', () => {
47
+ try {
48
+ // 1. Parse the input string using the Jison parser
49
+ // The parser expects a string of type definitions
50
+ console.log("Received body:", body);
51
+ const definitions = parse(body);
52
+ console.log("Parsed definitions:", JSON.stringify(definitions, null, 2));
53
+
54
+ // 2. Register the parsed codes
55
+ // codes.register returns the representatives map { temp_name: canonical_hash }
56
+ const representatives = codes.register(definitions);
57
+
58
+ // Filter out generated names (_gen_...) and canonical names (@...)
59
+ const filteredRepresentatives = Object.fromEntries(
60
+ Object.entries(representatives).filter(([key, value]) =>
61
+ !key.startsWith('_gen_') && !key.startsWith('@')
62
+ )
63
+ );
64
+
65
+ // 3. Save to disk
66
+ const dump = codes.dump();
67
+ fs.writeFileSync(REGISTRY_FILE, JSON.stringify(dump, null, 2));
68
+
69
+ // 4. Return the mapping
70
+ sendJSON(filteredRepresentatives);
71
+
72
+ } catch (err) {
73
+ console.error(err);
74
+ sendError(`Registration failed: ${err.message}`);
75
+ }
76
+ });
77
+ }
78
+ // Route: GET
79
+ else if (req.method === 'GET') {
80
+ const typeId = req.url.split('/').pop();
81
+ console.log("Requested type ID:", typeId);
82
+ if (!typeId) {
83
+ sendError('Missing type ID');
84
+ return;
85
+ }
86
+ // Decode URI component in case @ is encoded
87
+ const decodedId = decodeURIComponent(typeId);
88
+ console.log("Decoded type ID:", decodedId);
89
+
90
+ const typeDef = codes.find(decodedId);
91
+ if (typeDef.code === 'undefined') {
92
+ sendError('Type not found', 404);
93
+ } else {
94
+ sendJSON(typeDef);
95
+ }
96
+ }
97
+ // 404 Not Found
98
+ else {
99
+ sendError('Not Found', 404);
100
+ }
101
+ });
102
+
103
+ server.listen(PORT, () => {
104
+ console.log(`Type Registry running at http://localhost:${PORT}/`);
105
+ });