@fraczak/k 6.10.6 → 7.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 (83) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/CLAUDE.md +1 -1
  3. package/DOCS/BIG_PICTURE.md +156 -172
  4. package/DOCS/BINARY_CODEC_STATUS_AND_TODO.md +3 -3
  5. package/DOCS/CODEC_ARCHITECTURE.md +2 -2
  6. package/DOCS/DICTIONARY.md +243 -250
  7. package/DOCS/DIVERGENCE_DETECTION.md +0 -1
  8. package/DOCS/FILE_FORMATS.md +5 -5
  9. package/DOCS/KIR_SPECIALIZATION_DESIGN.md +1 -1
  10. package/DOCS/OBJECT_FILE_AND_PATTERN.md +12 -12
  11. package/DOCS/PATTERNS.md +17 -17
  12. package/DOCS/PATTERNS_AND_TYPED_VALUES.md +1 -1
  13. package/DOCS/REPL.md +9 -19
  14. package/DOCS/TYPE_DERIVATION.md +2 -8
  15. package/DOCS/WEB_REPL_REQUIREMENTS.md +8 -8
  16. package/DOCS/book/01-introduction.md +8 -11
  17. package/DOCS/book/02-syntax-and-values.md +19 -20
  18. package/DOCS/book/03-types-as-finite-automata.md +42 -43
  19. package/DOCS/book/04-partial-functions-and-composition.md +2 -3
  20. package/DOCS/book/05-typing-filters-and-normalization.md +44 -46
  21. package/DOCS/book/06-values-in-memory.md +10 -10
  22. package/DOCS/book/08-operational-semantics-and-execution.md +21 -26
  23. package/DOCS/book/09-compiler-architecture.md +11 -11
  24. package/DOCS/book/10-from-ast-to-intermediate-representation.md +6 -7
  25. package/DOCS/book/14-canonical-serialization.md +2 -2
  26. package/DOCS/book/appendix-b-incorporating-external-predefined-types-and-functions.md +17 -17
  27. package/DOCS/book.md +5 -5
  28. package/Examples/arithmetics.k +49 -153
  29. package/Examples/bnat.k +13 -13
  30. package/Examples/byte.k +2 -50
  31. package/Examples/ieee.k +408 -423
  32. package/Examples/karatsuba-mult.k +30 -34
  33. package/Examples/nat.k +5 -6
  34. package/README.md +54 -49
  35. package/TODO.md +13 -18
  36. package/backend-api.mjs +9 -1
  37. package/backends/llvm/scripts/run-tests.mjs +5 -0
  38. package/backends/llvm/src/kvm2llvm.mjs +2 -1
  39. package/backends/llvm/src/llvm.mjs +18 -25
  40. package/backends/llvm/tests/integration.sh +2 -2
  41. package/backends/llvm/tests/test-llvm.mjs +3 -3
  42. package/backends/llvm/tests/test-open-product-misalignment.mjs +140 -0
  43. package/backends/wasm/demo_ieee.mjs +2 -3
  44. package/backends/wasm/runtime.wat +157 -5
  45. package/backends/wasm/src/kvm2wasm.mjs +57 -34
  46. package/backends/wasm/src/wasm.mjs +38 -17
  47. package/backends/wasm/tests/perf-support.mjs +4 -4
  48. package/backends/wasm/tests/test-wasm.mjs +13 -14
  49. package/browser/vfs-data.mjs +12 -12
  50. package/codecs/BINARY_FORMAT.md +4 -4
  51. package/codecs/POLYMORPHIC_BINARY_FORMAT.md +4 -4
  52. package/codecs/README.md +6 -6
  53. package/codecs/ieee.mjs +15 -11
  54. package/codecs/int.mjs +9 -5
  55. package/codecs/string-codec.mjs +46 -47
  56. package/core.k +123 -95
  57. package/kir.mjs +213 -17
  58. package/object.mjs +0 -7
  59. package/package.json +5 -2
  60. package/parser.jison +2 -41
  61. package/parser.mjs +55 -95
  62. package/pretty.mjs +11 -4
  63. package/repl.mjs +8 -63
  64. package/scripts/build-repl-html.mjs +8 -38
  65. package/tests/code-derivation/blowup.mjs +2 -4
  66. package/tests/code-derivation/bnat.mjs +2 -3
  67. package/tests/code-derivation/dot.mjs +5 -11
  68. package/tests/code-derivation/nat.mjs +2 -2
  69. package/tests/code-derivation/not-not.mjs +6 -6
  70. package/tests/code-derivation/pattern-envelope.mjs +4 -6
  71. package/tests/code-derivation/product.mjs +3 -6
  72. package/tests/code-derivation/rlz.mjs +4 -5
  73. package/tests/test-codecs.mjs +1 -1
  74. package/tests/test-fingerprint.mjs +36 -41
  75. package/tests/test-hash-normalization.mjs +4 -6
  76. package/tests/test-ieee-arithmetic.mjs +6 -7
  77. package/tests/test-k-object.mjs +11 -15
  78. package/tests/test-kir.mjs +0 -1
  79. package/tests/test-kvm-polymorphic.mjs +1 -2
  80. package/tests/test-kvm.mjs +8 -11
  81. package/tests/test-repl-html.mjs +20 -2
  82. package/tests/test-repl.mjs +48 -53
  83. package/tests/test.mjs +2 -3
