@fraczak/k 7.0.2 → 7.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 (71) hide show
  1. package/CHANGELOG.md +75 -0
  2. package/DOCS/CODECS.md +37 -2
  3. package/DOCS/CODEC_ARCHITECTURE.md +12 -5
  4. package/DOCS/FILE_FORMATS.md +238 -15
  5. package/DOCS/POLYMORPHIC_IR.md +104 -0
  6. package/DOCS/REPL.md +11 -3
  7. package/DOCS/WEB_REPL_REQUIREMENTS.md +1 -1
  8. package/JOURNAL.md +288 -0
  9. package/README.md +12 -3
  10. package/Value.mjs +28 -3
  11. package/access-table.mjs +343 -0
  12. package/backend-api.mjs +23 -1
  13. package/backends/llvm/README.md +20 -21
  14. package/backends/llvm/bin/k-llvm-build.mjs +105 -32
  15. package/backends/llvm/bin/k-llvm-compile.mjs +63 -24
  16. package/backends/llvm/bin/k-llvm.mjs +292 -0
  17. package/backends/llvm/runtime/krt.c +8 -0
  18. package/backends/llvm/runtime/krt.h +1 -0
  19. package/backends/llvm/src/cli.mjs +95 -17
  20. package/backends/llvm/src/executable.mjs +128 -11
  21. package/backends/llvm/src/kvm2llvm.mjs +10 -5
  22. package/backends/llvm/src/llvm.mjs +121 -9
  23. package/backends/llvm/tests/integration.sh +11 -11
  24. package/backends/llvm/{bin → tests}/k-llvm-run.mjs +48 -22
  25. package/backends/llvm/tests/perf-support.mjs +24 -3
  26. package/backends/llvm/tests/test-llvm.mjs +22 -1
  27. package/backends/wasm/bin/k-wasm-compile.mjs +46 -20
  28. package/backends/wasm/bin/k-wasm-run.mjs +61 -20
  29. package/backends/wasm/bin/k-wasm.mjs +47 -13
  30. package/backends/wasm/src/cli.mjs +141 -19
  31. package/backends/wasm/src/wasm.mjs +130 -23
  32. package/browser/repl-browser.mjs +16 -5
  33. package/browser/tree-view.mjs +47 -8
  34. package/browser/vfs-data.mjs +6 -5
  35. package/codecs/README.md +1 -0
  36. package/codecs/djson.mjs +270 -0
  37. package/codecs/int.mjs +2 -1
  38. package/codecs/json.mjs +7 -2
  39. package/codecs/k-parse.mjs +36 -9
  40. package/codecs/k-print.mjs +26 -6
  41. package/codecs/runtime/codec-sdk.mjs +24 -6
  42. package/codecs/runtime/pattern-json.mjs +80 -1
  43. package/codecs/runtime/prefix-codec.mjs +629 -67
  44. package/codecs/runtime/show-value.mjs +93 -51
  45. package/codecs/runtime/unicode-string.mjs +29 -6
  46. package/codecs/show.mjs +24 -6
  47. package/codecs/utf16.mjs +2 -1
  48. package/k.mjs +56 -16
  49. package/kir.mjs +58 -19
  50. package/kvm.mjs +49 -12
  51. package/objects/compile.mjs +37 -23
  52. package/objects/decompile.mjs +40 -10
  53. package/objects/extract-aliases.mjs +44 -14
  54. package/objects/inspect.mjs +19 -14
  55. package/objects/validate.mjs +18 -11
  56. package/package.json +11 -3
  57. package/pkir.mjs +772 -0
  58. package/repl.mjs +92 -23
  59. package/scripts/build-repl-html.mjs +22 -3
  60. package/scripts/perf-ieee.mjs +264 -13
  61. package/scripts/perf-int.mjs +315 -22
  62. package/scripts/perf-multiplication.mjs +58 -7
  63. package/scripts/perf-poly.mjs +306 -24
  64. package/scripts/run-tests.mjs +3 -0
  65. package/tests/test-access-table.mjs +213 -0
  66. package/tests/test-codecs.mjs +168 -0
  67. package/tests/test-pkir.mjs +530 -0
  68. package/tests/test-polymorphic-tdd.mjs +196 -0
  69. package/tests/test-repl-html.mjs +16 -2
  70. package/tests/test-repl.mjs +63 -1
  71. package/backends/llvm/bin/k-llvm-jit.mjs +0 -160
