@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.
- package/CHANGELOG.md +66 -0
- package/DOCS/BIG_PICTURE.md +54 -7
- package/DOCS/CODECS.md +21 -11
- package/DOCS/CODEC_ARCHITECTURE.md +5 -3
- package/DOCS/DICTIONARY.md +13 -4
- package/DOCS/FILE_FORMATS.md +3 -2
- package/DOCS/JSON_APPROACHES_COMPARISON.md +306 -0
- package/DOCS/NAMED_PATTERNS.md +317 -0
- package/DOCS/PATTERNS.md +59 -11
- package/DOCS/REPL.md +13 -2
- package/DOCS/WEB_REPL_REQUIREMENTS.md +4 -2
- package/Examples/arithmetics.k +22 -17
- package/Examples/bnat.k +21 -11
- package/Examples/buda.k +37 -20
- package/Examples/byte.k +3 -1
- package/Examples/code.k +38 -39
- package/Examples/djson-btrie.k +414 -0
- package/Examples/djson-ptrie.k +663 -0
- package/Examples/djson.k +323 -0
- package/Examples/esc.k +1 -1
- package/Examples/karatsuba-mult.k +16 -13
- package/Examples/list.k +8 -4
- package/Examples/nat.k +17 -11
- package/Examples/poly.k +11 -3
- package/Examples/vhdl_to_k.k +43 -31
- package/README.md +42 -16
- package/browser/repl-browser.mjs +6 -0
- package/browser/tree-view.mjs +35 -16
- package/browser/vfs-data.mjs +15 -7
- package/codecs/README.md +2 -0
- package/codecs/djson-btrie.mjs +560 -0
- package/codecs/djson-ptrie.mjs +587 -0
- package/core.k +79 -115
- package/index.mjs +3 -0
- package/named-patterns.mjs +267 -0
- package/object.mjs +31 -3
- package/package.json +4 -1
- package/parser.jison +68 -5
- package/parser.mjs +111 -60
- package/pretty.mjs +5 -0
- package/repl.mjs +188 -22
- package/scripts/build-repl-html.mjs +68 -4
- package/scripts/perf-djson.mjs +1142 -0
- package/scripts/run-tests.mjs +5 -0
- package/symbol-table.mjs +20 -2
- package/tests/bench-djson-tries.mjs +340 -0
- package/tests/test-codecs.mjs +59 -16
- package/tests/test-named-patterns.mjs +189 -0
- package/tests/test-repl-html.mjs +51 -4
- package/tests/test-repl.mjs +190 -0
package/tests/test-repl-html.mjs
CHANGED
|
@@ -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
|
|
526
|
-
const expandBtn =
|
|
527
|
-
const collapseBtn =
|
|
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(`(() => {
|
package/tests/test-repl.mjs
CHANGED
|
@@ -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");
|