@fraczak/k 8.0.0 → 8.1.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.
@@ -9,8 +9,6 @@ import * as SDK from "../codecs/runtime/codec-sdk.mjs";
9
9
  import * as IntCodec from "../codecs/int.mjs";
10
10
  import * as JsonCodec from "../codecs/json.mjs";
11
11
  import * as DjsonCodec from "../codecs/djson.mjs";
12
- import * as DjsonBtrieCodec from "../codecs/djson-btrie.mjs";
13
- import * as DjsonPtrieCodec from "../codecs/djson-ptrie.mjs";
14
12
  import * as Utf8Codec from "../codecs/utf8.mjs";
15
13
  import * as Utf16Codec from "../codecs/utf16.mjs";
16
14
  import * as IeeeCodec from "../codecs/ieee.mjs";
@@ -57,16 +55,6 @@ for (const [name, codec, sampleInput] of [
57
55
  ["int.mjs", IntCodec, "42"],
58
56
  ["json.mjs", JsonCodec, '{"x": 1}'],
59
57
  ["djson.mjs", DjsonCodec, '{"x": 1, "arr": [true, null, "hi"]}'],
60
- [
61
- "djson-btrie.mjs",
62
- DjsonBtrieCodec,
63
- '{"x": 1, "arr": [true, null, "hi"]}'
64
- ],
65
- [
66
- "djson-ptrie.mjs",
67
- DjsonPtrieCodec,
68
- '{"x": 1, "arr": [true, null, "hi"]}'
69
- ],
70
58
  ["utf8.mjs", Utf8Codec, "hello"],
71
59
  ["utf16.mjs", Utf16Codec, "hello"],
72
60
  ["ieee.mjs", IeeeCodec, "3.14"],
@@ -87,8 +75,6 @@ for (const relPath of [
87
75
  "codecs/int.mjs",
88
76
  "codecs/json.mjs",
89
77
  "codecs/djson.mjs",
90
- "codecs/djson-btrie.mjs",
91
- "codecs/djson-ptrie.mjs",
92
78
  "codecs/utf8.mjs",
93
79
  "codecs/utf16.mjs",
94
80
  "codecs/ieee.mjs",
@@ -328,15 +314,11 @@ console.log("8. Verifying fast string decoding & bounded rendering...");
328
314
  );
329
315
  }
330
316
 
331
- // 9. DJSON Family Singleton Pattern Invariants & Round-Trip
332
- console.log("9. Verifying DJSON family singleton pattern invariants...");
317
+ // 9. DJSON Singleton Pattern Invariants & Round-Trip
318
+ console.log("9. Verifying DJSON singleton pattern invariants...");
333
319
  {
334
320
  assert(Array.isArray(DjsonCodec.DJSON_PATTERN));
335
- assert.equal(DjsonCodec.DJSON_PATTERN.length, 29);
336
- assert(Array.isArray(DjsonBtrieCodec.DJSON_BTRIE_PATTERN));
337
- assert.equal(DjsonBtrieCodec.DJSON_BTRIE_PATTERN.length, 27);
338
- assert(Array.isArray(DjsonPtrieCodec.DJSON_PTRIE_PATTERN));
339
- assert.equal(DjsonPtrieCodec.DJSON_PTRIE_PATTERN.length, 33);
321
+ assert.equal(DjsonCodec.DJSON_PATTERN.length, 27);
340
322
 
341
323
  const samples = [
342
324
  null,
@@ -351,19 +333,7 @@ console.log("9. Verifying DJSON family singleton pattern invariants...");
351
333
  ];
352
334
 
353
335
  for (const [label, codec, pat, relPath] of [
354
- ["djson", DjsonCodec, DjsonCodec.DJSON_PATTERN, "codecs/djson.mjs"],
355
- [
356
- "djson-btrie",
357
- DjsonBtrieCodec,
358
- DjsonBtrieCodec.DJSON_BTRIE_PATTERN,
359
- "codecs/djson-btrie.mjs"
360
- ],
361
- [
362
- "djson-ptrie",
363
- DjsonPtrieCodec,
364
- DjsonPtrieCodec.DJSON_PTRIE_PATTERN,
365
- "codecs/djson-ptrie.mjs"
366
- ]
336
+ ["djson", DjsonCodec, DjsonCodec.DJSON_PATTERN, "codecs/djson.mjs"]
367
337
  ]) {
368
338
  for (const sample of samples) {
369
339
  const jsonStr = JSON.stringify(sample);
@@ -0,0 +1,285 @@
1
+ // tests/test-djson-methods.mjs
2
+ // Test suite for standard dynamic JSON methods working on json? type.
3
+
4
+ import assert from "node:assert/strict";
5
+ import { readFileSync } from "node:fs";
6
+ import path from "node:path";
7
+ import { fileURLToPath } from "node:url";
8
+
9
+ import k from "../index.mjs";
10
+ import { Value } from "../Value.mjs";
11
+ import {
12
+ fromJsonValue,
13
+ toJsonValue
14
+ } from "../codecs/djson.mjs";
15
+ import {
16
+ textToStringValue,
17
+ stringValueToText
18
+ } from "../codecs/runtime/unicode-string.mjs";
19
+
20
+ const root = path.resolve(
21
+ path.dirname(fileURLToPath(import.meta.url)),
22
+ ".."
23
+ );
24
+ const libPath = path.join(root, "Examples", "djson.k");
25
+ const djsonK = readFileSync(libPath, "utf8");
26
+
27
+ function compileOp(op) {
28
+ return k.compile(`${djsonK}\n${op}\n`);
29
+ }
30
+
31
+ function makeIndex(n) {
32
+ if (n === 0) {
33
+ return Value.variant(
34
+ "0",
35
+ Value.variant("_", Value.product({}))
36
+ );
37
+ }
38
+ const s = n.toString(2);
39
+ let v = Value.variant("_", Value.product({}));
40
+ for (let i = s.length - 1; i >= 0; i--) {
41
+ v = Value.variant(s[i], v);
42
+ }
43
+ return v;
44
+ }
45
+
46
+ console.log("==> Running Dynamic JSON (json?) Methods Test Suite");
47
+
48
+ // Test 1: Object Accessors (get, has?, set, del)
49
+ console.log("--> Test 1: Object Accessors (get, has?, set, del)");
50
+ {
51
+ const fnGet = compileOp("get");
52
+ const fnHas = compileOp("has?");
53
+ const fnSet = compileOp("set");
54
+ const fnDel = compileOp("del");
55
+
56
+ const doc = {
57
+ name: "Alice",
58
+ age: 30,
59
+ tags: ["developer", "k"]
60
+ };
61
+ const kDoc = fromJsonValue(doc);
62
+
63
+ // get "name"
64
+ const resName = fnGet(Value.product({
65
+ key: textToStringValue("name"),
66
+ json: kDoc
67
+ }));
68
+ assert.equal(toJsonValue(resName), "Alice");
69
+
70
+ // get "age"
71
+ const resAge = fnGet(Value.product({
72
+ key: textToStringValue("age"),
73
+ json: kDoc
74
+ }));
75
+ assert.equal(toJsonValue(resAge), 30);
76
+
77
+ // has? hit
78
+ const hit = fnHas(Value.product({
79
+ key: textToStringValue("name"),
80
+ json: kDoc
81
+ }));
82
+ assert.ok(hit);
83
+
84
+ // has? miss
85
+ const miss = fnHas(Value.product({
86
+ key: textToStringValue("missing"),
87
+ json: kDoc
88
+ }));
89
+ assert.equal(miss, undefined);
90
+
91
+ // set "city" = "Paris"
92
+ const withCity = fnSet(Value.product({
93
+ key: textToStringValue("city"),
94
+ val: fromJsonValue("Paris"),
95
+ json: kDoc
96
+ }));
97
+ assert.equal(toJsonValue(withCity).city, "Paris");
98
+ assert.equal(toJsonValue(withCity).name, "Alice");
99
+
100
+ // del "age"
101
+ const withoutAge = fnDel(Value.product({
102
+ key: textToStringValue("age"),
103
+ json: withCity
104
+ }));
105
+ assert.equal(toJsonValue(withoutAge).age, undefined);
106
+ assert.equal(toJsonValue(withoutAge).city, "Paris");
107
+
108
+ console.log(" Passed: Object get, has?, set, del on json?.");
109
+ }
110
+
111
+ // Test 2: List Accessors (nth, has_index?, set_nth, del_nth, push)
112
+ console.log("--> Test 2: List Accessors (nth, set_nth, del_nth, push)");
113
+ {
114
+ const fnNth = compileOp("nth");
115
+ const fnHasIdx = compileOp("has_index?");
116
+ const fnSetNth = compileOp("set_nth");
117
+ const fnDelNth = compileOp("del_nth");
118
+ const fnPush = compileOp("push");
119
+
120
+ const list = [10, 20, 30];
121
+ const kList = fromJsonValue(list);
122
+
123
+ // nth 0, 1, 2
124
+ assert.equal(
125
+ toJsonValue(fnNth(Value.product({
126
+ index: makeIndex(0),
127
+ json: kList
128
+ }))),
129
+ 10
130
+ );
131
+ assert.equal(
132
+ toJsonValue(fnNth(Value.product({
133
+ index: makeIndex(1),
134
+ json: kList
135
+ }))),
136
+ 20
137
+ );
138
+ assert.equal(
139
+ toJsonValue(fnNth(Value.product({
140
+ index: makeIndex(2),
141
+ json: kList
142
+ }))),
143
+ 30
144
+ );
145
+
146
+ // has_index? hit and miss
147
+ assert.ok(fnHasIdx(Value.product({
148
+ index: makeIndex(1),
149
+ json: kList
150
+ })));
151
+ assert.equal(
152
+ fnHasIdx(Value.product({
153
+ index: makeIndex(99),
154
+ json: kList
155
+ })),
156
+ undefined
157
+ );
158
+
159
+ // set_nth 1 -> 99
160
+ const updatedList = fnSetNth(Value.product({
161
+ index: makeIndex(1),
162
+ val: fromJsonValue(99),
163
+ json: kList
164
+ }));
165
+ assert.deepEqual(toJsonValue(updatedList), [10, 99, 30]);
166
+
167
+ // del_nth 1
168
+ const delList = fnDelNth(Value.product({
169
+ index: makeIndex(1),
170
+ json: updatedList
171
+ }));
172
+ assert.deepEqual(toJsonValue(delList), [10, 30]);
173
+ assert.equal(
174
+ fnHasIdx(Value.product({
175
+ index: makeIndex(1),
176
+ json: delList
177
+ })),
178
+ undefined
179
+ );
180
+
181
+ // push 40
182
+ const pushed = fnPush(Value.product({
183
+ val: fromJsonValue(40),
184
+ json: kList
185
+ }));
186
+ assert.deepEqual(toJsonValue(pushed), [10, 20, 30, 40]);
187
+
188
+ console.log(" Passed: List nth, set_nth, del_nth, push on json?.");
189
+ }
190
+
191
+ // Test 3: Keys and Values (keys, keys_raw, values, values_raw)
192
+ console.log("--> Test 3: Keys and Values (keys, values)");
193
+ {
194
+ const fnKeys = compileOp("keys");
195
+ const fnKeysRaw = compileOp("keys_raw");
196
+ const fnValues = compileOp("values");
197
+ const fnValuesRaw = compileOp("values_raw");
198
+
199
+ const doc = { b: 2, a: 1, c: 3 };
200
+ const kDoc = fromJsonValue(doc);
201
+
202
+ // keys (json? list of string)
203
+ const resKeys = fnKeys(kDoc);
204
+ assert.deepEqual(toJsonValue(resKeys), ["a", "b", "c"]);
205
+
206
+ // keys_raw (raw K cons-list of strings)
207
+ const resKeysRaw = fnKeysRaw(kDoc);
208
+ const rawKeyStrings = [];
209
+ let curr = resKeysRaw;
210
+ while (curr && curr.type === "<>" && curr.tag === "cons") {
211
+ rawKeyStrings.push(stringValueToText(curr.value.product.car));
212
+ curr = curr.value.product.cdr;
213
+ }
214
+ assert.deepEqual(rawKeyStrings, ["a", "b", "c"]);
215
+
216
+ // values on object (json? list of values)
217
+ const resValues = fnValues(kDoc);
218
+ assert.deepEqual(toJsonValue(resValues), [1, 2, 3]);
219
+
220
+ // values on list (json? identity)
221
+ const listJson = fromJsonValue([100, 200]);
222
+ assert.deepEqual(toJsonValue(fnValues(listJson)), [100, 200]);
223
+
224
+ console.log(" Passed: Keys and values extraction on json?.");
225
+ }
226
+
227
+ // Test 4: Cardinality and Length (length, length_nat)
228
+ console.log("--> Test 4: Cardinality and Length (length, length_nat)");
229
+ {
230
+ const fnLength = compileOp("length");
231
+
232
+ const kObj = fromJsonValue({ x: 1, y: 2, z: 3, w: 4 });
233
+ const kArr = fromJsonValue(["a", "b", "c"]);
234
+ const kStr = fromJsonValue("hello");
235
+
236
+ // length returns json? number (float64)
237
+ assert.equal(toJsonValue(fnLength(kObj)), 4);
238
+ assert.equal(toJsonValue(fnLength(kArr)), 3);
239
+ assert.equal(toJsonValue(fnLength(kStr)), 5);
240
+
241
+ console.log(
242
+ " Passed: Length queries on json? object, list, string."
243
+ );
244
+ }
245
+
246
+ // Test 5: Type Predicates and type_of
247
+ console.log("--> Test 5: Type Predicates and type_of");
248
+ {
249
+ const fnTypeOf = compileOp("type_of");
250
+ const fnIsObj = compileOp("is_object?");
251
+ const fnIsList = compileOp("is_list?");
252
+ const fnIsStr = compileOp("is_string?");
253
+ const fnIsNum = compileOp("is_number?");
254
+ const fnIsBool = compileOp("is_bool?");
255
+ const fnIsNull = compileOp("is_null?");
256
+
257
+ const kObj = fromJsonValue({ a: 1 });
258
+ const kArr = fromJsonValue([1, 2]);
259
+ const kStr = fromJsonValue("hi");
260
+ const kNum = fromJsonValue(3.14);
261
+ const kBool = fromJsonValue(true);
262
+ const kNull = fromJsonValue(null);
263
+
264
+ assert.equal(fnTypeOf(kObj).tag, "object");
265
+ assert.equal(fnTypeOf(kArr).tag, "list");
266
+ assert.equal(fnTypeOf(kStr).tag, "string");
267
+ assert.equal(fnTypeOf(kNum).tag, "number");
268
+ assert.equal(fnTypeOf(kBool).tag, "bool");
269
+ assert.equal(fnTypeOf(kNull).tag, "null");
270
+
271
+ assert.ok(fnIsObj(kObj));
272
+ assert.equal(fnIsObj(kArr), undefined);
273
+
274
+ assert.ok(fnIsList(kArr));
275
+ assert.equal(fnIsList(kObj), undefined);
276
+
277
+ assert.ok(fnIsStr(kStr));
278
+ assert.ok(fnIsNum(kNum));
279
+ assert.ok(fnIsBool(kBool));
280
+ assert.ok(fnIsNull(kNull));
281
+
282
+ console.log(" Passed: Type predicates and type_of inspection.");
283
+ }
284
+
285
+ console.log("==> All Dynamic JSON Methods Tests Passed Successfully!");
@@ -140,10 +140,6 @@ 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
143
  "djson.k",
148
144
  "djson.mjs",
149
145
  "ieee.k",
@@ -581,74 +581,33 @@ 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);
584
+ // Verify Examples/djson.k (json?, get, nth, set, del)
585
+ const djsonKState = createState({ engine: "wasm" });
586
+ output = await evaluateInput(":load Examples/djson.k", djsonKState);
587
587
  assert.equal(output[0], "loaded Examples/djson.k");
588
- await evaluateInput(":codec load codecs/djson.mjs", jsonKState);
588
+ await evaluateInput(":codec load codecs/djson.mjs", djsonKState);
589
589
  await evaluateInput(
590
- ':input djson.mjs {"name":"Alice","age":30,"active":true}',
591
- jsonKState
590
+ ':input djson.mjs ["age", {"name":"Alice","age":30}]',
591
+ djsonKState
592
592
  );
593
593
  output = await evaluateInput(
594
- "{/object cdr car .key key, /object object} dot",
595
- jsonKState
594
+ "{{0 index, () json} nth /string key, " +
595
+ "{1 index, () json} nth json} get",
596
+ djsonKState
596
597
  );
597
598
  assert.match(output[0], /djson\.mjs: 30/);
598
599
 
599
600
  await evaluateInput(
600
- ':input djson.mjs {"name":"Alice","age":30}',
601
- jsonKState
601
+ ':input djson.mjs ["age", {"name":"Alice","age":30}]',
602
+ djsonKState
602
603
  );
603
604
  output = await evaluateInput(
604
- "{/object car .key key, /object object} remove object",
605
- jsonKState
605
+ "{{0 index, () json} nth /string key, " +
606
+ "{1 index, () json} nth json} del",
607
+ djsonKState
606
608
  );
607
609
  assert.match(output[0], /djson\.mjs: \{"name":"Alice"\}/);
608
610
 
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
611
  // Verify named pattern persistence, listing, display, and tab completion
653
612
  const patState = createState({ engine: "wasm" });
654
613