@@ -0,0 +1,213 @@
1
+ import assert from "node:assert/strict";
2
+ import { buildAccessTable } from "../access-table.mjs";
3
+
4
+ console.log("==> Running Access Table Tests (tests/test-access-table.mjs)");
5
+
6
+ // --------------------------------------------------------------------
7
+ // Test 1: Access Table for 'search' on Disparate Schema
8
+ // --------------------------------------------------------------------
9
+ {
10
+ console.log("--> Test 1: Access Table for 'search' on Disparate Schema");
11
+
12
+ // Input Schema S: list of { data, flag, id }
13
+ const S_list = [
14
+ ["closed-union", [["cons", 1], ["nil", 2]]],
15
+ ["closed-product", [["car", 3], ["cdr", 0]]],
16
+ ["closed-product", []],
17
+ ["closed-product", [["data", 4], ["flag", 5], ["id", 6]]],
18
+ ["any", []],
19
+ ["closed-union", [["false", 2], ["true", 2]]],
20
+ ["any", []]
21
+ ];
22
+
23
+ // Function Pattern P: list of { flag: <true, ...> }
24
+ const P_search = [
25
+ ["closed-union", [["cons", 1], ["nil", 2]]],
26
+ ["closed-product", [["car", 3], ["cdr", 0]]],
27
+ ["closed-product", []],
28
+ ["open-product", [["flag", 4]]],
29
+ ["open-union", [["true", 2]]],
30
+ ];
31
+
32
+ const table = buildAccessTable(S_list, P_search, { wordSize: 8 });
33
+ assert.equal(table.stateCount, 7);
34
+
35
+ // 1a: Check list union resolution (State 0)
36
+ const consVariant = table.resolveVariant(0, "cons");
37
+ assert.equal(consVariant.tagId, 0);
38
+ assert.equal(consVariant.branchId, 0);
39
+ assert.equal(consVariant.childState, 1);
40
+
41
+ const nilVariant = table.resolveVariant(0, "nil");
42
+ assert.equal(nilVariant.tagId, 1);
43
+ assert.equal(nilVariant.branchId, 1);
44
+ assert.equal(nilVariant.childState, 2);
45
+
46
+ // 1b: Check cons product resolution (State 1)
47
+ const carField = table.resolveField(1, "car");
48
+ assert.equal(carField.physicalSlot, 0);
49
+ assert.equal(carField.byteOffset, 0);
50
+ assert.equal(carField.childState, 3);
51
+
52
+ const cdrField = table.resolveField(1, "cdr");
53
+ assert.equal(cdrField.physicalSlot, 1);
54
+ assert.equal(cdrField.byteOffset, 8);
55
+ assert.equal(cdrField.childState, 0);
56
+
57
+ // 1c: Check item product resolution (State 3: { data, flag, id })
58
+ // In alphabetical order: data = 0, flag = 1, id = 2
59
+ const flagField = table.resolveField(3, "flag");
60
+ assert.equal(flagField.physicalSlot, 1);
61
+ assert.equal(flagField.byteOffset, 8);
62
+ assert.equal(flagField.skipCount, 1);
63
+ assert.equal(flagField.childState, 5); // points to boolean union
64
+
65
+ // Logical slot access (by numeric slot 0)
66
+ const flagBySlot = table.resolveField(3, 0);
67
+ assert.equal(flagBySlot.byteOffset, 8);
68
+ assert.equal(flagBySlot.childState, 5);
69
+ assert.equal(flagBySlot.skipCount, 1);
70
+
71
+ console.log(" Passed: AccessTable correctly resolves fields and variants.");
72
+ }
73
+
74
+ // --------------------------------------------------------------------
75
+ // Test 2: Access Table for Multi-Variant Dispatch ('extract_id')
76
+ // --------------------------------------------------------------------
77
+ {
78
+ console.log("--> Test 2: Access Table for Multi-Variant Dispatch");
79
+
80
+ const P_extract = [
81
+ ["closed-union", [["click", 1], ["ping", 2], ["sensor", 3]]],
82
+ ["open-product", [["id", 4]]],
83
+ ["open-product", [["id", 4]]],
84
+ ["open-product", [["id", 4]]],
85
+ ["any", []]
86
+ ];
87
+
88
+ // Click: { active, id } -> id is at physical slot 1
89
+ const S_click = [
90
+ ["closed-union", [["click", 1]]],
91
+ ["closed-product", [["active", 2], ["id", 3]]],
92
+ ["any", []],
93
+ ["any", []]
94
+ ];
95
+
96
+ // Sensor: { battery_low, id, reading } -> id is at physical slot 1
97
+ const S_sensor = [
98
+ ["closed-union", [["sensor", 1]]],
99
+ ["closed-product", [["battery_low", 2], ["id", 3], ["reading", 4]]],
100
+ ["any", []],
101
+ ["any", []],
102
+ ["any", []]
103
+ ];
104
+
105
+ // Ping: { id } -> id is at physical slot 0
106
+ const S_ping = [
107
+ ["closed-union", [["ping", 1]]],
108
+ ["closed-product", [["id", 2]]],
109
+ ["any", []]
110
+ ];
111
+
112
+ const tClick = buildAccessTable(S_click, P_extract, { wordSize: 8 });
113
+ const tSensor = buildAccessTable(S_sensor, P_extract, { wordSize: 8 });
114
+ const tPing = buildAccessTable(S_ping, P_extract, { wordSize: 8 });
115
+
116
+ // In click, id is at offset 8 (after active)
117
+ assert.equal(tClick.resolveField(1, "id").byteOffset, 8);
118
+ assert.equal(tClick.resolveField(1, "id").skipCount, 1);
119
+
120
+ // In sensor, id is at offset 8 (after battery_low)
121
+ assert.equal(tSensor.resolveField(1, "id").byteOffset, 8);
122
+ assert.equal(tSensor.resolveField(1, "id").skipCount, 1);
123
+
124
+ // In ping, id is at offset 0
125
+ assert.equal(tPing.resolveField(1, "id").byteOffset, 0);
126
+ assert.equal(tPing.resolveField(1, "id").skipCount, 0);
127
+
128
+ console.log(" Passed: Variable offsets resolved per variant schema.");
129
+ }
130
+
131
+ // --------------------------------------------------------------------
132
+ // Test 3: Simulating In-Memory Payload Navigation via AccessTable
133
+ // --------------------------------------------------------------------
134
+ {
135
+ console.log("--> Test 3: Simulating In-Memory Payload Navigation");
136
+
137
+ // S: list of { data, flag, id }
138
+ const S = [
139
+ ["closed-union", [["cons", 1], ["nil", 2]]],
140
+ ["closed-product", [["car", 3], ["cdr", 0]]],
141
+ ["closed-product", []],
142
+ ["closed-product", [["data", 4], ["flag", 5], ["id", 6]]],
143
+ ["any", []],
144
+ ["closed-union", [["false", 2], ["true", 2]]],
145
+ ["any", []]
146
+ ];
147
+
148
+ const P = [
149
+ ["closed-union", [["cons", 1], ["nil", 2]]],
150
+ ["closed-product", [["car", 3], ["cdr", 0]]],
151
+ ["closed-product", []],
152
+ ["open-product", [["flag", 4]]],
153
+ ["open-union", [["true", 2]]],
154
+ ];
155
+
156
+ const table = buildAccessTable(S, P, { wordSize: 8 });
157
+
158
+ // Mock in-memory value representation:
159
+ // Node representation in word arrays:
160
+ // Item 1: { data: 'blob1', flag: 'true', id: 'alpha' }
161
+ // Sorted physical layout: slot 0 = data, slot 1 = flag, slot 2 = id
162
+ const item1 = {
163
+ tag: null,
164
+ words: [
165
+ { text: "blob1" }, // offset 0
166
+ { tag: 1, text: "true" }, // offset 8 (true is tag 1 in {false:0, true:1})
167
+ { text: "alpha" } // offset 16
168
+ ]
169
+ };
170
+
171
+ // Cons cell: slot 0 = car, slot 1 = cdr
172
+ const nilCell = { tag: 1, words: [] }; // tag 1 = nil
173
+ const consCell = {
174
+ tag: 0, // tag 0 = cons
175
+ words: [item1, nilCell]
176
+ };
177
+
178
+ // Navigate using table and word indices (zero string comparisons):
179
+ let state = table.rootState; // 0
180
+ assert.equal(state, 0);
181
+
182
+ // Step 1: Match union at root
183
+ const unionDesc = table.getDescriptor(state);
184
+ const tag = consCell.tag; // 0
185
+ const branch = unionDesc.branchMap[tag]; // 0 (cons)
186
+ assert.equal(branch, 0);
187
+ state = unionDesc.childStateMap[tag]; // state 1 (cons product)
188
+ assert.equal(state, 1);
189
+
190
+ // Step 2: Project .car (slot 0)
191
+ const carMeta = table.resolveField(state, 0);
192
+ assert.equal(carMeta.byteOffset, 0);
193
+ const carNode = consCell.words[carMeta.byteOffset / 8];
194
+ state = carMeta.childState; // state 3 (item product)
195
+ assert.equal(state, 3);
196
+
197
+ // Step 3: Project .flag (slot 0 in P's view of car)
198
+ const flagMeta = table.resolveField(state, 0);
199
+ assert.equal(flagMeta.byteOffset, 8); // correctly jumps to slot 1!
200
+ const flagVal = carNode.words[flagMeta.byteOffset / 8];
201
+ state = flagMeta.childState; // state 5 (boolean union)
202
+ assert.equal(state, 5);
203
+
204
+ // Step 4: Check if flag is 'true'
205
+ const boolDesc = table.getDescriptor(state);
206
+ const flagTag = flagVal.tag; // 1 ('true')
207
+ const flagBranch = boolDesc.branchMap[flagTag];
208
+ assert.equal(flagBranch, 0); // matches P's only branch ('true')
209
+
210
+ console.log(" Passed: Zero-string navigation simulation successful.");
211
+ }
212
+
213
+ console.log("\n==> All Access Table Tests Passed Successfully!");
@@ -8,10 +8,15 @@ import { fileURLToPath } from "node:url";
8
8
  import * as SDK from "../codecs/runtime/codec-sdk.mjs";