package/kir.mjs CHANGED
@@ -8,6 +8,7 @@ import { compileObject, decodeObject, hydrateObject } from "./object.mjs";
8
8
  import { exportPatternGraph } from "./codecs/runtime/codec.mjs";
9
9
  import { patternToPropertyList } from "./codecs/runtime/pattern-json.mjs";
10
10
  import { propertyListToFilter } from "./codecs/runtime/show-value.mjs";
11
+ import { TypePatternGraph } from "./TypePatternGraph.mjs";
11
12
 
12
13
  const KIR_FORMAT = "k-ir";
13
14
  const KIR_VERSION = 1;
@@ -240,6 +241,11 @@ function resolveRelation(object, relationName = null) {
240
241
  const alias = object.relAlias?.[name];
241
242
  if (alias && object.rels?.[alias]) return { name: alias, rel: object.rels[alias] };
242
243
  if (alias && object.rels?.[name]) return { name, rel: object.rels[name] };
244
+ if (alias) {
245
+ for (const [k, rel] of Object.entries(object.rels || {})) {
246
+ if (object.relAlias?.[k] === alias) return { name: k, rel };
247
+ }
248
+ }
243
249
 
244
250
  throw new Error(`Relation '${name}' not found`);
245
251
  }
@@ -264,7 +270,188 @@ function relationLibraryWithTarget(object, targetRel) {
264
270
  };
265
271
  }
266
272
 
