@fraczak/k 7.1.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 +99 -0
- package/DOCS/BIG_PICTURE.md +54 -7
- package/DOCS/CODECS.md +47 -2
- package/DOCS/CODEC_ARCHITECTURE.md +14 -5
- 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 +24 -5
- 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 -15
- package/Value.mjs +28 -3
- package/backend-api.mjs +5 -1
- package/browser/repl-browser.mjs +6 -0
- package/browser/tree-view.mjs +82 -24
- package/browser/vfs-data.mjs +21 -12
- package/codecs/README.md +3 -0
- package/codecs/djson-btrie.mjs +560 -0
- package/codecs/djson-ptrie.mjs +587 -0
- package/codecs/djson.mjs +270 -0
- package/codecs/int.mjs +2 -1
- package/codecs/json.mjs +7 -2
- package/codecs/k-parse.mjs +7 -1
- package/codecs/k-print.mjs +4 -0
- package/codecs/runtime/codec-sdk.mjs +16 -2
- package/codecs/runtime/pattern-json.mjs +80 -1
- package/codecs/runtime/prefix-codec.mjs +629 -67
- package/codecs/runtime/show-value.mjs +93 -51
- package/codecs/runtime/unicode-string.mjs +29 -6
- package/codecs/show.mjs +4 -0
- package/codecs/utf16.mjs +2 -1
- package/core.k +79 -115
- package/index.mjs +3 -0
- package/named-patterns.mjs +267 -0
- package/object.mjs +31 -3
- package/package.json +5 -1
- package/parser.jison +68 -5
- package/parser.mjs +111 -60
- package/pretty.mjs +5 -0
- package/repl.mjs +245 -28
- package/scripts/build-repl-html.mjs +76 -5
- 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 +211 -0
- package/tests/test-named-patterns.mjs +189 -0
- package/tests/test-repl-html.mjs +52 -4
- package/tests/test-repl.mjs +223 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
// tests/test-named-patterns.mjs
|
|
2
|
+
// Comprehensive test suite for named patterns and type schemes.
|
|
3
|
+
|
|
4
|
+
import assert from "node:assert/strict";
|
|
5
|
+
import k from "../index.mjs";
|
|
6
|
+
import { Value } from "../Value.mjs";
|
|
7
|
+
|
|
8
|
+
console.log("==> Running Named Patterns Test Suite");
|
|
9
|
+
|
|
10
|
+
// Test 1: 0-arity pattern and composite product
|
|
11
|
+
console.log("--> Test 1: 0-arity pattern (bit[]) and product (byte[])");
|
|
12
|
+
{
|
|
13
|
+
const script = `
|
|
14
|
+
bit[] = < {}0, {}1 >;
|
|
15
|
+
byte[] = {
|
|
16
|
+
bit[] b0, bit[] b1, bit[] b2, bit[] b3,
|
|
17
|
+
bit[] b4, bit[] b5, bit[] b6, bit[] b7
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
is_byte = ? byte[];
|
|
21
|
+
|
|
22
|
+
sample = {
|
|
23
|
+
{}|0 b0, {}|1 b1, {}|0 b2, {}|1 b3,
|
|
24
|
+
{}|0 b4, {}|1 b5, {}|0 b6, {}|1 b7
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
sample is_byte
|
|
28
|
+
`;
|
|
29
|
+
const fn = k.compile(script);
|
|
30
|
+
const out = fn(Value.product({}));
|
|
31
|
+
assert.equal(out.type, "{}");
|
|
32
|
+
assert.equal(out.product.b0.tag, "0");
|
|
33
|
+
assert.equal(out.product.b1.tag, "1");
|
|
34
|
+
console.log(" Passed: 0-arity patterns and composition.");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Test 2: Parameterized list pattern
|
|
38
|
+
console.log("--> Test 2: Parameterized pattern (list[X])");
|
|
39
|
+
{
|
|
40
|
+
const script = `
|
|
41
|
+
bit[] = < {}0, {}1 >;
|
|
42
|
+
list[X] = < {}nil, { X car, Y cdr } cons > = Y;
|
|
43
|
+
|
|
44
|
+
is_bit_list = ? list[bit[]];
|
|
45
|
+
|
|
46
|
+
empty = {}|nil is_bit_list;
|
|
47
|
+
one = { {}|1 car, {}|nil cdr }|cons is_bit_list;
|
|
48
|
+
two = {
|
|
49
|
+
{}|0 car,
|
|
50
|
+
{ {}|1 car, {}|nil cdr }|cons cdr
|
|
51
|
+
}|cons is_bit_list;
|
|
52
|
+
|
|
53
|
+
{ empty empty, one one, two two }
|
|
54
|
+
`;
|
|
55
|
+
const fn = k.compile(script);
|
|
56
|
+
const out = fn(Value.product({}));
|
|
57
|
+
assert.equal(out.product.empty.tag, "nil");
|
|
58
|
+
assert.equal(out.product.one.tag, "cons");
|
|
59
|
+
assert.equal(out.product.one.value.product.car.tag, "1");
|
|
60
|
+
assert.equal(out.product.two.tag, "cons");
|
|
61
|
+
assert.equal(out.product.two.value.product.car.tag, "0");
|
|
62
|
+
console.log(" Passed: Parameterized list pattern.");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Test 3: Multi-parameter templates and composition
|
|
66
|
+
console.log("--> Test 3: Multi-parameter templates (pair, dict)");
|
|
67
|
+
{
|
|
68
|
+
const script = `
|
|
69
|
+
bit[] = < {}0, {}1 >;
|
|
70
|
+
pair[A, B] = { A key, B val };
|
|
71
|
+
list[X] = < {}nil, { X car, Y cdr } cons > = Y;
|
|
72
|
+
dict[K, V] = list[pair[K, V]];
|
|
73
|
+
|
|
74
|
+
is_dict = ? dict[bit[], bit[]];
|
|
75
|
+
|
|
76
|
+
entry = { {}|0 key, {}|1 val };
|
|
77
|
+
d = { entry car, {}|nil cdr }|cons is_dict;
|
|
78
|
+
|
|
79
|
+
d
|
|
80
|
+
`;
|
|
81
|
+
const fn = k.compile(script);
|
|
82
|
+
const out = fn(Value.product({}));
|
|
83
|
+
assert.equal(out.tag, "cons");
|
|
84
|
+
assert.equal(out.value.product.car.product.key.tag, "0");
|
|
85
|
+
assert.equal(out.value.product.car.product.val.tag, "1");
|
|
86
|
+
console.log(" Passed: Multi-parameter template composition.");
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Test 4: Existential internal variables (type unification across fields)
|
|
90
|
+
console.log("--> Test 4: Existential variables (same_pair[])");
|
|
91
|
+
{
|
|
92
|
+
const scriptOk = `
|
|
93
|
+
same_pair[] = { T first, T second };
|
|
94
|
+
check = ? same_pair[];
|
|
95
|
+
{ {}|0 first, {}|1 second } check
|
|
96
|
+
`;
|
|
97
|
+
const fnOk = k.compile(scriptOk);
|
|
98
|
+
const outOk = fnOk(Value.product({}));
|
|
99
|
+
assert.equal(outOk.product.first.tag, "0");
|
|
100
|
+
assert.equal(outOk.product.second.tag, "1");
|
|
101
|
+
|
|
102
|
+
const scriptBad = `
|
|
103
|
+
same_pair[] = { T first, T second };
|
|
104
|
+
check = ? same_pair[];
|
|
105
|
+
{ {} first, {}|0 second } check
|
|
106
|
+
`;
|
|
107
|
+
assert.throws(
|
|
108
|
+
() => k.compile(scriptBad),
|
|
109
|
+
/Cannot unify/
|
|
110
|
+
);
|
|
111
|
+
console.log(" Passed: Existential variables unified across fields.");
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Test 5: Recursive tree with internal knot and list
|
|
115
|
+
console.log("--> Test 5: Recursive tree with knot T (tree[X])");
|
|
116
|
+
{
|
|
117
|
+
const script = `
|
|
118
|
+
list[X] = < {}nil, { X car, Y cdr } cons > = Y;
|
|
119
|
+
tree[X] = < {}nil, { X val, list[T] children } node > = T;
|
|
120
|
+
|
|
121
|
+
bit[] = < {}0, {}1 >;
|
|
122
|
+
is_tree = ? tree[bit[]];
|
|
123
|
+
|
|
124
|
+
leaf = { {}|1 val, {}|nil children }|node is_tree;
|
|
125
|
+
root = {
|
|
126
|
+
{}|0 val,
|
|
127
|
+
{ leaf car, {}|nil cdr }|cons children
|
|
128
|
+
}|node is_tree;
|
|
129
|
+
|
|
130
|
+
{ leaf leaf, root root }
|
|
131
|
+
`;
|
|
132
|
+
const fn = k.compile(script);
|
|
133
|
+
const out = fn(Value.product({}));
|
|
134
|
+
assert.equal(out.product.leaf.tag, "node");
|
|
135
|
+
assert.equal(out.product.leaf.value.product.val.tag, "1");
|
|
136
|
+
assert.equal(out.product.root.tag, "node");
|
|
137
|
+
assert.equal(out.product.root.value.product.val.tag, "0");
|
|
138
|
+
console.log(" Passed: Recursive tree template with list port.");
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Test 6: Polymorphic relation with shared type variable
|
|
142
|
+
console.log("--> Test 6: Polymorphic relation ({ X car, list[X] cdr })");
|
|
143
|
+
{
|
|
144
|
+
const script = `
|
|
145
|
+
list[X] = < {}nil, { X car, Y cdr } cons > = Y;
|
|
146
|
+
head = ? { X car, list[X] cdr } .car;
|
|
147
|
+
|
|
148
|
+
bit[] = < {}0, {}1 >;
|
|
149
|
+
h1 = { {}|1 car, {}|nil cdr } head;
|
|
150
|
+
|
|
151
|
+
h1
|
|
152
|
+
`;
|
|
153
|
+
const fn = k.compile(script);
|
|
154
|
+
const out = fn(Value.product({}));
|
|
155
|
+
assert.equal(out.tag, "1");
|
|
156
|
+
console.log(" Passed: Polymorphic relation head with list[X].");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Test 7: Error handling for templates
|
|
160
|
+
console.log("--> Test 7: Error handling (unknown, arity, cycles, dupes)");
|
|
161
|
+
{
|
|
162
|
+
assert.throws(
|
|
163
|
+
() => k.compile("check = ? missing[]; ()"),
|
|
164
|
+
/Unknown pattern template 'missing\[\]'/
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
assert.throws(
|
|
168
|
+
() => k.compile("list[X] = {}; check = ? list[]; ()"),
|
|
169
|
+
/Pattern 'list' expects 1 argument, got 0/
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
assert.throws(
|
|
173
|
+
() => k.compile("pair[A, B] = {}; check = ? pair[{}]; ()"),
|
|
174
|
+
/Pattern 'pair' expects 2 arguments, got 1/
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
assert.throws(
|
|
178
|
+
() => k.compile("a[] = b[]; b[] = a[]; check = ? a[]; ()"),
|
|
179
|
+
/Cyclic pattern template dependency detected/
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
assert.throws(
|
|
183
|
+
() => k.compile("dupe[X, X] = {}; ()"),
|
|
184
|
+
/Duplicate parameter 'X' in pattern 'dupe'/
|
|
185
|
+
);
|
|
186
|
+
console.log(" Passed: Error conditions correctly handled.");
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
console.log("==> All Named Patterns Tests Passed Successfully!");
|
package/tests/test-repl-html.mjs
CHANGED
|
@@ -140,6 +140,12 @@ 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",
|
|
148
|
+
"djson.mjs",
|
|
143
149
|
"ieee.k",
|
|
144
150
|
"ieee.mjs",
|
|
145
151
|
"int.mjs",
|
|
@@ -521,9 +527,9 @@ if (chromiumBin) {
|
|
|
521
527
|
const v4Collapsed = v4 ? !v4.open : false;
|
|
522
528
|
|
|
523
529
|
// Click Expand All
|
|
524
|
-
const
|
|
525
|
-
const expandBtn =
|
|
526
|
-
const collapseBtn =
|
|
530
|
+
const toolbarActionBtns = lastEntry.querySelectorAll(".k-output-toolbar .k-btn-action");
|
|
531
|
+
const expandBtn = toolbarActionBtns[0];
|
|
532
|
+
const collapseBtn = toolbarActionBtns[1];
|
|
527
533
|
|
|
528
534
|
expandBtn.click();
|
|
529
535
|
const allOpenAfterExpand = v4.open;
|
|
@@ -535,13 +541,48 @@ if (chromiumBin) {
|
|
|
535
541
|
);
|
|
536
542
|
const v4CollapsedAgain = newV4 ? !newV4.open : false;
|
|
537
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
|
+
|
|
538
572
|
return {
|
|
539
573
|
v1Open,
|
|
540
574
|
v2Open,
|
|
541
575
|
v3Open,
|
|
542
576
|
v4Collapsed,
|
|
543
577
|
allOpenAfterExpand,
|
|
544
|
-
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] || ""
|
|
545
586
|
};
|
|
546
587
|
})()`);
|
|
547
588
|
|
|
@@ -551,6 +592,13 @@ if (chromiumBin) {
|
|
|
551
592
|
assert.strictEqual(variantDepthAndActionsCheck.v4Collapsed, true, "Variant level 4 must be collapsed by default");
|
|
552
593
|
assert.strictEqual(variantDepthAndActionsCheck.allOpenAfterExpand, true, "Expand button should expand collapsed nodes");
|
|
553
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}`);
|
|
554
602
|
|
|
555
603
|
// 4. Verify shortened welcome banner
|
|
556
604
|
const bannerCheck = await evaluateAsync(`(() => {
|
package/tests/test-repl.mjs
CHANGED
|
@@ -226,6 +226,18 @@ output = await evaluateInput(':input json.mjs {"left":true,"right":false}', json
|
|
|
226
226
|
assert.match(output[0], /json\.mjs: \{"left":true,"right":false\}/);
|
|
227
227
|
assert.equal(promptForState(jsonState), "> ");
|
|
228
228
|
|
|
229
|
+
// Test djson.mjs in repl
|
|
230
|
+
const djsonState = createState();
|
|
231
|
+
output = await evaluateInput(":codec load ./codecs/djson.mjs", djsonState);
|
|
232
|
+
assert.match(output[0], /^loaded codec djson\.mjs/);
|
|
233
|
+
completions = completeInput(":input dj", djsonState)[0];
|
|
234
|
+
assert(completions.includes(":input djson.mjs"));
|
|
235
|
+
output = await evaluateInput(
|
|
236
|
+
':input djson.mjs {"msg":"hello","values":[1,2]}',
|
|
237
|
+
djsonState
|
|
238
|
+
);
|
|
239
|
+
assert.match(output[0], /djson\.mjs: \{"msg":"hello","values":\[1,2\]\}/);
|
|
240
|
+
|
|
229
241
|
const replPath = fileURLToPath(new URL("../repl.mjs", import.meta.url));
|
|
230
242
|
try {
|
|
231
243
|
fs.rmSync(symlinkPath, { force: true });
|
|
@@ -548,6 +560,217 @@ output = await evaluateInput("len", wasmListState);
|
|
|
548
560
|
assert.match(output[0], /int\.mjs:\s*3/);
|
|
549
561
|
assert.match(output[1], /\(wasm\)/);
|
|
550
562
|
|
|
563
|
+
// Verify :limit command and bounded printing
|
|
564
|
+
const limitState = createState({ engine: "wasm" });
|
|
565
|
+
output = await evaluateInput(":limit", limitState);
|
|
566
|
+
assert.equal(output[0], "Current output limit: none");
|
|
567
|
+
|
|
568
|
+
output = await evaluateInput(":limit 40", limitState);
|
|
569
|
+
assert.equal(output[0], "output limit set to 40");
|
|
570
|
+
|
|
571
|
+
output = await evaluateInput(":limit", limitState);
|
|
572
|
+
assert.equal(output[0], "Current output limit: 40");
|
|
573
|
+
|
|
574
|
+
output = await evaluateInput(":load Examples/arithmetics.k", limitState);
|
|
575
|
+
output = await evaluateInput("{ 10 int x, 5 int y } plus", limitState);
|
|
576
|
+
assert(output[0].includes("..."));
|
|
577
|
+
assert(output[0].length <= 80);
|
|
578
|
+
|
|
579
|
+
output = await evaluateInput(":limit none", limitState);
|
|
580
|
+
assert.equal(output[0], "output limit disabled");
|
|
581
|
+
output = await evaluateInput(":limit", limitState);
|
|
582
|
+
assert.equal(output[0], "Current output limit: none");
|
|
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
|
+
|
|
551
774
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
552
775
|
fs.rmSync(symlinkPath, { force: true });
|
|
553
776
|
console.log("OK");
|