@fraczak/k 6.0.1 → 6.0.3

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 (55) hide show
  1. package/AGENTS.md +35 -0
  2. package/CHANGELOG.md +48 -0
  3. package/CLAUDE.md +64 -0
  4. package/DOCS/BIG_PICTURE.md +4 -8
  5. package/DOCS/BINARY_CODEC_STATUS_AND_TODO.md +74 -0
  6. package/DOCS/CONVERGENCE.md +92 -0
  7. package/DOCS/DIVERGENCE_DETECTION.md +24 -0
  8. package/DOCS/Makefile +99 -0
  9. package/DOCS/OBJECT_FILE_AND_PATTERN.md +221 -0
  10. package/DOCS/PATTERNS.md +114 -0
  11. package/DOCS/PATTERNS_AND_TYPED_VALUES.md +665 -0
  12. package/DOCS/REPL.md +127 -126
  13. package/DOCS/TEXTUAL_VALUES.md +108 -0
  14. package/DOCS/TYPE_DERIVATION.md +230 -0
  15. package/DOCS/book/06-values-in-memory.md +1085 -0
  16. package/DOCS/book/07-the-partial-function-abi.md +143 -0
  17. package/DOCS/book/08-operational-semantics-and-execution.md +201 -0
  18. package/DOCS/book/09-compiler-architecture.md +160 -0
  19. package/DOCS/book/10-from-ast-to-intermediate-representation.md +167 -0
  20. package/DOCS/book/14-canonical-serialization.md +176 -0
  21. package/DOCS/book/appendix-b-incorporating-external-predefined-types-and-functions.md +155 -0
  22. package/DOCS/book/overview.txt +68 -0
  23. package/README.md +78 -30
  24. package/UU/ta.k +1 -0
  25. package/UU/ta.k~ +0 -0
  26. package/UU/tb.k +0 -0
  27. package/UU/tc.k +0 -0
  28. package/codecs/README.md +12 -0
  29. package/codecs/pandoc.css +71 -0
  30. package/codecs/runtime/test-codec.mjs +89 -0
  31. package/core.klib +0 -0
  32. package/core.klib~ +0 -0
  33. package/ieee.klib +0 -0
  34. package/kkk.k +197 -0
  35. package/kkk.klib +0 -0
  36. package/kkk.klib~ +0 -0
  37. package/object.mjs +162 -8
  38. package/objects/README.md +10 -0
  39. package/objects/compile-lib.mjs +26 -2
  40. package/objects/compile.mjs +26 -2
  41. package/objects/decompile.mjs +25 -2
  42. package/objects/extract-aliases.mjs +59 -0
  43. package/package.json +3 -2
  44. package/{codecs/k-pattern.mjs → patterns/from-k.mjs} +3 -3
  45. package/repl2.mjs +345 -83
  46. package/scripts/generate-dependency-graph.mjs +121 -0
  47. package/test-k-object.mjs +77 -1
  48. package/test-repl2.mjs +65 -8
  49. package/DOCS/book-combined.html +0 -3798
  50. package/DOCS/book-combined.md +0 -3745
  51. package/DOCS/book-combined.pdf +0 -0
  52. package/DOCS/book-combined.tex +0 -5682
  53. package/DOCS/book.html +0 -65
  54. package/DOCS/book.pdf +0 -0
  55. package/DOCS/book.tex +0 -150
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { execSync } from "node:child_process";
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+ const root = path.resolve(__dirname, "..");
11
+ const outPath = path.join(root, "DOCS", "dependency-graph.md");
12
+
13
+ function getTrackedMjsFiles() {
14
+ const raw = execSync("git ls-files '*.mjs'", {
15
+ cwd: root,
16
+ encoding: "utf8"
17
+ });
18
+
19
+ const all = raw
20
+ .split("\n")
21
+ .map((s) => s.trim())
22
+ .filter(Boolean);
23
+
24
+ return all.filter((relPath) => {
25
+ if (relPath.startsWith("codecs/")) return true;
26
+ if (relPath.includes("/")) return false;
27
+ if (relPath.startsWith("test")) return false;
28
+ if (relPath.includes("-test")) return false;
29
+ return true;
30
+ });
31
+ }
32
+
33
+ function parseLocalImports(absPath) {
34
+ const source = fs.readFileSync(absPath, "utf8");
35
+ const regex = /^\s*import\s+[^;]*?from\s+["']([^"']+)["'];?\s*$/gm;
36
+ const imports = [];
37
+ let match;
38
+ while ((match = regex.exec(source)) !== null) {
39
+ const specifier = match[1];
40
+ if (specifier.startsWith("./") || specifier.startsWith("../")) {
41
+ imports.push(specifier);
42
+ }
43
+ }
44
+ return imports;
45
+ }
46
+
47
+ function toNodeId(relPath) {
48
+ return relPath.replace(/[^a-zA-Z0-9]/g, "_");
49
+ }
50
+
51
+ function makeBar(sizeKb) {
52
+ const units = Math.max(1, Math.min(20, Math.round(sizeKb)));
53
+ return "█".repeat(units);
54
+ }
55
+
56
+ function renderGraph(files, sizesKb, edges) {
57
+ const lines = [];
58
+ lines.push("# Project Dependency Graph");
59
+ lines.push("");
60
+ lines.push("_Auto-generated by scripts/generate-dependency-graph.mjs._");
61
+ lines.push("");
62
+ lines.push("```mermaid");
63
+ lines.push("graph TD");
64
+ lines.push(" classDef default fill:#fff,stroke:#333,stroke-width:2px,color:#000;");
65
+ lines.push("");
66
+
67
+ for (const relPath of files) {
68
+ const id = toNodeId(relPath);
69
+ const sizeKb = sizesKb[relPath];
70
+ const bar = makeBar(sizeKb);
71
+ lines.push(` ${id}[\"${relPath} (${sizeKb.toFixed(1)} KB)<br/>${bar}\"]`);
72
+ }
73
+
74
+ if (edges.length > 0) {
75
+ lines.push("");
76
+ }
77
+
78
+ for (const { src, dst } of edges) {
79
+ lines.push(` ${toNodeId(src)} --> ${toNodeId(dst)}`);
80
+ }
81
+
82
+ lines.push("```");
83
+ lines.push("");
84
+ return lines.join("\n");
85
+ }
86
+
87
+ function main() {
88
+ const files = getTrackedMjsFiles().sort();
89
+ const fileSet = new Set(files);
90
+
91
+ const sizesKb = {};
92
+ for (const relPath of files) {
93
+ const absPath = path.join(root, relPath);
94
+ const bytes = fs.statSync(absPath).size;
95
+ sizesKb[relPath] = bytes / 1024;
96
+ }
97
+
98
+ const edgeKeys = new Set();
99
+ const edges = [];
100
+ for (const relPath of files) {
101
+ const absPath = path.join(root, relPath);
102
+ const imports = parseLocalImports(absPath);
103
+
104
+ for (const specifier of imports) {
105
+ const resolved = path.normalize(path.join(path.dirname(relPath), specifier));
106
+ if (!resolved.endsWith(".mjs")) continue;
107
+ if (!fileSet.has(resolved)) continue;
108
+ const key = `${relPath}->${resolved}`;
109
+ if (edgeKeys.has(key)) continue;
110
+ edgeKeys.add(key);
111
+ edges.push({ src: relPath, dst: resolved });
112
+ }
113
+ }
114
+
115
+ const graph = renderGraph(files, sizesKb, edges);
116
+ fs.writeFileSync(outPath, graph, "utf8");
117
+ console.log(`Wrote ${path.relative(root, outPath)}`);
118
+ console.log(`Nodes: ${files.length}, Edges: ${edges.length}`);
119
+ }
120
+
121
+ main();
package/test-k-object.mjs CHANGED
@@ -6,7 +6,14 @@ import { decodeWire, encodeToWire, CORE_PATTERN_PROPERTY_LIST } from "./codecs/r
6
6
  import { Product, Variant } from "./Value.mjs";