267
- function relationPatternPropertyList(rel, index) {
273
+ function addPropertyListToPatternGraph(graph, propertyList) {
274
+ const kindToPattern = {
275
+ any: "(...)",
276
+ "open-product": "{...}",
277
+ "open-union": "<...>",
278
+ "closed-product": "{}",
279
+ "closed-union": "<>"
280
+ };
281
+ const nodes = propertyList.map(([kind]) =>
282
+ graph.addNewNode({ pattern: kindToPattern[kind] })
283
+ );
284
+
285
+ propertyList.forEach(([, edges], nodeIndex) => {
286
+ for (const [label, target] of edges) {
287
+ graph.edges[nodes[nodeIndex]][label] = [nodes[target]];
288
+ }
289
+ });
290
+
291
+ return nodes[0];
292
+ }
293
+
294
+ function cloneRelation(rel) {
295
+ const clonedGraph = new TypePatternGraph(
296
+ rel.typePatternGraph.registerCodeDef,
297
+ rel.typePatternGraph.findCode
298
+ );
299
+ clonedGraph.patterns.nodes = JSON.parse(JSON.stringify(rel.typePatternGraph.patterns.nodes));
300
+ clonedGraph.patterns.parent = [...rel.typePatternGraph.patterns.parent];
301
+ clonedGraph.edges = JSON.parse(JSON.stringify(rel.typePatternGraph.edges));
302
+ clonedGraph.codeId = { ...rel.typePatternGraph.codeId };
303
+
304
+ return {
305
+ ...rel,
306
+ def: JSON.parse(JSON.stringify(rel.def)),
307
+ typePatternGraph: clonedGraph,
308
+ typeDerivation: { ...(rel.typeDerivation || {}) }
309
+ };
310
+ }
311
+
312
+ function findCallSites(exp) {
313
+ const calls = [];
314
+ function visit(node) {
315
+ if (!node) return;
316
+ if (node.op === "ref") calls.push(node);
317
+ if (node.comp) node.comp.forEach(visit);
318
+ if (node.items) node.items.forEach(visit);
319
+ if (node.union) node.union.forEach(visit);
320
+ if (node.product) node.product.forEach((item) => visit(item.exp));
321
+ if (node.fields) node.fields.forEach((item) => visit(item.expr));
322
+ }
323
+ visit(exp);
324
+ return calls;
325
+ }
326
+
327
+ function findRelation(rels, relAlias, name) {
328
+ if (!name) return null;
329
+ if (rels[name]) return { name, rel: rels[name] };
330
+ const alias = relAlias?.[name];
331
+ if (alias && rels[alias]) return { name: alias, rel: rels[alias] };
332
+ if (alias) {
333
+ for (const [k, rel] of Object.entries(rels)) {
334
+ if (relAlias?.[k] === alias) return { name: k, rel };
335
+ }
336
+ }
337
+ return null;
338
+ }
339
+
340
+ export function specializeObjectRelations(object, entryRelationName = null, inputPattern = null) {
341
+ if (object?.format !== "k-object") {
342
+ throw new Error("specializeObjectRelations requires a k object");
343
+ }
344
+
345
+ const baseRels = Object.fromEntries(
346
+ Object.entries(object.rels || {}).map(([name, rel]) => [name, cloneRelation(rel)])
347
+ );
348
+ for (const [aliasName, hash] of Object.entries(object.relAlias || {})) {
349
+ if (!baseRels[aliasName]) {
350
+ const match = Object.entries(baseRels).find(([k]) => object.relAlias?.[k] === hash);
351
+ if (match) baseRels[aliasName] = match[1];
352
+ }
353
+ }
354
+
355
+ const entry = findRelation(
356
+ baseRels,
357
+ object.relAlias,
358
+ entryRelationName || object.main || "__main__"
359
+ );
360
+ if (!entry) return object;
361
+
362
+ const specializedRels = new Map();
363
+ const specializedCounts = new Map();
364
+ const newRels = {};
365
+
366
+ const entryRel = cloneRelation(entry.rel);
367
+ newRels[entry.name] = entryRel;
368
+
369
+ if (Array.isArray(inputPattern) && inputPattern.length > 0) {
370
+ const inRoot = addPropertyListToPatternGraph(entryRel.typePatternGraph, inputPattern);
371
+ try {
372
+ entryRel.typePatternGraph.unify("specialize", entryRel.def.patterns[0], inRoot);
373
+ } catch {
374
+ // Best-effort entry unification
375
+ }
376
+ specializedRels.set(entry.name + "::" + JSON.stringify(inputPattern), entry.name);
377
+ }
378
+
379
+ const worklist = [entry.name];
380
+ while (worklist.length > 0) {
381
+ const currentName = worklist.shift();
382
+ const currentRel = newRels[currentName];
383
+ if (!currentRel) continue;
384
+
385
+ const calls = findCallSites(currentRel.def);
386
+ for (const call of calls) {
387
+ const target = findRelation(baseRels, object.relAlias, call.ref);
388
+ if (!target) continue;
389
+
390
+ const argPatternId = call.patterns?.[0];
391
+ if (argPatternId != null) {
392
+ const argRoot = currentRel.typePatternGraph.find(argPatternId);
393
+ const argPropList = patternToPropertyList(
394
+ exportPatternGraph(currentRel.typePatternGraph, argRoot)
395
+ );
396
+ const argSig = JSON.stringify(argPropList);
397
+ const specKey = target.name + "::" + argSig;
398
+
399
+ let specName = specializedRels.get(specKey);
400
+ if (!specName) {
401
+ const count = (specializedCounts.get(target.name) || 0) + 1;
402
+ specializedCounts.set(target.name, count);
403
+ specName = count === 1 ? target.name : `${target.name}$${count}`;
404
+ specializedRels.set(specKey, specName);
405
+
406
+ const targetRel = cloneRelation(target.rel);
407
+ const tInRoot = addPropertyListToPatternGraph(targetRel.typePatternGraph, argPropList);
408
+ try {
409
+ targetRel.typePatternGraph.unify("specialize", targetRel.def.patterns[0], tInRoot);
410
+ } catch {
411
+ // Unification failure: keep target unspecialized
412
+ }
413
+ newRels[specName] = targetRel;
414
+ worklist.push(specName);
415
+ }
416
+
417
+ call.ref = specName;
418
+
419
+ const targetRel = newRels[specName];
420
+ const outPatternId = targetRel.def.patterns?.[1];
421
+ const retPatternId = call.patterns?.[1];
422
+ if (outPatternId != null && retPatternId != null) {
423
+ const outRoot = targetRel.typePatternGraph.find(outPatternId);
424
+ const retPropList = patternToPropertyList(
425
+ exportPatternGraph(targetRel.typePatternGraph, outRoot)
426
+ );
427
+ const rRetRoot = addPropertyListToPatternGraph(currentRel.typePatternGraph, retPropList);
428
+ try {
429
+ currentRel.typePatternGraph.unify("specialize", retPatternId, rRetRoot);
430
+ } catch {
431
+ // Keep existing call return pattern on unification error
432
+ }
433
+ }
434
+ }
435
+ }
436
+ }
437
+
438
+ const resultRels = {
439
+ ...baseRels,
440
+ ...newRels
441
+ };
442
+ const resultRelAlias = { ...(object.relAlias || {}) };
443
+ for (const name of Object.keys(newRels)) {
444
+ if (!resultRelAlias[name]) resultRelAlias[name] = name;
445
+ }
446
+
447
+ return {
448
+ ...object,
449
+ rels: resultRels,
450
+ relAlias: resultRelAlias
451
+ };
452
+ }
453
+
454
+ export function relationPatternPropertyList(rel, index) {
268
455
  return expPatternPropertyList(rel, rel.def, index);
269
456
  }
270
457
 
@@ -284,28 +471,35 @@ export function retypeObjectRelationForBackend(object, relationName, inputPatter
284
471
  }
285
472
 
286
473
  const target = resolveRelation(object, relationName);
287
- const source = `?${propertyListToFilter(inputPattern)} __kir_target__`;
288
- const retypedObject = hydrateObject(compileObject(source, {
289
- source: options.source || "<kir-retype>",
290
- libraries: [relationLibraryWithTarget(object, target.rel)]
291
- }));
292
- const kir = objectToKIRP(retypedObject);
293
- const entryName = retypedObject.main || "__main__";
294
- const retypedRel = retypedObject.rels[entryName];
295
- const entryInputPattern = relationPatternPropertyList(retypedRel, 0);
474
+ const specializedObject = specializeObjectRelations(object, target.name, inputPattern);
475
+ const specializedRel = resolveRelation(specializedObject, target.name);
476
+ const kir = objectToKIRP(specializedObject);
296
477
 
297
478
  return {
298
- relation: relationName || object.main,
299
- retypedObject,
479
+ relation: target.name,
480
+ retypedObject: specializedObject,
300
481
  kir,
301
- entryName,
302
- inputPattern: entryInputPattern,
303
- outputPattern: relationPatternPropertyList(retypedRel, 1)
482
+ entryName: target.name,
483
+ inputPattern: relationPatternPropertyList(specializedRel.rel, 0),
484
+ outputPattern: relationPatternPropertyList(specializedRel.rel, 1)
304
485
  };
305
486
  }
