@fraczak/k 6.7.0 → 6.9.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.
@@ -16,18 +16,21 @@ import {
16
16
  isMainEntrypoint,
17
17
  lineHasExplicitContinuation,
18
18
  lineTerminatesSnippet,
19
- promptForState
19
+ promptForState,
20
+ registerCodec
20
21
  } from "../repl.mjs";
21
22
  import { decodeObject, objectToFunction } from "../object.mjs";
22
23
  import { Value } from "../Value.mjs";
23
24
 
24
25
  const state = createState();
25
26
 
26
- let output = await evaluateInput(":type nat = <{} zero, nat succ>", state);
27
- assert.match(output[0], /^\$ nat = @/);
27
+ let output = await evaluateInput("$ nat = < {} zero, nat succ >;", state);
28
+ output = await evaluateInput(":type nat", state);
29
+ assert.match(output[0], /^\$ nat = /);
28
30
 
29
- output = await evaluateInput(":rel succ = |succ", state);
30
- assert.match(output[0], /^succ = @/);
31
+ output = await evaluateInput("succ = | succ;", state);
32
+ output = await evaluateInput(":d succ", state);
33
+ assert.match(output[0], /^succ = /);
31
34
 
32
35
  output = await evaluateInput(":codes", state);
33
36
  assert.match(output[0], /^nat = @/m);
@@ -82,8 +85,8 @@ assert.equal(analyzeRawSnippet("a =").kind, "incomplete");
82
85
  assert.equal(analyzeRawSnippet("{} | succ").kind, "withMain");
83
86
 
84
87
  const offsetState = createState();