7
7
  import { exportPatternGraph } from "./codecs/runtime/codec.mjs";
8
8
  import { patternToPropertyList } from "./codecs/runtime/pattern-json.mjs";
9
- import { compileObjectBuffer, decodeObject, objectToFunction, decompileObjectBuffer } from "./object.mjs";
9
+ import {
10
+ compileObjectBuffer,
11
+ compileLibraryBuffer,
12
+ decodeObject,
13
+ objectToFunction,
14
+ decompileObjectBuffer,
15
+ extractAliasesFromObject
16
+ } from "./object.mjs";
10
17
 
11
18
  const value = parseValue("{a:{b:x,c:{}}}");
12
19
  const wire = encodeToWire(value, null);
@@ -71,4 +78,73 @@ const sccSource = decompileObjectBuffer(compileObjectBuffer("c = {}; b = c |x; a
71
78
  assert.match(sccSource, /----- rels -----\nPQgV = .+\n\naQAD = .+\n\nN9UH = /s);
72
79
  assert.deepEqual(k.compile(sccSource)(new Product({})).toJSON(), { y: "x" });
73
80
 
81
+ const library = decodeObject(compileLibraryBuffer("$ nat = <{} zero, nat succ>;\nsucc = |succ;\n", { source: "defs-only.k" }));
82
+ assert.equal(library.main, null);
83
+ assert(Object.values(library.meta).some(({ type, origins }) =>
84
+ type === "code" &&
85
+ origins.some((origin) =>
86
+ origin.source === "defs-only.k" &&
87
+ origin.name === "nat" &&
88
+ JSON.stringify(Object.keys(origin).sort()) === JSON.stringify(["compiledAt", "name", "source"]) &&
89
+ typeof origin.compiledAt === "string"
90
+ )
91
+ ));
92
+ assert(Object.values(library.meta).some(({ type, origins }) =>
93
+ type === "rel" &&
94
+ origins.some((origin) =>
95
+ origin.source === "defs-only.k" &&
96
+ origin.name === "succ" &&
97
+ JSON.stringify(Object.keys(origin).sort()) === JSON.stringify(["compiledAt", "name", "source"]) &&
98
+ typeof origin.compiledAt === "string"
99
+ )
100
+ ));
101
+
102
+ const derivedLibrary = decodeObject(compileLibraryBuffer("other = |other;\n", {
103
+ source: "derived.k",
104
+ libraries: [library]
105
+ }));
106
+ const derivedAliases = extractAliasesFromObject(derivedLibrary);
107
+ assert.match(derivedAliases, /^\$ nat = @/m);
108
+ assert.match(derivedAliases, /^succ = @/m);
109
+ assert.match(derivedAliases, /^other = @/m);
110
+ assert(derivedAliases.indexOf("$ nat = @") < derivedAliases.indexOf("other = @"));
111
+ assert(derivedAliases.indexOf("other = @") < derivedAliases.indexOf("succ = @"));
112
+
113
+ const aliasSnippet = extractAliasesFromObject({
114
+ format: "k-object",
115
+ version: 2,
116
+ codes: {},
117
+ rels: {},
118
+ meta: {
119
+ "@relB": {
120
+ type: "rel",
121
+ origins: [
122
+ { source: "new.k", name: "times", compiledAt: "2026-04-12T06:08:10.390Z" },
123
+ { source: "core.k", name: "times", compiledAt: "2026-03-09T06:17:23.390Z" },
124
+ { source: "other.k", name: "plus", compiledAt: "2026-05-17T06:10:23.390Z" }
125
+ ]
126
+ },
127
+ "@codeB": {
128
+ type: "code",
129
+ origins: [
130
+ { source: "z.k", name: "edges", compiledAt: "2026-03-09T06:17:23.390Z" }
131
+ ]
132
+ },
133
+ "@codeA": {
134
+ type: "code",
135
+ origins: [
136
+ { source: "a.k", name: "bits", compiledAt: "2026-05-17T06:10:23.390Z" }
137
+ ]
138
+ }
139
+ },
140
+ main: null
141
+ });
142
+ assert.deepEqual(aliasSnippet.split("\n").filter(Boolean), [
143
+ '$ bits = @codeA; # {"source":"a.k","compiledAt":"2026-05-17T06:10:23.390Z"}',
144
+ '$ edges = @codeB; # {"source":"z.k","compiledAt":"2026-03-09T06:17:23.390Z"}',
145
+ 'plus = @relB; # {"source":"other.k","compiledAt":"2026-05-17T06:10:23.390Z"}',
146
+ '# times = @relB; # {"source":"core.k","compiledAt":"2026-03-09T06:17:23.390Z"}',
147
+ 'times = @relB; # {"source":"new.k","compiledAt":"2026-04-12T06:08:10.390Z"}'
148
+ ]);
149
+
74
150
  console.log("OK");
package/test-repl2.mjs CHANGED
@@ -4,7 +4,18 @@ import os from "node:os";
4
4
  import path from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
 
7
- import { completeInput, createState, evaluateInput, isMainEntrypoint } from "./repl2.mjs";
7
+ import {
8
+ aliasNames,
9
+ analyzeAcceptedSnippet,
10
+ analyzeRawSnippet,
11
+ completeInput,
12
+ createState,
13
+ evaluateInput,
14
+ explicitSnippetTerminated,
15
+ isMainEntrypoint,
16
+ lineHasExplicitContinuation,
17
+ lineTerminatesSnippet
18
+ } from "./repl2.mjs";
8
19
  import { decodeObject, objectToFunction } from "./object.mjs";
9
20
  import { Product } from "./Value.mjs";
10
21
 
@@ -41,16 +52,44 @@ assert(completions.some((line) => line.endsWith(state.typeAliases.nat)));
41
52
  completions = completeInput(`(${canonicalPartial}`, state)[0];
42
53
  assert(completions.includes(`(${state.typeAliases.nat}`));
43
54
 
55
+ completions = completeInput(":run su", state)[0];
56
+ assert(completions.includes(":run succ"));
57
+
58
+ completions = completeInput("$na", state)[0];
59
+ assert(completions.includes("$nat"));
60
+
61
+ assert.deepEqual(aliasNames(state), ["nat", "succ"]);
62
+ assert.equal(lineTerminatesSnippet(";"), true);
63
+ assert.equal(lineTerminatesSnippet(" ; "), true);
64
+ assert.equal(lineTerminatesSnippet(" ; -- comment"), false);
65
+ assert.equal(lineTerminatesSnippet("succ"), false);
66
+ assert.equal(explicitSnippetTerminated("$ bool = <{} true, {} false>;"), true);
67
+ assert.equal(lineHasExplicitContinuation("succ \\"), true);
68
+ assert.equal(analyzeRawSnippet("$ bool = <{} true, {} false>").kind, "incomplete");
69
+ assert.equal(analyzeAcceptedSnippet("$ bool = <{} true, {} false>;", true).kind, "definitionsOnly");
70
+ assert.equal(analyzeRawSnippet("a =").kind, "incomplete");
71
+ assert.equal(analyzeRawSnippet("{} | succ").kind, "withMain");
72
+
73
+ const offsetState = createState();
74
+ await evaluateInput(":type ab = <{} 1, {} 2>", offsetState);
75
+ await evaluateInput(":type bool = <{} true, {} false>", offsetState);
76
+ await assert.rejects(
77
+ () => evaluateInput("$ab", offsetState),
78
+ /Type Error in 'filter' \(lines 1:1\.\.\.1:4\)/
79
+ );
80
+
44
81
  const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "k-repl2-"));