306
487
 
307
488
  export function retypeObjectRelation(object, relationName, inputPattern, options = {}) {
308
- return retypeObjectRelationForBackend(object, relationName, inputPattern, options).kir;
489
+ if (object?.format !== "k-object") {
490
+ throw new Error("KIR retyping requires a k object");
491
+ }
492
+ if (!Array.isArray(inputPattern)) {
493
+ throw new Error("KIR retyping requires an input pattern property list");
494
+ }
495
+
496
+ const target = resolveRelation(object, relationName);
497
+ const source = `?${propertyListToFilter(inputPattern)} __kir_target__`;
498
+ const retypedObject = hydrateObject(compileObject(source, {
499
+ source: options.source || "<kir-retype>",
500
+ libraries: [relationLibraryWithTarget(object, target.rel)]
501
+ }));
502
+ return objectToKIRP(retypedObject);
309
503
  }
310
504
 
311
505
  export { KIR_FORMAT, KIR_VERSION };
@@ -314,8 +508,10 @@ export default {
314
508
  KIR_FORMAT,
315
509
  KIR_VERSION,
316
510
  objectToKIRP,
511
+ relationPatternPropertyList,
317
512
  retypeObjectRelation,
318
- retypeObjectRelationForBackend
513
+ retypeObjectRelationForBackend,
514
+ specializeObjectRelations
319
515
  };
