@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
@@ -14,7 +14,9 @@ import {
14
14
  propertyListToPattern,
15
15
  Value,
16
16
  specializeKVM,
17
- specializeObjectRelations
17
+ specializeObjectRelations,
18
+ compileRelationToPKIR,
19
+ lowerPKIRToKVM
18
20
  } from "@fraczak/k/backend-api.mjs";
19
21
  import { intersectPropertyListPatterns } from "@fraczak/k/codecs/runtime/codec.mjs";
20
22
  import { propertyListToFilter } from "@fraczak/k/codecs/runtime/show-value.mjs";
@@ -283,7 +285,7 @@ function cloneKVMFunction(kvmFunc) {
283
285
  };
284
286
  }
285
287
 
286
- function compileModule(mainRelName, defs) {
288
+ function compileModule(mainRelName, defs, { usePKIR = false } = {}) {
287
289
  const compiled = new Set();
288
290
  const queue = [mainRelName];
289
291
  const wats = [];
@@ -299,8 +301,14 @@ function compileModule(mainRelName, defs) {
299
301
  throw new Error(`Relation ${name} not found`);
300
302
  }
301
303
 
302
- const kvmFunc = lowerToKVM(relDef, name);
303
- kvmFunc.typePatternGraph = relDef.typePatternGraph;
304
+ let kvmFunc;
305
+ if (usePKIR) {
306
+ const pkir = compileRelationToPKIR(relDef, name);
307
+ kvmFunc = lowerPKIRToKVM(pkir);
308
+ } else {
309
+ kvmFunc = lowerToKVM(relDef, name);
310
+ kvmFunc.typePatternGraph = relDef.typePatternGraph;
311
+ }
304
312
  scanCalls(kvmFunc.body, compiled, queue);
305
313
 
306
314
  kvmFunc.name = nameMap.get(name) || cleanName(name);
@@ -502,7 +510,7 @@ function buildExportPreamble(exportSpecs, libraries) {
502
510
  async function compileWasmArtifactFromDefs(
503
511
  defs,
504
512
  mainRelName = "__main__",
505
- { typingMode = "generic" } = {}
513
+ { typingMode = "generic", usePKIR = false } = {}
506
514
  ) {
507
515
  resetTagIds();
508
516
  const mainRel = defs.rels[mainRelName];
@@ -510,7 +518,11 @@ async function compileWasmArtifactFromDefs(
510
518
  throw new Error(`No main relation (${mainRelName}) defined in script`);
511
519
  }
512
520
 
513
- const { wat: moduleWatBody, relationNames, entryName } = compileModule(mainRelName, defs);
521
+ const { wat: moduleWatBody, relationNames, entryName } = compileModule(
522
+ mainRelName,
523
+ defs,
524
+ { usePKIR }
525
+ );
514
526
  assertRelationsConverged(defs, relationNames);
515
527
  const fullWat = runtimeWat.trim().slice(0, -1) + "\n" + moduleWatBody + "\n)";
516
528
  const graph = mainRel.typePatternGraph;
@@ -523,7 +535,12 @@ async function compileWasmArtifactFromDefs(
523
535
  entry: entryName || cleanName(mainRelName),
524
536
  inputPattern,
525
537
  outputPattern,
526
- typing: classifyTyping(inputPattern, outputPattern, relTypeStatus(defs, mainRelName), typingMode),
538
+ typing: classifyTyping(
539
+ inputPattern,
540
+ outputPattern,
541
+ relTypeStatus(defs, mainRelName),
542
+ typingMode
543
+ ),
527
544
  tags: getTagEntries()
528
545
  };
529
546
  return appendCustomSection(await compileWat(fullWat), METADATA_SECTION, JSON.stringify(metadata));
@@ -598,12 +615,17 @@ async function compileWasmArtifactFromKVM(kvmInput, options = {}) {
598
615
  return appendCustomSection(await compileWat(fullWat), METADATA_SECTION, JSON.stringify(metadata));
599
616
  }
600
617
 
601
- async function compileWasmArtifactFromObject(object, { entry = null, inputEnvelopePattern = null } = {}) {
618
+ async function compileWasmArtifactFromObject(
619
+ object,
620
+ { entry = null, inputEnvelopePattern = null, usePKIR = false } = {}
621
+ ) {
602
622
  if (!object?.rels) {
603
623
  throw new Error("Expected .ko input to contain k object relations");
604
624
  }
605
625
  if (object.main == null) {
606
- throw new Error("Cannot compile a .klib library to WebAssembly without a main relation");
626
+ throw new Error(
627
+ "Cannot compile a .klib library to WebAssembly without a main relation"
628
+ );
607
629
  }
608
630
  const mainRelName = entry || object.main || "__main__";
609
631
  let defs = { rels: object.rels, compileStats: object.compileStats };
@@ -611,43 +633,126 @@ async function compileWasmArtifactFromObject(object, { entry = null, inputEnvelo
611
633
 
612
634
  if (inputEnvelopePattern) {
613
635
  const mainRel = object.rels[mainRelName];
614
- if (!mainRel) throw new Error(`No main relation (${mainRelName}) defined in object`);
615
- const inputPattern = getPatternPropertyList(mainRel.typePatternGraph, mainRel.def.patterns[0]);
616
- const outputPattern = getPatternPropertyList(mainRel.typePatternGraph, mainRel.def.patterns[1]);
636
+ if (!mainRel) {
637
+ throw new Error(`No main relation (${mainRelName}) defined in object`);
638
+ }
639
+ const inputPattern = getPatternPropertyList(
640
+ mainRel.typePatternGraph,
641
+ mainRel.def.patterns[0]
642
+ );
643
+ const outputPattern = getPatternPropertyList(
644
+ mainRel.typePatternGraph,
645
+ mainRel.def.patterns[1]
646
+ );
617
647
  const typing = classifyTyping(inputPattern, outputPattern);
618
648
  if (typing.program === "polymorphic") {
619
- const specialized = specializeObjectRelations(object, mainRelName, inputEnvelopePattern);
620
- defs = { rels: specialized.rels, relAlias: specialized.relAlias, compileStats: specialized.compileStats };
649
+ const specialized = specializeObjectRelations(
650
+ object,
651
+ mainRelName,
652
+ inputEnvelopePattern
653
+ );
654
+ defs = {
655
+ rels: specialized.rels,
656
+ relAlias: specialized.relAlias,
657
+ compileStats: specialized.compileStats
658
+ };
621
659
  typingMode = "specialized";
622
660
  }
623
661
  }
624
662
 
625
- return compileWasmArtifactFromDefs(defs, mainRelName, { typingMode });
663
+ return compileWasmArtifactFromDefs(defs, mainRelName, {
664
+ typingMode,
665
+ usePKIR
666
+ });
626
667
  }
627
668
 
628
669
  async function compileWasmArtifact(
629
670
  source,
630
- { libraries = [], exports = [], source: sourceName = null, inputEnvelopePattern = null } = {}
671
+ {
672
+ libraries = [],
673
+ exports = [],
674
+ source: sourceName = null,
675
+ inputEnvelopePattern = null,
676
+ usePKIR = false
677
+ } = {}
631
678
  ) {
632
679
  const preamble = buildExportPreamble(exports, libraries);
633
680
  const fullSource = preamble + source;
634
- const options = { libraries, ...(sourceName ? { source: sourceName } : {}) };
681
+ const options = {
682
+ libraries,
683
+ ...(sourceName ? { source: sourceName } : {})
684
+ };
635
685
  let defs = annotate(fullSource, options);
636
686
 
637
687
  let typingMode = "generic";
638
688
  if (inputEnvelopePattern) {
639
689
  const mainRel = defs.rels.__main__;
640
- const inputPattern = getPatternPropertyList(mainRel.typePatternGraph, mainRel.def.patterns[0]);
641
- const outputPattern = getPatternPropertyList(mainRel.typePatternGraph, mainRel.def.patterns[1]);
690
+ const inputPattern = getPatternPropertyList(
691
+ mainRel.typePatternGraph,
692
+ mainRel.def.patterns[0]
693
+ );
694
+ const outputPattern = getPatternPropertyList(
695
+ mainRel.typePatternGraph,
696
+ mainRel.def.patterns[1]
697
+ );
642
698
  if (classifyTyping(inputPattern, outputPattern).program === "polymorphic") {
643
- const obj = { format: "k-object", rels: defs.rels, relAlias: defs.relAlias, compileStats: defs.compileStats, main: "__main__" };
644
- const specialized = specializeObjectRelations(obj, "__main__", inputEnvelopePattern);
645
- defs = { rels: specialized.rels, relAlias: specialized.relAlias, compileStats: specialized.compileStats };
699
+ const obj = {
700
+ format: "k-object",
701
+ rels: defs.rels,
702
+ relAlias: defs.relAlias,
703
+ compileStats: defs.compileStats,
704
+ main: "__main__"
705
+ };
706
+ const specialized = specializeObjectRelations(
707
+ obj,
708
+ "__main__",
709
+ inputEnvelopePattern
710
+ );
711
+ defs = {
712
+ rels: specialized.rels,
713
+ relAlias: specialized.relAlias,
714
+ compileStats: specialized.compileStats
715
+ };
646
716
  typingMode = "specialized";
647
717
  }
648
718
  }
649
719
 
650
- return compileWasmArtifactFromDefs(defs, "__main__", { typingMode });
720
+ return compileWasmArtifactFromDefs(defs, "__main__", {
721
+ typingMode,
722
+ usePKIR
723
+ });
724
+ }
725
+
726
+ async function compileWasmArtifactFromPKIR(pkirInput, options = {}) {
727
+ let kvmProgram;
728
+ let entry = options.entry || "__main__";
729
+ if (pkirInput && typeof pkirInput === "object") {
730
+ if (pkirInput.format === "pk-ir" && pkirInput.functions) {
731
+ entry = pkirInput.entry || entry;
732
+ kvmProgram = {};
733
+ for (const [name, f] of Object.entries(pkirInput.functions)) {
734
+ kvmProgram[name] = lowerPKIRToKVM(f);
735
+ }
736
+ } else if (pkirInput.instructions) {
737
+ const kvm = lowerPKIRToKVM(pkirInput);
738
+ entry = kvm.name || entry;
739
+ kvmProgram = { [entry]: kvm };
740
+ } else if (pkirInput.functions) {
741
+ entry = pkirInput.entry || entry;
742
+ kvmProgram = {};
743
+ for (const [name, f] of Object.entries(pkirInput.functions)) {
744
+ kvmProgram[name] = lowerPKIRToKVM(f);
745
+ }
746
+ } else {
747
+ throw new Error("Invalid PKIR input for WebAssembly compilation");
748
+ }
749
+ } else {
750
+ throw new Error("Expected PKIR input object");
751
+ }
752
+ return compileWasmArtifactFromKVM(
753
+ { format: "k-vm", layer: "KVM", entry, functions: kvmProgram },
754
+ options
755
+ );
651
756
  }
652
757
 
653
758
  function createTagRegistry(entries) {
@@ -1050,6 +1155,7 @@ export {
1050
1155
  appendCustomSection,
1051
1156
  compileWasmArtifactFromKVM,
1052
1157
  compileWasmArtifactFromObject,
1158
+ compileWasmArtifactFromPKIR,
1053
1159
  compileWasmArtifact,
1054
1160
  instantiateWasmArtifact,
1055
1161
  metadataFromModule,
@@ -1067,6 +1173,7 @@ export default {
1067
1173
  appendCustomSection,
1068
1174
  compileWasmArtifactFromKVM,
1069
1175
  compileWasmArtifactFromObject,
1176
+ compileWasmArtifactFromPKIR,
1070
1177
  compileWasmArtifact,
1071
1178
  metadataFromModule,
1072
1179
  readArenaValue,
@@ -311,13 +311,17 @@ export async function executeCommand(input) {
311
311
  function updateEngineUI() {
312
312
  const badge = document.getElementById("engine-badge");
313
313
  if (!badge) return;
314
- badge.textContent = state.engine === "js" ? "js-in-process" : "wasm-in-process";
315
- badge.title = `Engine: ${state.engine} (click to toggle wasm/js)`;
314
+ badge.classList.remove("js-mode", "pkir-mode");
316
315
  if (state.engine === "js") {
316
+ badge.textContent = "js-in-process";
317
317
  badge.classList.add("js-mode");
318
+ } else if (state.engine === "pkir") {
319
+ badge.textContent = "pkir-wasm";
320
+ badge.classList.add("pkir-mode");
318
321
  } else {
319
- badge.classList.remove("js-mode");
322
+ badge.textContent = "wasm-in-process";
320
323
  }
324
+ badge.title = `Engine: ${state.engine} (click to toggle wasm/pkir/js)`;
321
325
  }
322
326
 
323
327
  function adjustInputHeight() {
@@ -979,11 +983,18 @@ export function initRepl() {
979
983
  }
980
984
  });
981
985
 
982
- // Engine Badge toggle (wasm <-> js)
986
+ // Engine Badge toggle (wasm -> pkir -> js -> wasm)
983
987
  const engineBadge = document.getElementById("engine-badge");
984
988
  if (engineBadge) {
985
989
  engineBadge.onclick = () => {
986
- const nextEngine = state.engine === "wasm" ? "js" : "wasm";
990
+ let nextEngine;
991
+ if (state.engine === "wasm") {
992
+ nextEngine = "pkir";
993
+ } else if (state.engine === "pkir") {
994
+ nextEngine = "js";
995
+ } else {
996
+ nextEngine = "wasm";
997
+ }
987
998
  executeCommand(`:engine ${nextEngine}`);
988
999
  };
989
1000
  }
@@ -101,7 +101,9 @@ export function renderValueNode(val, currentDepth = 1, depthLimit = 3) {
101
101
  const populateChildren = () => {
102
102
  if (populated) return;
103
103
  populated = true;
104
- for (const k of keys) {
104
+ const maxFields = 100;
105
+ const displayKeys = keys.slice(0, maxFields);
106
+ for (const k of displayKeys) {
105
107
  const row = document.createElement("div");
106
108
  row.className = "k-tree-row";
107
109
 
@@ -113,12 +115,25 @@ export function renderValueNode(val, currentDepth = 1, depthLimit = 3) {
113
115
  const childContainer = document.createElement("div");
114
116
  childContainer.className = "k-tree-child-val";
115
117
  childContainer.appendChild(
116
- renderValueNode(val.product[k], isOpen ? currentDepth + 1 : 1, depthLimit)
118
+ renderValueNode(
119
+ val.product[k],
120
+ isOpen ? currentDepth + 1 : 1,
121
+ depthLimit
122
+ )
117
123
  );
118
124
  row.appendChild(childContainer);
119
125
 
120
126
  childrenContainer.appendChild(row);
121
127
  }
128
+ if (keys.length > maxFields) {
129
+ const moreRow = document.createElement("div");
130
+ moreRow.className = "k-tree-row k-tree-more";
131
+ moreRow.style.color = "var(--color-muted)";
132
+ moreRow.style.paddingLeft = "1.2rem";
133
+ moreRow.textContent =
134
+ `... and ${keys.length - maxFields} more fields`;
135
+ childrenContainer.appendChild(moreRow);
136
+ }
122
137
  };
123
138
 
124
139
  if (isOpen) {
@@ -169,13 +184,15 @@ export function renderValueNode(val, currentDepth = 1, depthLimit = 3) {
169
184
  if (isVariant(val.value)) {
170
185
  let chainCount = 0;
171
186
  let curr = val.value;
172
- while (isVariant(curr)) {
187
+ while (isVariant(curr) && chainCount < 20) {
173
188
  chainCount++;
174
189
  curr = curr.value;
175
190
  }
176
191
  const badge = document.createElement("span");
177
192
  badge.className = "k-tree-badge";
178
- badge.textContent = `+${chainCount} tags`;
193
+ badge.textContent = isVariant(curr)
194
+ ? "+20+ tags"
195
+ : `+${chainCount} tags`;
179
196
  summary.appendChild(badge);
180
197
  }
181
198
 
@@ -486,7 +503,15 @@ export function createOutputEntryElement({
486
503
 
487
504
  const valSummary = document.createElement("summary");
488
505
  valSummary.className = "k-tree-section-summary";
489
- valSummary.innerHTML = `<span class="k-tree-arrow">▶</span> <span class="k-section-title">🌲 Value</span> <span class="k-section-preview">${escapeHtml(valueToK(value))}</span>`;
506
+ const rawValPreview = valueToK(value, 120);
507
+ const valPreview = rawValPreview.length > 120
508
+ ? `${rawValPreview.slice(0, 120)}...`
509
+ : rawValPreview;
510
+ const valEscaped = escapeHtml(valPreview);
511
+ valSummary.innerHTML =
512
+ `<span class="k-tree-arrow">▶</span> ` +
513
+ `<span class="k-section-title">🌲 Value</span> ` +
514
+ `<span class="k-section-preview">${valEscaped}</span>`;
490
515
  valSection.appendChild(valSummary);
491
516
 
492
517
  const valBody = document.createElement("div");
@@ -504,8 +529,15 @@ export function createOutputEntryElement({
504
529
 
505
530
  const patSummary = document.createElement("summary");
506
531
  patSummary.className = "k-tree-section-summary";
507
- const patPreview = propertyListToFilter(value.pattern);
508
- patSummary.innerHTML = `<span class="k-tree-arrow">▶</span> <span class="k-section-title">🌿 Pattern</span> <span class="k-section-preview">?${escapeHtml(patPreview)}</span>`;
532
+ const rawPatPreview = propertyListToFilter(value.pattern, "X", 120);
533
+ const patPreview = rawPatPreview.length > 120
534
+ ? `${rawPatPreview.slice(0, 120)}...`
535
+ : rawPatPreview;
536
+ const patEscaped = escapeHtml(patPreview);
537
+ patSummary.innerHTML =
538
+ `<span class="k-tree-arrow">▶</span> ` +
539
+ `<span class="k-section-title">🌿 Pattern</span> ` +
540
+ `<span class="k-section-preview">?${patEscaped}</span>`;
509
541
  patSection.appendChild(patSummary);
510
542
 
511
543
  patBody = document.createElement("div");
@@ -545,7 +577,14 @@ export function createOutputEntryElement({
545
577
 
546
578
  const rawPre = document.createElement("pre");
547
579
  rawPre.className = "k-raw-pre";
548
- rawPre.textContent = rawText;
580
+ const MAX_RAW_PRE_CHARS = 100000;
581
+ if (rawText.length > MAX_RAW_PRE_CHARS) {
582
+ rawPre.textContent =
583
+ rawText.slice(0, MAX_RAW_PRE_CHARS) +
584
+ `\n\n... [truncated, ${rawText.length - MAX_RAW_PRE_CHARS} more characters]`;
585
+ } else {
586
+ rawPre.textContent = rawText;
587
+ }
549
588
  rawWrapper.appendChild(rawPre);
550
589
 
551
590
  container.appendChild(rawWrapper);