@fraczak/k 7.2.0 → 8.0.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 (50) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/DOCS/BIG_PICTURE.md +54 -7
  3. package/DOCS/CODECS.md +21 -11
  4. package/DOCS/CODEC_ARCHITECTURE.md +5 -3
  5. package/DOCS/DICTIONARY.md +13 -4
  6. package/DOCS/FILE_FORMATS.md +3 -2
  7. package/DOCS/JSON_APPROACHES_COMPARISON.md +306 -0
  8. package/DOCS/NAMED_PATTERNS.md +317 -0
  9. package/DOCS/PATTERNS.md +59 -11
  10. package/DOCS/REPL.md +13 -2
  11. package/DOCS/WEB_REPL_REQUIREMENTS.md +4 -2
  12. package/Examples/arithmetics.k +22 -17
  13. package/Examples/bnat.k +21 -11
  14. package/Examples/buda.k +37 -20
  15. package/Examples/byte.k +3 -1
  16. package/Examples/code.k +38 -39
  17. package/Examples/djson-btrie.k +414 -0
  18. package/Examples/djson-ptrie.k +663 -0
  19. package/Examples/djson.k +323 -0
  20. package/Examples/esc.k +1 -1
  21. package/Examples/karatsuba-mult.k +16 -13
  22. package/Examples/list.k +8 -4
  23. package/Examples/nat.k +17 -11
  24. package/Examples/poly.k +11 -3
  25. package/Examples/vhdl_to_k.k +43 -31
  26. package/README.md +42 -16
  27. package/browser/repl-browser.mjs +6 -0
  28. package/browser/tree-view.mjs +35 -16
  29. package/browser/vfs-data.mjs +15 -7
  30. package/codecs/README.md +2 -0
  31. package/codecs/djson-btrie.mjs +560 -0
  32. package/codecs/djson-ptrie.mjs +587 -0
  33. package/core.k +79 -115
  34. package/index.mjs +3 -0
  35. package/named-patterns.mjs +267 -0
  36. package/object.mjs +31 -3
  37. package/package.json +4 -1
  38. package/parser.jison +68 -5
  39. package/parser.mjs +111 -60
  40. package/pretty.mjs +5 -0
  41. package/repl.mjs +188 -22
  42. package/scripts/build-repl-html.mjs +68 -4
  43. package/scripts/perf-djson.mjs +1142 -0
  44. package/scripts/run-tests.mjs +5 -0
  45. package/symbol-table.mjs +20 -2
  46. package/tests/bench-djson-tries.mjs +340 -0
  47. package/tests/test-codecs.mjs +59 -16
  48. package/tests/test-named-patterns.mjs +189 -0
  49. package/tests/test-repl-html.mjs +51 -4
  50. package/tests/test-repl.mjs +190 -0