85
- await evaluateInput(":type ab = <{} 1, {} 2>", offsetState);
86
- await evaluateInput(":type bool = <{} true, {} false>", offsetState);
88
+ await evaluateInput("$ ab = < {} 1, {} 2 >;", offsetState);
89
+ await evaluateInput("$ bool = < {} true, {} false >;", offsetState);
87
90
  await assert.rejects(
88
91
  () => evaluateInput("$ab", offsetState),
89
92
  /Type Error in 'filter' \(lines 1:1\.\.\.1:4\)/
@@ -100,10 +103,10 @@ output = await evaluateInput(`:klib ${libPath}`, state);
100
103
  assert.equal(output[0], `saved ${libPath}`);
101
104
 
102
105
  const recursiveState = createState();
103
- await evaluateInput(":type $ nat = < {}_, nat 0, nat 1 >;", recursiveState);
104
- await evaluateInput(":rel 0? = $ nat < / _ {}|0, / 0 0? >;", recursiveState);
106
+ await evaluateInput("$ nat = < {} _, nat 0, nat 1 >;", recursiveState);
107
+ await evaluateInput("0? = $ nat < / _ {} | 0, / 0 0? >;", recursiveState);
105
108
  const staleZeroHash = recursiveState.relAliases["0?"];
106
- await evaluateInput(":rel 0? = $ nat < / _ {}|_, / 0 0? >;", recursiveState);
109
+ await evaluateInput("0? = $ nat < / _ {} | _, / 0 0? >;", recursiveState);
107
110
  const zeroHash = recursiveState.relAliases["0?"];
108
111
  assert.notEqual(zeroHash, staleZeroHash);
109
112
  await evaluateInput("{}|_|0|0 $nat", recursiveState);
@@ -141,8 +144,7 @@ output = await evaluateInput(":C nat", loadedSource);
141
144
  assert.match(output[0], /^\$ nat = < nat succ, @[^ ]+ zero >; -- @/);
142
145
 
143
146
  const codecState = createState();
144
- output = await evaluateInput(":type bool = <{} true, {} false>", codecState);
145
- assert.match(output[0], /^\$ bool = @/);
147
+ output = await evaluateInput("$ bool = < {} true, {} false >;", codecState);
146
148
  const boolHash = codecState.typeAliases.bool;
147
149
  const valueModuleUrl = pathToFileURL(path.resolve("Value.mjs")).href;
148
150
  fs.writeFileSync(codecPath, `
@@ -158,6 +160,9 @@ export function parse(text) {
158
160
  }
159
161
 
160
162
  export function print(value) {
163
+ if (!value || (value.tag !== "true" && value.tag !== "false")) {
164
+ throw new Error("expected true or false");
165
+ }
161
166
  return value.tag;
162
167
  }
163
168
  `);
@@ -170,59 +175,52 @@ assert(completions.includes(":codec list"));
170
175
  completions = completeInput(`:codec load ${tmpDir}/bool`, codecState)[0];
171
176
  assert(completions.includes(`:codec load ${codecPath}`));
172
177
  output = await evaluateInput(`:codec load ${codecPath}`, codecState);
173
- assert.equal(output[0], `loaded codec yn for ${boolHash}`);
178
+ assert.equal(output[0], `loaded codec bool-codec.mjs`);
174
179
  output = await evaluateInput(":codec list", codecState);
175
- assert.match(output[0], new RegExp(`^yn ${boolHash.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`));
176
- completions = completeInput(":input bo", codecState)[0];
177
- assert(completions.includes(":input bool"));
178
- completions = completeInput(":input bool y", codecState)[0];
179
- assert(completions.includes(":input bool yn"));
180
- output = await evaluateInput(":input bool yn", codecState);
181
- assert.equal(output[0], `input ${boolHash} using yn: enter value text`);
180
+ assert.match(output[0], /^bool-codec\.mjs/);
181
+ completions = completeInput(":input", codecState)[0];
182
+ assert(completions.includes(":input"));
183
+ completions = completeInput(":input ", codecState)[0];
184
+ assert(completions.includes(":input bool-codec.mjs"));
185
+ completions = completeInput(":input b", codecState)[0];
186
+ assert(completions.includes(":input bool-codec.mjs"));
187
+ output = await evaluateInput(":input bool-codec.mjs", codecState);
188
+ assert.equal(promptForState(codecState), "bool-codec.mjs> ");
182
189
  output = await evaluateInput("true", codecState);
183
190
  assert.match(output[0], /\{\}\|true \?</);
184
- assert.match(output[0], /yn: true/);
191
+ assert.match(output[0], /bool-codec\.mjs: true/);
185
192
  output = await evaluateInput("{} | false", codecState);
186
- assert.doesNotMatch(output[0], /yn: false/);
193
+ assert.match(output[0], /bool-codec\.mjs: false/);
194
+ output = await evaluateInput("{}", codecState);
195
+ assert.doesNotMatch(output[0], /bool-codec\.mjs:/);
187
196
 
188
197
  const utf8State = createState();
189
198
  output = await evaluateInput(":load core.k", utf8State);
190
199
  assert.equal(output[0], "loaded core.k");
191
200
  output = await evaluateInput(":codec load ./codecs/utf8.mjs", utf8State);
192
- assert.equal(output[0], `loaded codec utf8 for ${utf8State.typeAliases.string}`);
193
- output = await evaluateInput(":input string utf8", utf8State);
194
- assert.equal(output[0], `input ${utf8State.typeAliases.string} using utf8: enter value text`);
195
- output = await evaluateInput("hello", utf8State);
196
- assert.match(output[0], /utf8: hello/);
201
+ assert.equal(output[0], `loaded codec utf8.mjs`);
202
+ output = await evaluateInput(":input utf8.mjs hello", utf8State);
203
+ assert.match(output[0], /utf8\.mjs: hello/);
197
204
 
198
205
  const jsonState = createState();
199
- output = await evaluateInput(":type bool = <{} true, {} false>", jsonState);
200
- assert.match(output[0], /^\$ bool = @/);
201
206
  output = await evaluateInput(":codec load ./codecs/json.mjs", jsonState);
202
- assert.match(output[0], /^loaded codec json for all types/);
207
+ assert.match(output[0], /^loaded codec json\.mjs/);
203
208
  output = await evaluateInput(":codec list", jsonState);
204
- assert.match(output[0], /^json all /);
205
- completions = completeInput(":input bool j", jsonState)[0];
206
- assert(completions.includes(":input bool json"));
207
- output = await evaluateInput(":input bool", jsonState);
208
- assert.equal(output[0], `input ${jsonState.typeAliases.bool}: enter value text`);
209
- assert.equal(promptForState(jsonState), "json> ");
209
+ assert.match(output[0], /^json\.mjs/);
210
+ completions = completeInput(":input j", jsonState)[0];
211
+ assert(completions.includes(":input json.mjs"));
212
+ output = await evaluateInput(":input json.mjs", jsonState);
213
+ assert.equal(promptForState(jsonState), "json.mjs> ");
210
214
  output = await evaluateInput("true", jsonState);
211
215
  assert.match(output[0], /\{\}\|true \?</);
212
- assert.match(output[0], /json: true/);
216
+ assert.match(output[0], /json\.mjs: true/);
213
217
  assert.equal(promptForState(jsonState), "> ");
214
- output = await evaluateInput(":input <{}true,{}false>", jsonState);
215
- assert.equal(output[0], `input ${jsonState.typeAliases.bool}: enter value text`);
216
- assert.equal(promptForState(jsonState), "json> ");
217
- output = await evaluateInput("false", jsonState);
218
+ output = await evaluateInput(":input json.mjs false", jsonState);
218
219
  assert.match(output[0], /\{\}\|false \?</);
219
- assert.match(output[0], /json: false/);
220
+ assert.match(output[0], /json\.mjs: false/);
220
221
  assert.equal(promptForState(jsonState), "> ");
221
- output = await evaluateInput(":input {bool left, bool right} json", jsonState);
222
- assert.match(output[0], /^input @[^ ]+ using json: enter value text$/);
223
- assert.equal(promptForState(jsonState), "json> ");
224
- output = await evaluateInput('{"left":true,"right":false}', jsonState);
225
- assert.match(output[0], /json: \{"left":true,"right":false\}/);
222
+ output = await evaluateInput(':input json.mjs {"left":true,"right":false}', jsonState);
223
+ assert.match(output[0], /json\.mjs: \{"left":true,"right":false\}/);
226
224
  assert.equal(promptForState(jsonState), "> ");
227
225
 
228
226
  const replPath = fileURLToPath(new URL("../repl.mjs", import.meta.url));
@@ -256,7 +254,7 @@ assert.equal(output[0], "(none)");
256
254
 
257
255
  const shorthand = createState();
258
256
  output = await evaluateInput("$ nat = <{} zero, nat succ>\n; succ = |succ\n;", shorthand);
259
- assert.deepEqual(output, []);
257
+ assert.match(output[0], /\/\* comp: .* \*\//);
260
258
  output = await evaluateInput(":codes", shorthand);
261
259
  assert.match(output[0], /^nat = @/m);
262
260
  output = await evaluateInput(":rels", shorthand);
@@ -266,7 +264,7 @@ assert.match(output[0], /^\{\}\|false \?</);
266
264
  assert.match(output[0], / false/);
267
265
  assert.match(output[0], / true/);
268
266
  output = await evaluateInput("$ maybe = <{} none, {} some>;", shorthand);
269
- assert.deepEqual(output, []);
267
+ assert.match(output[0], /\/\* comp: .* \*\//);
270
268
  output = await evaluateInput(":codes", shorthand);
271
269
  assert.match(output[0], /^maybe = @/m);
272
270
  output = await evaluateInput("{} | true not", shorthand);
@@ -279,8 +277,8 @@ await assert.rejects(
279
277
 
280
278
  // Verify WebAssembly in-process backend supports TCO / stack safety on deep recursion
281
279
  const tcoState = createState();
282
- await evaluateInput(":type nat = <{} zero, nat succ>", tcoState);
283
- await evaluateInput(":rel countdown = $nat < /zero {}|zero, /succ countdown >", tcoState);
280
+ await evaluateInput("$ nat = < {} zero, nat succ >;", tcoState);
281
+ await evaluateInput("countdown = $ nat < / zero {} | zero, / succ countdown >;", tcoState);
284
282
 
285
283
  let deepNat = Value.variant("zero", Value.product({}, [["closed-product", []]]), [["open-union", [["zero", 1]]], ["closed-product", []]]);
286
284
  for (let i = 0; i < 5000; i++) {
@@ -301,39 +299,45 @@ assert.match(output[0], /^\{\}\|_\|0\|1\|0\|1/);
301
299
  output = await evaluateInput("{10 int x, 5 int y} plus", arithState);
302
300
  assert.match(output[0], /^\{\}\|_\|1\|1\|1\|1\|\+/);
303
301
 
304
- // Verify built-in codecs, :codecs, :codec define, and :codec unload
302
+ // Verify codecs loading, listing, and unloading
305
303
  const codecTestState = createState();
306
304
  output = await evaluateInput(":codecs", codecTestState);
307
305
  assert.equal(output[0], "(none)");
308
306
 
309
- output = await evaluateInput(":codec load int", codecTestState);
310
- assert.match(output[0], /^loaded codec int for @/);
307
+ output = await evaluateInput(":codec load ./codecs/int.mjs", codecTestState);
308
+ assert.match(output[0], /^loaded codec int\.mjs/);
311
309
 
312
310
  output = await evaluateInput(":codecs", codecTestState);
313
- assert.match(output[0], /^int @/);
311
+ assert.match(output[0], /^int\.mjs/);
314
312
 
315
313
  completions = completeInput(":codec un", codecTestState)[0];
316
314
  assert(completions.includes(":codec unload"));
317
315
  completions = completeInput(":codec unload ", codecTestState)[0];
318
- assert(completions.includes(":codec unload int"));
319
- completions = completeInput(":codec load i", codecTestState)[0];
320
- assert(completions.includes(":codec load int"));
316
+ assert(completions.includes(":codec unload int.mjs"));
317
+ completions = completeInput(":codec load ./codecs/i", codecTestState)[0];
318
+ assert(completions.includes(":codec load ./codecs/int.mjs"));
321
319
 
322
- output = await evaluateInput(":codec unload int", codecTestState);
323
- assert.equal(output[0], "unloaded codec int");
320
+ output = await evaluateInput(":codec unload int.mjs", codecTestState);
321
+ assert.equal(output[0], "unloaded codec int.mjs");
324
322
  output = await evaluateInput(":codecs", codecTestState);
325
323
  assert.equal(output[0], "(none)");
326
324
 
327
- output = await evaluateInput(":type bool = <{} true, {} false>", codecTestState);
328
- output = await evaluateInput(
329
- `:codec define yn bool ({ parse: (text) => Value.variant(text.trim() === "yes" ? "true" : "false", Value.product({})), print: (v) => v.tag === "true" ? "YES" : "NO" })`,
330
- codecTestState
331
- );
332
- assert.match(output[0], /^defined codec yn for @/);
333
-
334
- output = await evaluateInput(":input bool yn", codecTestState);
325
+ output = await evaluateInput("$ bool = < {} true, {} false >;", codecTestState);
326
+ registerCodec(codecTestState, {
327
+ name: "yn.mjs",
328
+ parse: (text) => Value.variant(text.trim() === "yes" ? "true" : "false", Value.product({})),
329
+ print: (v) => {
330
+ if (v.tag !== "true" && v.tag !== "false") throw new Error("not a bool");
331
+ return v.tag === "true" ? "YES" : "NO";
332
+ }
333
+ }, "yn.mjs");
334
+
335
+ output = await evaluateInput(":input yn.mjs", codecTestState);
336
+ assert.equal(promptForState(codecTestState), "yn.mjs> ");
335
337
  output = await evaluateInput("yes", codecTestState);
336
- assert.match(output[0], /yn: YES/);
338
+ assert.match(output[0], /yn\.mjs: YES/);
339
+ output = await evaluateInput(":input yn.mjs no", codecTestState);
340
+ assert.match(output[0], /yn\.mjs: NO/);
337
341
 
338
342
  // Projection on product with variant field (avoids filter variable collisions)
339
343
  const projState = createState();
@@ -348,48 +352,88 @@ assert.match(output[0], /^\{\{\}\|a a, \{\} y\}/);
348
352
  output = await evaluateInput(".a", projState2);
349
353
  assert.match(output[0], /^\{\}\|a/);
350
354
 
351
- // Auto-loading dependencies and bidirectional type aliasing tests
352
- const autoLoadState = createState();
353
- output = await evaluateInput(":codec load int", autoLoadState);
354
- assert.match(output[0], /auto-loaded (?:Examples\/)?arithmetics\.k/);
355
- assert.ok(autoLoadState.typeAliases.int, "int type alias should be registered");
356
- output = await evaluateInput(":input int", autoLoadState);
357
- output = await evaluateInput("123", autoLoadState);
358
- assert.match(output[0], /int: 123/);
355
+ // Explicit loading of dependencies and codec testing (no auto-loading)
356
+ const intState = createState();
357
+ await evaluateInput(":load Examples/arithmetics.k", intState);
358
+ output = await evaluateInput(":codec load ./codecs/int.mjs", intState);
359
+ assert.match(output[0], /^loaded codec int\.mjs/);
360
+ output = await evaluateInput(":input int.mjs", intState);
361
+ output = await evaluateInput("123", intState);
362
+ assert.match(output[0], /int\.mjs: 123/);
363
+ output = await evaluateInput(":input int.mjs", intState);
364
+ output = await evaluateInput("[0,1,2]", intState);
365
+ assert.match(output[0], /int\.mjs: \[0,1,2\]/);
359
366
 
360
367
  const utf8AutoState = createState();
361
- output = await evaluateInput(":codec load utf8", utf8AutoState);
362
- assert.match(output[0], /auto-loaded core\.k/);
363
- assert.ok(utf8AutoState.typeAliases.utf8, "utf8 type alias should be registered");
364
- assert.ok(utf8AutoState.typeAliases.string, "string type alias should be registered");
365
- assert.equal(utf8AutoState.typeAliases.utf8, utf8AutoState.typeAliases.string);
366
- output = await evaluateInput(":input utf8", utf8AutoState);
368
+ await evaluateInput(":load core.k", utf8AutoState);
369
+ output = await evaluateInput(":codec load ./codecs/utf8.mjs", utf8AutoState);
370
+ assert.match(output[0], /^loaded codec utf8\.mjs/);
371
+ output = await evaluateInput(":input utf8.mjs", utf8AutoState);
367
372
  output = await evaluateInput("test utf8", utf8AutoState);
368
- assert.match(output[0], /utf8: test utf8/);
373
+ assert.match(output[0], /utf8\.mjs: test utf8/);
369
374
 
370
375
  const ieeeAutoState = createState();
371
- output = await evaluateInput(":codec load ieee", ieeeAutoState);
372
- assert.match(output[0], /auto-loaded (?:Examples\/)?ieee\.k/);
376
+ await evaluateInput(":load Examples/ieee.k", ieeeAutoState);
377
+ output = await evaluateInput(":codec load ./codecs/ieee.mjs", ieeeAutoState);
378
+ assert.match(output[0], /^loaded codec ieee\.mjs/);
373
379
  assert.ok(ieeeAutoState.typeAliases.ieee, "ieee type alias should be registered");
374
- assert.ok(ieeeAutoState.typeAliases.float64, "float64 type alias should be registered");
375
- assert.equal(ieeeAutoState.typeAliases.ieee, ieeeAutoState.typeAliases.float64);
376
380
 
377
381
  const jsonInputState = createState();
378
382
  await evaluateInput(":load core.k", jsonInputState);
379
- await evaluateInput(":load ieee.k", jsonInputState);
380
- await evaluateInput(":codec load json", jsonInputState);
381
- output = await evaluateInput(":input {string a, float64 n}", jsonInputState);
382
- assert.match(output[0], /enter value text/);
383
- output = await evaluateInput('{"a":"Woj","n":123}', jsonInputState);
384
- assert.match(output[0], /json: \{"a":"Woj","n":123\}/);
383
+ await evaluateInput(":load Examples/ieee.k", jsonInputState);
384
+ await evaluateInput(":codec load ./codecs/json.mjs", jsonInputState);
385
+ output = await evaluateInput(':input json.mjs {"a":"Woj","n":123}', jsonInputState);
386
+ assert.match(output[0], /json\.mjs: \{"a":"Woj","n":123\}/);
387
+
388
+ // Verify standalone codec input, one-line and interactive modes, and multi-codec printing
389
+ const multiCodecState = createState();
390
+ await evaluateInput(":codec load ./codecs/json.mjs", multiCodecState);
391
+ await evaluateInput(":codec load ./codecs/utf8.mjs", multiCodecState);
392
+ await evaluateInput(":codec load ./codecs/int.mjs", multiCodecState);
393
+
394
+ // 1. One-line JSON input: :input json.mjs {"hello":"world"}
395
+ output = await evaluateInput(':input json.mjs {"hello":"world"}', multiCodecState);
396
+ assert.match(output[0], /json\.mjs: \{"hello":"world"\}/);
397
+
398
+ // 2. Interactive JSON input: :input json.mjs then {"anything": [1, 2, 3]}
399
+ output = await evaluateInput(":input json.mjs", multiCodecState);
400
+ assert.equal(promptForState(multiCodecState), "json.mjs> ");
401
+ output = await evaluateInput('{"anything": [1, 2, 3]}', multiCodecState);
402
+ assert.match(output[0], /json\.mjs: \{"anything":\[1,2,3\]\}/);
403
+ assert.equal(promptForState(multiCodecState), "> ");
404
+
405
+ // 3. Integer scalar and list input
406
+ output = await evaluateInput(":input int.mjs 42", multiCodecState);
407
+ assert.match(output[0], /int\.mjs: 42/);
408
+
409
+ output = await evaluateInput(":input int.mjs [0,1,2]", multiCodecState);
410
+ assert.match(output[0], /int\.mjs: \[0,1,2\]/);
411
+
412
+ // 4. Bare :input displays usage and loaded codecs
413
+ const bareInputState = createState();
414
+ output = await evaluateInput(":input", bareInputState);
415
+ assert.match(output[0], /^Usage: :input <codec\.mjs> \[text\]/);
416
+ assert.match(output[1], /Loaded codecs:/);
417
+
418
+ // 5. Codec with leading dollar sign is accepted
419
+ output = await evaluateInput(":input $ int.mjs 42", multiCodecState);
420
+ assert.match(output[0], /int\.mjs: 42/);
421
+ output = await evaluateInput(":input $int.mjs 100", multiCodecState);
422
+ assert.match(output[0], /int\.mjs: 100/);
423
+
424
+ // 6. Unknown codec throws descriptive error
425
+ await assert.rejects(
426
+ () => evaluateInput(":input nonexistent 123", multiCodecState),
427
+ /Codec 'nonexistent' is not loaded/
428
+ );
385
429
 
386
430
  const unitState = createState();
387
- output = await evaluateInput(":codec load unit", unitState);
388
- assert.match(output[0], /loaded codec unit/);
389
- output = await evaluateInput(":input unit", unitState);
390
- assert.match(output[0], /enter value text/);
431
+ output = await evaluateInput(":codec load ./codecs/unit.mjs", unitState);
432
+ assert.match(output[0], /^loaded codec unit\.mjs/);
433
+ output = await evaluateInput(":input unit.mjs", unitState);
434
+ assert.equal(promptForState(unitState), "unit.mjs> ");
391
435
  output = await evaluateInput("{}", unitState);
392
- assert.match(output[0], /unit: \{\}/);
436
+ assert.match(output[0], /unit\.mjs: \{\}/);
393
437
 
394
438
  const polyAutoState = createState();
395
439
  output = await evaluateInput(":load Examples/poly.k", polyAutoState);
@@ -397,28 +441,20 @@ assert.equal(output[0], "loaded Examples/poly.k");
397
441
  assert.ok(polyAutoState.relAliases.reverse, "poly relations should be loaded");
398
442
  assert.ok(polyAutoState.typeAliases.int, "arithmetics int should be auto-loaded");
399
443
 
400
- // Verify :timing and :time commands
444
+ // Verify timing reporting is always on and :time command
401
445
  const timingState = createState();
402
- output = await evaluateInput(":time ()", timingState);
403
- assert.equal(output[0], "{} ?{}");
404
- assert.match(output[1], /\/\* comp: .*, exec: .* \*\//);
405
- assert.ok(timingState.lastTiming);
406
- assert.ok(typeof timingState.lastTiming.compileMs === "number");
407
- assert.ok(typeof timingState.lastTiming.executeMs === "number");
408
-
409
- output = await evaluateInput(":timing on", timingState);
410
- assert.equal(output[0], "timing reporting enabled");
411
446
  assert.equal(timingState.showTiming, true);
447
+
412
448
  output = await evaluateInput("()", timingState);
413
449
  assert.equal(output[0], "{} ?{}");
414
450
  assert.match(output[1], /\/\* comp: .*, exec: .* \*\//);
415
451
 
416
- output = await evaluateInput(":timing off", timingState);
417
- assert.equal(output[0], "timing reporting disabled");
418
- assert.equal(timingState.showTiming, false);
419
- output = await evaluateInput("()", timingState);
420
- assert.equal(output.length, 1);
452
+ output = await evaluateInput(":time ()", timingState);
421
453
  assert.equal(output[0], "{} ?{}");
454
+ assert.match(output[1], /\/\* comp: .*, exec: .* \*\//);
455
+ assert.ok(timingState.lastTiming);
456
+ assert.ok(typeof timingState.lastTiming.compileMs === "number");
457
+ assert.ok(typeof timingState.lastTiming.executeMs === "number");
422
458
 
423
459
  fs.rmSync(tmpDir, { recursive: true, force: true });
424
460
  fs.rmSync(symlinkPath, { force: true });
@@ -25,3 +25,20 @@ assert.equal(valueToK(foreignTwo), "{}|_|0|1|+");
25
25
 
26
26
  const decoded = decodeWire(encodeToWire(foreignTwo, INT_PATTERN)).value;
27
27
  assert.equal(printInt(decoded), "2");
28
+
29
+ const { INT_LIST_PATTERN, parse: parseIntCodec } = await import("../codecs/int.mjs");
30
+ const { valueWithEnvelopeToK } = await import("../codecs/runtime/show-value.mjs");
31
+
32
+ const parsedList = parseIntCodec("[0,1,2]");
33
+ const listWire = encodeToWire(parsedList, INT_LIST_PATTERN);
34
+ const decodedList = decodeWire(listWire).value;
35
+ assert.equal(
36
+ valueWithEnvelopeToK(decodedList),
37
+ "{{}|_|0|+ car, {{}|_|1|+ car, {{}|_|0|1|+ car, {}|nil cdr}|cons cdr}|cons cdr}|cons ?<{<<X1 0, X1 1, {}=X2 _>=X1 +, X1 -> car, X0 cdr} cons, X2 nil>=X0"
38
+ );
39
+ assert.equal(printInt(decodedList), "[0,1,2]");
40
+
41
+ const emptyList = parseIntCodec("[]");
42
+ const emptyWire = encodeToWire(emptyList, INT_LIST_PATTERN);
43
+ const decodedEmpty = decodeWire(emptyWire).value;
44
+ assert.equal(printInt(decodedEmpty), "[]");
@@ -172,6 +172,7 @@ assert.strictEqual(patTree.open, true, "Root pattern node must be open");
172
172
  assert(patTree.textContent.includes("recursive"), "Recursive cycle must be detected and marked 'recursive'");
173
173
 
174
174
  // 5. Test createOutputEntryElement
175
+ variantVal.pattern = recursivePattern;
175
176
  const entryEl = createOutputEntryElement({
176
177
  value: variantVal,
177
178
  rawText: "{}|_|1|0|1|+ ?<<X0 0, X0 1, {} _>=X0 +, X0 ->\nint: 11",
@@ -180,6 +181,8 @@ const entryEl = createOutputEntryElement({
180
181
 
181
182
  assert(entryEl.querySelector(".k-tree-view"), "Tree view container must exist");
182
183
  assert(entryEl.querySelector(".k-raw-view"), "Raw view container must exist");
184
+ assert.strictEqual(entryEl.querySelector(".k-tree-value-section")?.open, false, "Value section must be collapsed by default");
185
+ assert.strictEqual(entryEl.querySelector(".k-tree-pat-section")?.open, false, "Pattern section must be collapsed by default");
183
186
  assert(entryEl.textContent.includes("int: 11"), "Codec output must be visible in container");
184
187
 
185
188
  console.log("OK - All tree view unit tests passed.");
@@ -1,163 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Example: Demonstrating the codec pipeline
4
- *
5
- * Shows how to:
6
- * 1. Define types with canonical names
7
- * 2. Create values
8
- * 3. Encode to binary
9
- * 4. Pass through "compiled program" (identity)
10
- * 5. Decode from binary
11
- * 6. Print result
12
- */
13
-
14
- import { deriveClosedPattern } from './runtime/codec.mjs';
15
- import { encodeToWire, decodeWire } from './runtime/prefix-codec.mjs';
16
- import { patternToPropertyList } from './runtime/pattern-json.mjs';
17
- import { Value, isVariant } from '../Value.mjs';
18
- import hash from '../hash.mjs';
19
- import { argv, exit } from 'node:process';
20
-
21
- function usage() {
22
- console.log(`Usage: ${argv[1]}`);
23
- console.log(" Run a self-contained demonstration of the codec pipeline.");
24
- console.log("");
25
- console.log("Options:");
26
- console.log(" -h, --help Show this help.");
27
- }
28
-
29
- if (argv.slice(2).includes("-h") || argv.slice(2).includes("--help")) {
30
- usage();
31
- exit(0);
32
- }
33
-
34
- console.log('=== K Codec Pipeline Example ===\n');
35
-
36
- // ============================================================================
37
- // Step 1: Define types (normally from compiler)
38
- // ============================================================================
39
-
40
- const types = {};
41
-
42
- // Unit: {}
43
- const unitDef = { code: 'product', product: {} };
44
- const unitName = hash('$C0={};');
45
- types[unitName] = unitDef;
46
-
47
- // Bool: <{} false, {} true>
48
- const boolDef = {
49
- code: 'union',
50
- union: { false: unitName, true: unitName }
51
- };
52
- const boolName = hash(`$C0=<${unitName}"false",${unitName}"true">;`);
53
- types[boolName] = boolDef;
54
-
55
- // Maybe: <{} nothing, Bool something>
56
- const maybeDef = {
57
- code: 'union',
58
- union: { nothing: unitName, something: boolName }
59
- };
60
- const maybeName = hash(
61
- `$C0=<${unitName}"nothing",${boolName}"something">;`
62
- );
63
- types[maybeName] = maybeDef;
64
-
65
- console.log('Types defined:');
66
- console.log(' unit:', unitName);
67
- console.log(' bool:', boolName);
68
- console.log(' maybe:', maybeName);
69
- console.log();
70
-
71
- // ============================================================================
72
- // Step 2: Type resolver
73
- // ============================================================================
74
-
75
- function resolveType(typeName) {
76
- const def = types[typeName];
77
- if (!def) {
78
- throw new Error(`Unknown type: ${typeName}`);
79
- }
80
- return def;
81
- }
82
-
83
- // ============================================================================
84
- // Step 3: Create input value: Maybe Bool = something(true)
85
- // ============================================================================
86
-
87
- const inputValue = Value.variant(
88
- 'something',
89
- Value.variant('true', Value.product({}))
90
- );
91
-
92
- console.log('Input value:', inputValue.toString());
93
- console.log();
94
-
95
- const maybePattern = patternToPropertyList(
96
- deriveClosedPattern(maybeName, maybeDef, resolveType)
97
- );
98
-
99
- // ============================================================================
100
- // Step 4: Parse (TEXT → BINARY)
101
- // ============================================================================
102
-
103
- console.log('--- PARSE (text → binary) ---');
104
- const binaryInput = encodeToWire(inputValue, maybePattern);
105
- console.log('Binary size:', binaryInput.length, 'bytes');
106
- console.log(' Bytes:', binaryInput.toString('hex'));
107
- console.log();
108
-
109
- // ============================================================================
110
- // Step 5: Compiled k-program (BINARY → BINARY)
111
- // ============================================================================
112
-
113
- console.log('--- COMPILED PROGRAM (binary → binary) ---');
114
-
115
- // Simulate a compiled k-program: decode → transform → encode
116
- const { value: programInput } = decodeWire(binaryInput);
117
-
118
- console.log('Program sees value:', programInput.toString());
119
-
120
- // Example transformation: NOT operation (flip bool inside Maybe)
121
- let programOutput;
122
- if (isVariant(programInput) && programInput.tag === 'something') {
123
- const innerBool = programInput.value;
124
- if (isVariant(innerBool)) {
125
- const flipped = Value.variant(
126
- innerBool.tag === 'true' ? 'false' : 'true',
127
- innerBool.value
128
- );
129
- programOutput = Value.variant('something', flipped);
130
- }
131
- } else {
132
- programOutput = programInput; // nothing → nothing
133
- }
134
-
135
- console.log('Program output value:', programOutput.toString());
136
-
137
- // Encode output
138
- const binaryOutput = encodeToWire(programOutput, maybePattern);
139
-
140
- console.log('Binary output size:', binaryOutput.length, 'bytes');
141
- console.log(' Bytes:', binaryOutput.toString('hex'));
142
- console.log();
143
-
144
- // ============================================================================
145
- // Step 6: Print (BINARY → TEXT)
146
- // ============================================================================
147
-
148
- console.log('--- PRINT (binary → text) ---');
149
- const { value: finalValue } = decodeWire(binaryOutput);
150
-
151
- console.log('Output value:', finalValue.toString());
152
- console.log();
153
-
154
- // ============================================================================
155
- // Summary
156
- // ============================================================================
157
-
158
- console.log('=== Pipeline Summary ===');
159
- console.log('Input: ', inputValue.toString(), '(true inside Maybe)');
160
- console.log('Transform: NOT operation on inner bool');
161
- console.log('Output: ', finalValue.toString(), '(false inside Maybe)');
162
- console.log();
163
- console.log('Binary representations are canonical and type-safe!');
@@ -1,30 +0,0 @@
1
- import fs from "node:fs";
2
- import { parse as parseScript } from "../../parser.mjs";
3
- import codes from "../../codes.mjs";
4
- import { deriveClosedPattern } from "./codec.mjs";
5
- import { patternToPropertyList } from "./pattern-json.mjs";
6
-
7
- const IEEE_SCRIPT = fs.readFileSync(new URL("../../Examples/ieee.k", import.meta.url), "utf8");
8
-
9
- function loadFloat64Pattern() {
10
- const { defs } = parseScript(IEEE_SCRIPT);
11
- const { codes: finalizedCodes, representatives } = codes.finalize(defs.codes);
12
- const typeName = representatives.float64 || "float64";
13
- const typeInfo = finalizedCodes[typeName];
14
- if (!typeInfo) {
15
- throw new Error("Could not resolve float64 type from Examples/ieee.k");
16
- }
17
- const resolveType = (name) => {
18
- const type = finalizedCodes[name];
19
- if (!type) {
20
- throw new Error(`Unknown IEEE helper type '${name}'`);
21
- }
22
- return type;
23
- };
24
- return patternToPropertyList(deriveClosedPattern(typeName, typeInfo, resolveType));
25
- }
26
-
27
- const FLOAT64_PATTERN = loadFloat64Pattern();
28
-
29
- export { FLOAT64_PATTERN, loadFloat64Pattern };
30
- export default { FLOAT64_PATTERN, loadFloat64Pattern };