320
516
 
321
517
  function helpText() {
package/object.mjs CHANGED
@@ -737,9 +737,6 @@ function decompileObject(object) {
737
737
  ...Object.values(relAliases)
738
738
  ]);
739
739
 
740
- const codeDefs = Object.keys(hydrated.codes)
741
- .sort()
742
- .map((name) => `$ ${aliases[name]} = ${codeToSource(hydrated.codes[name], aliases)};`);
743
740
  const emittedRelAliases = new Set();
744
741
  const relDefForName = (name) => {
745
742
  const rel = rels[name];
@@ -766,8 +763,6 @@ function decompileObject(object) {
766
763
  if (hydrated.main == null) {
767
764
  // Library: no main section
768
765
  return [
769
- "----- codes -----",
770
- ...codeDefs,
771
766
  "----- rels -----",
772
767
  ...relDefs,
773
768
  ""
@@ -788,8 +783,6 @@ function decompileObject(object) {
788
783
  ].join(" ");
789
784
 
790
785
  return [
791
- "----- codes -----",
792
- ...codeDefs,
793
786
  "----- rels -----",
794
787
  ...relDefs,
795
788
  "----- main -----",
package/package.json CHANGED
@@ -1,7 +1,10 @@
1
1
  {
2
2
  "name": "@fraczak/k",
3
- "version": "6.10.6",
4
- "description": "k — a language for composing first-order partial functions over algebraic data types (products and tagged unions); includes runtime, type derivation, REPL, and codec toolchain",
3
+ "version": "7.0.1",
4
+ "description": "k — a language for content-addressable schemas and data transformations over algebraic data types (products and tagged unions); includes runtime, contract derivation, REPL, and codec toolchain",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
5
8
  "main": "index.mjs",
6
9
  "exports": {
7
10
  ".": "./index.mjs",
package/parser.jison CHANGED
@@ -60,7 +60,6 @@ function fromEscString(escString) {
60
60
  "," return 'COMMA';
61
61
  ";" return 'SC';
62
62
  ":" return 'COL';
63
- "$" return 'DOLLAR';
64
63
  "?" return 'QMARK';
65
64
  "@" return 'AT';
66
65
  \"([^"\\]|\\(.|\n))*\"|\'([^'\\]|\\(.|\n))*\' return 'STRING';
@@ -72,7 +71,7 @@ function fromEscString(escString) {
72
71
  /lex
73
72
 
74
73
  %token NAME STRING
75
- %token LA LC LP RA RP RC EQ DOT COMMA SC COL DOLLAR AT QMARK DOTS DIV VID
74
+ %token LA LC LP RA RP RC EQ DOT COMMA SC COL AT QMARK DOTS DIV VID
76
75
  %token INCREMENTAL
77
76
  %token EOF
78
77
 
@@ -97,7 +96,6 @@ vid: VID { $$ = getToken(yytext,yy,_$); };
97
96
  comma: COMMA { $$ = getToken(yytext,yy,_$); };
98
97
  sc: SC { $$ = getToken(yytext,yy,_$); };
99
98
  col: COL { $$ = getToken(yytext,yy,_$); };
100
- dollar: DOLLAR { $$ = getToken(yytext,yy,_$); };
101
99
  qmark: QMARK { $$ = getToken(yytext,yy,_$); };
102
100
  incremental: INCREMENTAL { $$ = getToken(yytext,yy,_$); };
103
101
 
@@ -115,46 +113,10 @@ initialize_symbol_table
115
113
 
116
114
  defs: { }
117
115
  | defs name eq comp sc { s.add_rel($2.value,$4); }
118
- | defs dollar name eq codeDef SC { s.add_code($3.value,$5); }
119
- ;
120
-
121
- code
122
- : name { $$ = { code: "ref", ref: $1.value, start: $1.start, end: $1.end}; }
123
- | codeDef { $$ = $1; }
124
- ;
125
-
126
- codeDef
127
- : lc labelled_codes rc { $$ = { code: "product", product: $2, start: $1.start, end: $3.end }; }
128
- | la labelled_codes ra { $$ = { code: "union", union: $2, start: $1.start, end: $3.end }; }
129
- | at name { $$ = { code: "ref", ref: "@" + $2.value, start: $1.start, end: $2.end}; }
130
- ;
131
-
132
- labelled_codes
133
- : { $$ = {}; }
134
- | non_empty_labelled_codes { $$ = $1.reduce((r, lc) => {
135
- if (r[lc.label])
136
- anError(@0,`Duplicate label '${lc.label}'.`);
137
- r[lc.label] = s.as_ref(lc.code);
138
- return r }
139
- , {}); }
140
- ;
141
-
142
- non_empty_labelled_codes
143
- : code_label { $$ = [$1]; }
144
- | non_empty_labelled_codes comma code_label
145
- { $$ = [].concat($1,$3); }
146
- ;
147
-
148
- code_label
149
- : code name { $$ = {label: $2.value, code: $1}; }
150
- | code str { $$ = {label: $2.value, code: $1}; }
151
- | name col code { $$ = {label: $1.value, code: $3}; }
152
- | str col code { $$ = {label: $1.value, code: $3}; }
153
116
  ;
154
117
 
155
118
  filter_
156
- : dollar code { $$ = { type: "code", code: s.as_ref($2), start: $1.start, end: $2.end}; }
157
- | lp dots rp { $$ = { type: null, open: true, fields: {}, start: $1.start, end: $3.end}; }
119
+ : lp dots rp { $$ = { type: null, open: true, fields: {}, start: $1.start, end: $3.end}; }
158
120
  | la labelled_filters ra { $$ = { type: "union", open: $2.open, fields: $2.fields, start: $1.start, end: $3.end}; }
159
121
  | lc labelled_filters rc { $$ = { type: "product", open: $2.open, fields: $2.fields, start: $1.start, end: $3.end}; }
160
122
  ;
@@ -210,7 +172,6 @@ exp
210
172
  | div str { $$ = {op: "div", div: $2.value, start: $1.start, end: $2.end }; }
211
173
  | vid name { $$ = {op: "vid", vid: $2.value, start: $1.start, end: $2.end }; }
212
174
  | vid str { $$ = {op: "vid", vid: $2.value, start: $1.start, end: $2.end }; }
213
- | dollar code { $$ = {op: "code", code: s.as_ref($2), start: $1.start, end: $2.end}; }
214
175
  | qmark filter { $$ = {op: "filter", filter: $2, start: $1.start, end:$2.end}; }
215
176
  ;
216
177