@@ -140,6 +140,11 @@ if (chromiumBin) {
140
140
  const expectedFiles = [
141
141
  "arithmetics.k",
142
142
  "core.k",
143
+ "djson-btrie.k",
144
+ "djson-btrie.mjs",
145
+ "djson-ptrie.k",
146
+ "djson-ptrie.mjs",
147
+ "djson.k",
143
148
  "djson.mjs",
144
149
  "ieee.k",
145
150
  "ieee.mjs",
@@ -522,9 +527,9 @@ if (chromiumBin) {
522
527
  const v4Collapsed = v4 ? !v4.open : false;
523
528
 
524
529
  // Click Expand All
525
- const actionBtns = lastEntry.querySelectorAll(".k-btn-action");
526
- const expandBtn = actionBtns[0];
527
- const collapseBtn = actionBtns[1];
530
+ const toolbarActionBtns = lastEntry.querySelectorAll(".k-output-toolbar .k-btn-action");
531
+ const expandBtn = toolbarActionBtns[0];
532
+ const collapseBtn = toolbarActionBtns[1];
528
533
 
529
534
  expandBtn.click();
530
535
  const allOpenAfterExpand = v4.open;
@@ -536,13 +541,48 @@ if (chromiumBin) {
536
541
  );
537
542
  const v4CollapsedAgain = newV4 ? !newV4.open : false;
538
543
 
544
+ // Verify Copy buttons on Value and Pattern section headers
545
+ const patSection = lastEntry.querySelector(".k-tree-pat-section");
546
+ const copyValBtn = valSection?.querySelector("summary.k-tree-section-summary > .k-btn-copy-value");
547
+ const copyPatBtn = patSection?.querySelector("summary.k-tree-section-summary > .k-btn-copy-pattern");
548
+
549
+ const copiedTexts = [];
550
+ const origWriteText = navigator.clipboard?.writeText;
551
+ if (navigator.clipboard) {
552
+ navigator.clipboard.writeText = async (txt) => { copiedTexts.push(txt); };
553
+ } else {
554
+ Object.defineProperty(navigator, "clipboard", {
555
+ value: { writeText: async (txt) => { copiedTexts.push(txt); } },
556
+ configurable: true
557
+ });
558
+ }
559
+
560
+ const valOpenBefore = valSection?.open;
561
+ copyValBtn?.click();
562
+ const valOpenAfter = valSection?.open;
563
+
564
+ const patOpenBefore = patSection?.open;
565
+ copyPatBtn?.click();
566
+ const patOpenAfter = patSection?.open;
567
+
568
+ if (origWriteText && navigator.clipboard) {
569
+ navigator.clipboard.writeText = origWriteText;
570
+ }
571
+
539
572
  return {
540
573
  v1Open,
541
574
  v2Open,
542
575
  v3Open,
543
576
  v4Collapsed,
544
577
  allOpenAfterExpand,
545
- v4CollapsedAgain
578
+ v4CollapsedAgain,
579
+ toolbarBtnCount: toolbarActionBtns.length,
580
+ hasCopyValBtn: !!copyValBtn,
581
+ hasCopyPatBtn: !!copyPatBtn,
582
+ valDidNotToggle: valOpenBefore === valOpenAfter,
583
+ patDidNotToggle: patOpenBefore === patOpenAfter,
584
+ copiedValText: copiedTexts[0] || "",
585
+ copiedPatText: copiedTexts[1] || ""
546
586
  };
547
587
  })()`);
548
588
 
@@ -552,6 +592,13 @@ if (chromiumBin) {
552
592
  assert.strictEqual(variantDepthAndActionsCheck.v4Collapsed, true, "Variant level 4 must be collapsed by default");
553
593
  assert.strictEqual(variantDepthAndActionsCheck.allOpenAfterExpand, true, "Expand button should expand collapsed nodes");
554
594
  assert.strictEqual(variantDepthAndActionsCheck.v4CollapsedAgain, true, "Collapse button should reset deeper nodes to collapsed");
595
+ assert.strictEqual(variantDepthAndActionsCheck.toolbarBtnCount, 2, "Toolbar action group should only have Expand and Collapse buttons");
596
+ assert.strictEqual(variantDepthAndActionsCheck.hasCopyValBtn, true, "Value section summary should have Copy button");
597
+ assert.strictEqual(variantDepthAndActionsCheck.hasCopyPatBtn, true, "Pattern section summary should have Copy button");
598
+ assert.strictEqual(variantDepthAndActionsCheck.valDidNotToggle, true, "Clicking Value Copy button should not toggle Value details");
599
+ assert.strictEqual(variantDepthAndActionsCheck.patDidNotToggle, true, "Clicking Pattern Copy button should not toggle Pattern details");
600
+ assert.strictEqual(variantDepthAndActionsCheck.copiedValText, "{}|_|0|1|0|1", "Value Copy button should copy K value definition");
601
+ assert(variantDepthAndActionsCheck.copiedPatText.startsWith("?<"), `Pattern Copy button should copy K pattern filter definition, got: ${variantDepthAndActionsCheck.copiedPatText}`);
555
602
 
556
603
  // 4. Verify shortened welcome banner
557
604
  const bannerCheck = await evaluateAsync(`(() => {
@@ -581,6 +581,196 @@ assert.equal(output[0], "output limit disabled");
581
581
  output = await evaluateInput(":limit", limitState);
582
582
  assert.equal(output[0], "Current output limit: none");
583
583
 
584
+ // Verify Examples/djson.k (json?, dot, has_key?, keys, values, remove, put)
585
+ const jsonKState = createState({ engine: "wasm" });
586
+ output = await evaluateInput(":load Examples/djson.k", jsonKState);
587
+ assert.equal(output[0], "loaded Examples/djson.k");
588
+ await evaluateInput(":codec load codecs/djson.mjs", jsonKState);
589
+ await evaluateInput(
590
+ ':input djson.mjs {"name":"Alice","age":30,"active":true}',
591
+ jsonKState
592
+ );
593
+ output = await evaluateInput(
594
+ "{/object cdr car .key key, /object object} dot",
595
+ jsonKState
596
+ );
597
+ assert.match(output[0], /djson\.mjs: 30/);
598
+
599
+ await evaluateInput(
600
+ ':input djson.mjs {"name":"Alice","age":30}',
601
+ jsonKState
602
+ );
603
+ output = await evaluateInput(
604
+ "{/object car .key key, /object object} remove object",
605
+ jsonKState
606
+ );
607
+ assert.match(output[0], /djson\.mjs: \{"name":"Alice"\}/);
608
+
609
+ // Verify Examples/djson-btrie.k (json?, dot, nth, put, remove)
610
+ const btrieState = createState({ engine: "wasm" });
611
+ output = await evaluateInput(":load Examples/djson-btrie.k", btrieState);
612
+ assert.equal(output[0], "loaded Examples/djson-btrie.k");
613
+ await evaluateInput(":codec load codecs/djson-btrie.mjs", btrieState);
614
+ await evaluateInput(
615
+ ':input djson-btrie.mjs ["age", {"name":"Alice","age":30}]',
616
+ btrieState
617
+ );
618
+ output = await evaluateInput(
619
+ "{{0 index, /list list} nth /string key, " +
620
+ "{1 index, /list list} nth /object object} dot",
621
+ btrieState
622
+ );
623
+ assert.match(output[0], /djson-btrie\.mjs: 30/);
624
+
625
+ await evaluateInput(
626
+ ':input djson-btrie.mjs ["age", {"name":"Alice","age":30}]',
627
+ btrieState
628
+ );
629
+ output = await evaluateInput(
630
+ "{{0 index, /list list} nth /string key, " +
631
+ "{1 index, /list list} nth /object object} remove object",
632
+ btrieState
633
+ );
634
+ assert.match(output[0], /djson-btrie\.mjs: \{"name":"Alice"\}/);
635
+
636
+ // Verify Examples/djson-ptrie.k (json?, dot, nth, list_size, obj_size)
637
+ const ptrieState = createState({ engine: "wasm" });
638
+ output = await evaluateInput(":load Examples/djson-ptrie.k", ptrieState);
639
+ assert.equal(output[0], "loaded Examples/djson-ptrie.k");
640
+ await evaluateInput(":codec load codecs/djson-ptrie.mjs", ptrieState);
641
+ await evaluateInput(
642
+ ':input djson-ptrie.mjs ["age", {"name":"Alice","age":30}]',
643
+ ptrieState
644
+ );
645
+ output = await evaluateInput(
646
+ "{{0 index, /list list} nth /string key, " +
647
+ "{1 index, /list list} nth /object object} dot",
648
+ ptrieState
649
+ );
650
+ assert.match(output[0], /djson-ptrie\.mjs: 30/);
651
+
652
+ // Verify named pattern persistence, listing, display, and tab completion
653
+ const patState = createState({ engine: "wasm" });
654
+
655
+ output = await evaluateInput(":patterns", patState);
656
+ assert.equal(output[0], "(none)");
657
+ output = await evaluateInput(":pats", patState);
658
+ assert.equal(output[0], "(none)");
659
+
660
+ output = await evaluateInput("bit[] = < {} 0, {} 1 >;", patState);
661
+ assert.match(output[0], /\/\* comp:/);
662
+
663
+ output = await evaluateInput(":patterns", patState);
664
+ assert.equal(output[0], "bit[]");
665
+ output = await evaluateInput(":pats", patState);
666
+ assert.equal(output[0], "bit[]");
667
+
668
+ output = await evaluateInput(":pattern bit", patState);
669
+ assert.equal(
670
+ output[0],
671
+ "bit[] = <{} 0, {} 1>; -- " +
672
+ "@MjusyRWZnVj8TR4BhFCCcMRrQbdKp9gPtC29LT9nZvjg"
673
+ );
674
+ output = await evaluateInput(":pattern bit[]", patState);
675
+ assert.equal(
676
+ output[0],
677
+ "bit[] = <{} 0, {} 1>; -- " +
678
+ "@MjusyRWZnVj8TR4BhFCCcMRrQbdKp9gPtC29LT9nZvjg"
679
+ );
680
+ output = await evaluateInput(":pat bit", patState);
681
+ assert.equal(
682
+ output[0],
683
+ "bit[] = <{} 0, {} 1>; -- " +
684
+ "@MjusyRWZnVj8TR4BhFCCcMRrQbdKp9gPtC29LT9nZvjg"
685
+ );
686
+ output = await evaluateInput(
687
+ ":pattern @MjusyRWZnVj8TR4BhFCCcMRrQbdKp9gPtC29LT9nZvjg",
688
+ patState
689
+ );
690
+ assert.equal(
691
+ output[0],
692
+ "bit[] = <{} 0, {} 1>; -- " +
693
+ "@MjusyRWZnVj8TR4BhFCCcMRrQbdKp9gPtC29LT9nZvjg"
694
+ );
695
+
696
+ await assert.rejects(
697
+ () => evaluateInput(":pattern unknown", patState),
698
+ /Unknown pattern 'unknown'/
699
+ );
700
+ await assert.rejects(
701
+ () => evaluateInput(":pattern", patState),
702
+ /:pattern requires a pattern name/
703
+ );
704
+
705
+ completions = completeInput(":pat", patState)[0];
706
+ assert(completions.includes(":pattern"));
707
+ assert(completions.includes(":patterns"));
708
+ assert(completions.includes(":pat"));
709
+ assert(completions.includes(":pats"));
710
+
711
+ completions = completeInput(":pattern b", patState)[0];
712
+ assert(completions.includes(":pattern bit"));
713
+ completions = completeInput(":pat b", patState)[0];
714
+ assert(completions.includes(":pat bit"));
715
+ completions = completeInput(":pattern @Mjusy", patState)[0];
716
+ assert(
717
+ completions.includes(
718
+ ":pattern @MjusyRWZnVj8TR4BhFCCcMRrQbdKp9gPtC29LT9nZvjg"
719
+ )
720
+ );
721
+
722
+ output = await evaluateInput("{} | 0 ? bit[]", patState);
723
+ assert.match(output[0], /\{\}\|0/);
724
+
725
+ output = await evaluateInput(
726
+ "pair[A, B] = { A first, B second };",
727
+ patState
728
+ );
729
+ output = await evaluateInput(":pattern pair", patState);
730
+ assert.equal(
731
+ output[0],
732
+ "pair[A, B] = {A first, B second}; -- " +
733
+ "@KqTBNkznjWWwync2Mm94XjCez63neJBJtgYcJJEE5kEm"
734
+ );
735
+
736
+ output = await evaluateInput(":load Examples/list.k", patState);
737
+ assert.equal(output[0], "loaded Examples/list.k");
738
+ output = await evaluateInput(":patterns", patState);
739
+ assert(output[0].includes("bit[]"));
740
+ assert(output[0].includes("list[X]"));
741
+ assert(output[0].includes("pair[A, B]"));
742
+
743
+ output = await evaluateInput(":pattern list", patState);
744
+ assert.equal(
745
+ output[0],
746
+ "list[X] = <{} nil, {X car, Y cdr} cons>=Y; -- " +
747
+ "@JagVexzgseRr4rVZRmVpsMbr2AKXDjzZnz6Qnf8a9SNe"
748
+ );
749
+
750
+ output = await evaluateInput("{} | nil ? list[bit[]]", patState);
751
+ assert.match(output[0], /\{\}\|nil/);
752
+
753
+ const testKlibPath = path.join(tmpDir, "patterns-test.klib");
754
+ output = await evaluateInput(`:klib ${testKlibPath}`, patState);
755
+ assert.equal(output[0], `saved ${testKlibPath}`);
756
+
757
+ const freshState = createState({ engine: "wasm" });
758
+ output = await evaluateInput(`:load ${testKlibPath}`, freshState);
759
+ assert.equal(output[0], `loaded ${testKlibPath}`);
760
+ output = await evaluateInput(":patterns", freshState);
761
+ assert(output[0].includes("bit[]"));
762
+ assert(output[0].includes("pair[A, B]"));
763
+ assert(output[0].includes("list[X]"));
764
+
765
+ output = await evaluateInput(":pattern bit", freshState);
766
+ assert.equal(
767
+ output[0],
768
+ "bit[] = <{} 0, {} 1>; -- " +
769
+ "@MjusyRWZnVj8TR4BhFCCcMRrQbdKp9gPtC29LT9nZvjg"
770
+ );
771
+ output = await evaluateInput("{} | nil ? list[bit[]]", freshState);
772
+ assert.match(output[0], /\{\}\|nil/);
773
+
584
774
  fs.rmSync(tmpDir, { recursive: true, force: true });
585
775
  fs.rmSync(symlinkPath, { force: true });
586
776
  console.log("OK");