9
9
  import * as IntCodec from "../codecs/int.mjs";
10
10
  import * as JsonCodec from "../codecs/json.mjs";
11
+ import * as DjsonCodec from "../codecs/djson.mjs";
11
12
  import * as Utf8Codec from "../codecs/utf8.mjs";
12
13
  import * as Utf16Codec from "../codecs/utf16.mjs";
13
14
  import * as IeeeCodec from "../codecs/ieee.mjs";
14
15
  import * as UnitCodec from "../codecs/unit.mjs";
16
+ import {
17
+ valueToK,
18
+ valueWithEnvelopeToK
19
+ } from "../codecs/runtime/show-value.mjs";
15
20
 
16
21
  const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
17
22
 
@@ -49,6 +54,7 @@ console.log("2. Verifying in-memory parse -> enveloped value (P, v)...");
49
54
  for (const [name, codec, sampleInput] of [
50
55
  ["int.mjs", IntCodec, "42"],
51
56
  ["json.mjs", JsonCodec, '{"x": 1}'],
57
+ ["djson.mjs", DjsonCodec, '{"x": 1, "arr": [true, null, "hi"]}'],
52
58
  ["utf8.mjs", Utf8Codec, "hello"],
53
59
  ["utf16.mjs", Utf16Codec, "hello"],
54
60
  ["ieee.mjs", IeeeCodec, "3.14"],
@@ -68,6 +74,7 @@ console.log("3. Verifying CLI --help flag...");
68
74
  for (const relPath of [
69
75
  "codecs/int.mjs",
70
76
  "codecs/json.mjs",
77
+ "codecs/djson.mjs",
71
78
  "codecs/utf8.mjs",
72
79
  "codecs/utf16.mjs",
73
80
  "codecs/ieee.mjs",
@@ -111,6 +118,24 @@ console.log("4. Verifying CLI --parse and --print pipelines...");
111
118
  assert.deepEqual(JSON.parse(printRes.stdout.toString("utf8")), JSON.parse(jsonInput));
112
119
  }
113
120
 
121
+ // djson.mjs: dynamic JSON AST with singleton pattern
122
+ {
123
+ const djsonInput = JSON.stringify({
124
+ name: "k",
125
+ count: 42.5,
126
+ tags: ["fast", "typed"],
127
+ meta: { active: true, extra: null }
128
+ });
129
+ const parseRes = runCLI("codecs/djson.mjs", ["--parse"], djsonInput);
130
+ assert.equal(parseRes.status, 0);
131
+ const printRes = runCLI("codecs/djson.mjs", ["--print"], parseRes.stdout);
132
+ assert.equal(printRes.status, 0);
133
+ assert.deepEqual(
134
+ JSON.parse(printRes.stdout.toString("utf8")),
135
+ JSON.parse(djsonInput)
136
+ );
137
+ }
138
+
114
139
  // utf8.mjs: unicode text
115
140
  {
116
141
  const utf8Input = "Hello 🌍, ąęćłó! \nSecond line.";
@@ -183,4 +208,147 @@ console.log("6. Verifying pattern derivation via k type system (patternFromFilte
183
208
  assert.throws(() => SDK.patternFromFilter(".field"), /Main expression must be a filter or a type name/);
184
209
  }
185
210
 
211
+ // 7. Lazy Prefix-Free Wire Decoding
212
+ console.log("7. Verifying lazy prefix-free wire decoding...");
213
+ {
214
+ assert.equal(typeof SDK.decodeWireLazy, "function");
215
+
216
+ // Lazy decoding should instantiate root in sub-millisecond time
217
+ const sampleStr = "a".repeat(20000);
218
+ const parsedStr = Utf8Codec.parse(sampleStr);
219
+ const wireStr = SDK.encodeToWire(parsedStr, parsedStr.pattern);
220
+
221
+ const t0 = performance.now();
222
+ const decodedLazy = SDK.decodeWire(wireStr);
223
+ const constructMs = performance.now() - t0;
224
+ assert(
225
+ constructMs < 50,
226
+ `Lazy decode took ${constructMs}ms (expected <50ms)`
227
+ );
228
+ assert.equal(decodedLazy.value.tag, "cons");
229
+
230
+ // On-demand traversal through print()
231
+ const printedStr = Utf8Codec.print(decodedLazy.value);
232
+ assert.equal(printedStr, sampleStr);
233
+
234
+ // Lazy list of integers
235
+ const ints = Array.from({ length: 5000 }, (_, i) => i).join(",");
236
+ const sampleList = `[${ints}]`;
237
+ const parsedList = IntCodec.parse(sampleList);
238
+ const wireList = SDK.encodeToWire(parsedList, parsedList.pattern);
239
+ const decodedList = SDK.decodeWireLazy(wireList);
240
+ assert.equal(IntCodec.print(decodedList.value), sampleList);
241
+
242
+ // Verify stack-safe toJSON on deep variant chains
243
+ let deepVal = SDK.Value.product({});
244
+ for (let i = 0; i < 15000; i++) {
245
+ deepVal = SDK.Value.variant("step", deepVal);
246
+ }
247
+ const json = deepVal.toJSON();
248
+ assert(json && typeof json === "object");
249
+ }
250
+
251
+ // 8. High-Speed Direct Bitstream String Decoding & Bounded Rendering
252
+ console.log("8. Verifying fast string decoding & bounded rendering...");
253
+ {
254
+ const testStrings = [
255
+ "hello world",
256
+ "Zażółć gęślą jaźń",
257
+ "Unicode: 🚀🎉 𝒦-lang π ≈ 3.14159",
258
+ "a".repeat(50000),
259
+ ""
260
+ ];
261
+
262
+ for (const s of testStrings) {
263
+ const parsed = Utf8Codec.parse(s);
264
+ const wire = SDK.encodeToWire(parsed, parsed.pattern);
265
+ const decoded = SDK.decodeWire(wire);
266
+ const restored = Utf8Codec.print(decoded.value);
267
+ assert.equal(
268
+ restored,
269
+ s,
270
+ `String round-trip failed for "${s.slice(0, 20)}..."`
271
+ );
272
+ }
273
+
274
+ // Fast decoding performance: 50,000 ASCII chars in < 500ms
275
+ const bigAscii = "x".repeat(50000);
276
+ const bigParsed = Utf8Codec.parse(bigAscii);
277
+ const bigWire = SDK.encodeToWire(bigParsed, bigParsed.pattern);
278
+ const tStart = performance.now();
279
+ const bigDecoded = SDK.decodeWire(bigWire);
280
+ const bigRestored = Utf8Codec.print(bigDecoded.value);
281
+ const elapsed = performance.now() - tStart;
282
+ assert.equal(bigRestored.length, 50000);
283
+ assert(
284
+ elapsed < 500,
285
+ `Fast string decoding took ${elapsed}ms (expected < 500ms)`
286
+ );
287
+
288
+ // Bounded rendering: valueToK with limit
289
+ let deepVal = SDK.Value.product({});
290
+ for (let i = 0; i < 1000; i++) {
291
+ deepVal = SDK.Value.variant("item", deepVal);
292
+ }
293
+ const preview = valueToK(deepVal, 30);
294
+ assert(preview.endsWith("..."), "Output should end with '...'");
295
+ assert(preview.length <= 35, "Preview length should stay bounded");
296
+
297
+ // valueWithEnvelopeToK with limit
298
+ const enveloped = SDK.withPattern(deepVal, [["any", []]]);
299
+ const envPreview = valueWithEnvelopeToK(enveloped, 30);
300
+ assert(envPreview.includes("..."), "Preview should contain '...'");
301
+
302
+ // Ensure early termination: evaluating deep chain with limit 20
303
+ let hugeVal = SDK.Value.product({});
304
+ for (let i = 0; i < 50000; i++) {
305
+ hugeVal = SDK.Value.variant("v", hugeVal);
306
+ }
307
+ const tEarly0 = performance.now();
308
+ const earlyPreview = valueToK(hugeVal, 20);
309
+ const tEarlyElapsed = performance.now() - tEarly0;
310
+ assert(earlyPreview.endsWith("..."));
311
+ assert(
312
+ tEarlyElapsed < 50,
313
+ `Early termination took ${tEarlyElapsed}ms (expected < 50ms)`
314
+ );
315
+ }
316
+
317
+ // 9. DJSON Singleton Pattern Invariant & Round-Trip
318
+ console.log("9. Verifying DJSON singleton pattern invariant...");
319
+ {
320
+ assert(Array.isArray(DjsonCodec.DJSON_PATTERN));
321
+ assert.equal(DjsonCodec.DJSON_PATTERN.length, 29);
322
+
323
+ const samples = [
324
+ null,
325
+ true,
326
+ false,
327
+ 123.456,
328
+ "Hello world!",
329
+ [],
330
+ [1, "text", false, null],
331
+ {},
332
+ { key: "value", nested: [1, 2, { deep: true }] }
333
+ ];
334
+
335
+ for (const sample of samples) {
336
+ const jsonStr = JSON.stringify(sample);
337
+ const enveloped = DjsonCodec.parse(jsonStr);
338
+ assert.strictEqual(
339
+ enveloped.pattern,
340
+ DjsonCodec.DJSON_PATTERN,
341
+ "Every DJSON value must share the exact singleton pattern"
342
+ );
343
+ const wire = SDK.encodeToWire(enveloped, enveloped.pattern);
344
+ const decoded = SDK.decodeWire(wire);
345
+ const restored = DjsonCodec.print(decoded.value);
346
+ assert.deepEqual(
347
+ JSON.parse(restored),
348
+ sample,
349
+ `DJSON round-trip mismatch for: ${jsonStr}`
350
+ );
351
+ }
352
+ }
353
+
186
354
  console.log("OK - All codec tests passed.");