45
82
  const libPath = path.join(tmpDir, "session.klib");
46
83
  const koPath = path.join(tmpDir, "succ.ko");
47
84
  const sourcePath = path.join(tmpDir, "session.k");
48
85
  const symlinkPath = path.resolve(".test-k-repl2-link");
49
- output = await evaluateInput(`:save ${libPath}`, state);
86
+ output = await evaluateInput(`:klib ${libPath}`, state);
50
87
  assert.equal(output[0], `saved ${libPath}`);
51
88
 
52
89
  completions = completeInput(`:load ${tmpDir}/ses`, state)[0];
53
90
  assert(completions.includes(`:load ${libPath}`));
91
+ completions = completeInput(`:load --no-alias ${tmpDir}/ses`, state)[0];
92
+ assert(completions.includes(`:load --no-alias ${libPath}`));
54
93
 
55
94
  output = await evaluateInput(`:ko ${koPath} succ`, state);
56
95
  assert.equal(output[0], `saved ${koPath} (succ)`);
@@ -88,13 +127,31 @@ assert.match(output[0], /^succ = @/m);
88
127
  output = await evaluateInput("succ", reloaded);
89
128
  assert.equal(output[0], "{}|succ ?<{} succ, ...>");
90
129
 
130
+ const noAliasReloaded = createState();
131
+ output = await evaluateInput(`:load --no-alias ${libPath}`, noAliasReloaded);
132
+ assert.equal(output[0], `loaded ${libPath}`);
133
+ output = await evaluateInput(":codes", noAliasReloaded);
134
+ assert.equal(output[0], "(none)");
135
+ output = await evaluateInput(":rels", noAliasReloaded);
136
+ assert.equal(output[0], "(none)");
137
+
91
138
  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, ...>");
139
+ output = await evaluateInput("$ nat = <{} zero, nat succ>\n; succ = |succ\n;", shorthand);
140
+ assert.deepEqual(output, []);
141
+ output = await evaluateInput(":codes", shorthand);
142
+ assert.match(output[0], /^nat = @/m);
143
+ output = await evaluateInput(":rels", shorthand);
144
+ assert.match(output[0], /^succ = @/m);
145
+ output = await evaluateInput("$ bool = <{} true, {} false>\n; not = $bool </true | false, {} | true >\n; {} | true not", shorthand);
146
+ assert.match(output[0], /^\{\}\|false \?</);
147
+ assert.match(output[0], / false/);
148
+ assert.match(output[0], / true/);
149
+ output = await evaluateInput("$ maybe = <{} none, {} some>;", shorthand);
150
+ assert.deepEqual(output, []);
151
+ output = await evaluateInput(":codes", shorthand);
152
+ assert.match(output[0], /^maybe = @/m);
153
+ output = await evaluateInput("{} | true not", shorthand);
154
+ assert.match(output[0], /^\{\}\|false \?</);
98
155
 
99
156
  await assert.rejects(
100
157
  () => evaluateInput("/type", createState()),