@fraczak/k 7.2.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.
- package/CHANGELOG.md +90 -0
- package/DOCS/BIG_PICTURE.md +54 -7
- package/DOCS/CODECS.md +21 -13
- package/DOCS/CODEC_ARCHITECTURE.md +1 -1
- package/DOCS/DICTIONARY.md +13 -4
- package/DOCS/FILE_FORMATS.md +3 -2
- package/DOCS/JSON_APPROACHES_COMPARISON.md +302 -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 +3 -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.k +789 -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 +40 -16
- package/browser/repl-browser.mjs +2 -0
- package/browser/tree-view.mjs +35 -16
- package/browser/vfs-data.mjs +10 -8
- package/codecs/README.md +1 -1
- package/codecs/djson.mjs +375 -84
- package/core.k +79 -115
- package/index.mjs +3 -0
- package/named-patterns.mjs +267 -0
- package/object.mjs +31 -3
- package/package.json +1 -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 +58 -4
- package/scripts/run-tests.mjs +10 -0
- package/symbol-table.mjs +20 -2
- package/tests/test-codecs.mjs +30 -17
- package/tests/test-djson-methods.mjs +285 -0
- package/tests/test-named-patterns.mjs +189 -0
- package/tests/test-repl-html.mjs +47 -4
- package/tests/test-repl.mjs +149 -0
package/codecs/README.md
CHANGED
|
@@ -105,7 +105,7 @@ as `k-parse.mjs`, keep that name. Every installed codec binary supports
|
|
|
105
105
|
| `k-print` | `k-print.mjs` | Read a binary pattern+value stream and write the decoded JSON-like value. Use `--debug` to include the decoded pattern. |
|
|
106
106
|
| `k-show` | `show.mjs` | Pass the wire stream through unchanged on stdout while printing the decoded value and filter to stderr. |
|
|
107
107
|
| `k-json` | `json.mjs` | Convert JSON to/from the binary stream with `--parse` or `--print`. |
|
|
108
|
-
| `k-djson` | `djson.mjs` |
|
|
108
|
+
| `k-djson` | `djson.mjs` | Dynamic binary bit-trie JSON codec. |
|
|
109
109
|
| `k-int` | `int.mjs` | Convert decimal integers to/from the binary stream with `--parse` or `--print`. |
|
|
110
110
|
| `k-ieee` | `ieee.mjs` | Convert float literals to/from the binary stream with `--parse` or `--print`. |
|
|
111
111
|
| `k-unit` | `unit.mjs` | Write or validate the unit value `{}` with `--parse` or `--print`. |
|
package/codecs/djson.mjs
CHANGED
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Dynamic JSON (djson)
|
|
4
|
+
* Dynamic JSON (djson) binary bit-trie codec.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* Uses a unified binary bit-trie for both JSON arrays (list) and JSON
|
|
7
|
+
* objects (object):
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
9
|
+
* TRIE = <
|
|
10
|
+
* {} nil,
|
|
11
|
+
* {
|
|
12
|
+
* < {} nil, JSON val > val,
|
|
13
|
+
* TRIE 0,
|
|
14
|
+
* TRIE 1
|
|
15
|
+
* } trie
|
|
16
16
|
* >
|
|
17
|
+
*
|
|
18
|
+
* - In `list`, element at index i >= 0 is stored at the canonical binary
|
|
19
|
+
* bit-path of i (0 -> [0], 1 -> [1], 2 -> [1,0], 3 -> [1,1], ...).
|
|
20
|
+
* - In `object`, entry with key k is stored at the prefix-free Unicode
|
|
21
|
+
* character bit-path of k.
|
|
17
22
|
*/
|
|
18
23
|
|
|
19
24
|
import {
|
|
@@ -26,22 +31,24 @@ import {
|
|
|
26
31
|
} from "./runtime/codec-sdk.mjs";
|
|
27
32
|
import { encodeNumberToValue, decodeValueToNumber } from "./ieee.mjs";
|
|
28
33
|
import {
|
|
34
|
+
codePointToUnicodeValue,
|
|
35
|
+
unicodeValueToCodePoint,
|
|
29
36
|
textToStringValue,
|
|
30
37
|
stringValueToText
|
|
31
38
|
} from "./runtime/unicode-string.mjs";
|
|
32
39
|
|
|
33
40
|
export const doc = `
|
|
34
|
-
Dynamic JSON (djson)
|
|
41
|
+
Dynamic JSON (djson) binary bit-trie codec.
|
|
35
42
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
Represents both JSON arrays (list) and JSON objects (object) using a
|
|
44
|
+
unified binary bit-trie:
|
|
45
|
+
TRIE = <
|
|
46
|
+
{} nil,
|
|
47
|
+
{
|
|
48
|
+
< {} nil, JSON val > val,
|
|
49
|
+
TRIE 0,
|
|
50
|
+
TRIE 1
|
|
51
|
+
} trie
|
|
45
52
|
>
|
|
46
53
|
`;
|
|
47
54
|
|
|
@@ -122,13 +129,21 @@ export const DJSON_PATTERN = patternFromFilter(`
|
|
|
122
129
|
< {} true, {} false > bool,
|
|
123
130
|
{} null,
|
|
124
131
|
|
|
125
|
-
<
|
|
132
|
+
<
|
|
133
|
+
{} nil,
|
|
134
|
+
{
|
|
135
|
+
< {} nil, JSON val > val,
|
|
136
|
+
TRIE 0,
|
|
137
|
+
TRIE 1
|
|
138
|
+
} trie
|
|
139
|
+
> = TRIE list,
|
|
126
140
|
|
|
127
|
-
|
|
141
|
+
TRIE object
|
|
128
142
|
> = JSON
|
|
129
143
|
`);
|
|
130
144
|
|
|
131
145
|
const UNIT = Value.product({});
|
|
146
|
+
const NIL = Value.variant("nil", UNIT);
|
|
132
147
|
|
|
133
148
|
function requireProduct(value, where) {
|
|
134
149
|
if (!isProduct(value)) {
|
|
@@ -137,37 +152,298 @@ function requireProduct(value, where) {
|
|
|
137
152
|
return value.product;
|
|
138
153
|
}
|
|
139
154
|
|
|
140
|
-
function
|
|
141
|
-
let
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
155
|
+
function pushBitsMSB(out, width, n) {
|
|
156
|
+
for (let i = width - 1; i >= 0; i--) {
|
|
157
|
+
out.push((n >> i) & 1);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function readBitsMSB(bits, offset, width) {
|
|
162
|
+
let n = 0;
|
|
163
|
+
for (let i = 0; i < width; i++) {
|
|
164
|
+
n = (n << 1) | bits[offset + i];
|
|
165
|
+
}
|
|
166
|
+
return n;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function indexToBitPath(index) {
|
|
170
|
+
if (index === 0) return [0];
|
|
171
|
+
const bits = [];
|
|
172
|
+
let n = index;
|
|
173
|
+
while (n > 0) {
|
|
174
|
+
bits.push(n & 1);
|
|
175
|
+
n = Math.floor(n / 2);
|
|
176
|
+
}
|
|
177
|
+
bits.reverse();
|
|
178
|
+
return bits;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function bitPathToIndex(bits) {
|
|
182
|
+
if (bits.length === 1 && bits[0] === 0) return 0;
|
|
183
|
+
let n = 0;
|
|
184
|
+
for (const b of bits) {
|
|
185
|
+
n = n * 2 + b;
|
|
186
|
+
}
|
|
187
|
+
return n;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function codePointToBitPath(cp, out = []) {
|
|
191
|
+
if (cp <= 0x7f) {
|
|
192
|
+
out.push(0);
|
|
193
|
+
pushBitsMSB(out, 7, cp);
|
|
194
|
+
return out;
|
|
195
|
+
}
|
|
196
|
+
if (cp <= 0x7ff) {
|
|
197
|
+
out.push(1, 0);
|
|
198
|
+
pushBitsMSB(out, 11, cp);
|
|
199
|
+
return out;
|
|
200
|
+
}
|
|
201
|
+
if (cp <= 0xffff) {
|
|
202
|
+
const hi = (cp >> 8) & 0xff;
|
|
203
|
+
const lo = cp & 0xff;
|
|
204
|
+
if (hi >= 0xe0 && hi <= 0xf8) {
|
|
205
|
+
out.push(1, 1, 1, 0);
|
|
206
|
+
if (hi <= 0xef) {
|
|
207
|
+
out.push(0);
|
|
208
|
+
pushBitsMSB(out, 4, hi - 0xe0);
|
|
209
|
+
} else if (hi <= 0xf7) {
|
|
210
|
+
out.push(1, 0);
|
|
211
|
+
pushBitsMSB(out, 3, hi - 0xf0);
|
|
212
|
+
} else {
|
|
213
|
+
out.push(1, 1);
|
|
214
|
+
}
|
|
215
|
+
pushBitsMSB(out, 8, lo);
|
|
216
|
+
return out;
|
|
217
|
+
}
|
|
218
|
+
out.push(1, 1, 0);
|
|
219
|
+
if (hi >= 0x08 && hi <= 0x0f) {
|
|
220
|
+
out.push(0, 0, 0);
|
|
221
|
+
pushBitsMSB(out, 3, hi - 0x08);
|
|
222
|
+
} else if (hi >= 0x10 && hi <= 0x7f) {
|
|
223
|
+
out.push(0, 0, 1);
|
|
224
|
+
pushBitsMSB(out, 7, hi - 0x10);
|
|
225
|
+
} else if (hi >= 0x80 && hi <= 0xcf) {
|
|
226
|
+
out.push(0, 1, 0);
|
|
227
|
+
pushBitsMSB(out, 7, hi - 0x80);
|
|
228
|
+
} else if (hi >= 0xd0 && hi <= 0xd7) {
|
|
229
|
+
out.push(0, 1, 1);
|
|
230
|
+
pushBitsMSB(out, 3, hi - 0xd0);
|
|
231
|
+
} else if (hi === 0xf9) {
|
|
232
|
+
out.push(1, 0, 0);
|
|
233
|
+
} else if (hi >= 0xfa && hi <= 0xfb) {
|
|
234
|
+
out.push(1, 0, 1);
|
|
235
|
+
pushBitsMSB(out, 1, hi - 0xfa);
|
|
236
|
+
} else if (hi >= 0xfc && hi <= 0xfd) {
|
|
237
|
+
out.push(1, 1, 0);
|
|
238
|
+
pushBitsMSB(out, 1, hi - 0xfc);
|
|
239
|
+
} else if (hi >= 0xfe && hi <= 0xff) {
|
|
240
|
+
out.push(1, 1, 1);
|
|
241
|
+
pushBitsMSB(out, 1, hi - 0xfe);
|
|
242
|
+
} else {
|
|
243
|
+
throw new Error(`Unsupported BMP hi byte: ${hi}`);
|
|
244
|
+
}
|
|
245
|
+
pushBitsMSB(out, 8, lo);
|
|
246
|
+
return out;
|
|
247
|
+
}
|
|
248
|
+
const mid = (cp >> 8) & 0xff;
|
|
249
|
+
const lo = cp & 0xff;
|
|
250
|
+
if (cp <= 0x1ffff) {
|
|
251
|
+
out.push(1, 1, 1, 1, 0);
|
|
252
|
+
pushBitsMSB(out, 8, mid);
|
|
253
|
+
pushBitsMSB(out, 8, lo);
|
|
254
|
+
return out;
|
|
255
|
+
}
|
|
256
|
+
const plane = cp >> 16;
|
|
257
|
+
out.push(1, 1, 1, 1, 1);
|
|
258
|
+
if (plane >= 2 && plane <= 3) {
|
|
259
|
+
out.push(0, 0);
|
|
260
|
+
pushBitsMSB(out, 1, plane - 2);
|
|
261
|
+
} else if (plane >= 4 && plane <= 7) {
|
|
262
|
+
out.push(0, 1);
|
|
263
|
+
pushBitsMSB(out, 2, plane - 4);
|
|
264
|
+
} else if (plane >= 8 && plane <= 15) {
|
|
265
|
+
out.push(1, 0);
|
|
266
|
+
pushBitsMSB(out, 3, plane - 8);
|
|
267
|
+
} else if (plane === 16) {
|
|
268
|
+
out.push(1, 1);
|
|
269
|
+
} else {
|
|
270
|
+
throw new Error(`Unsupported plane: ${plane}`);
|
|
271
|
+
}
|
|
272
|
+
pushBitsMSB(out, 8, mid);
|
|
273
|
+
pushBitsMSB(out, 8, lo);
|
|
274
|
+
return out;
|
|
149
275
|
}
|
|
150
276
|
|
|
151
|
-
function
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
277
|
+
export function stringToBitPath(str) {
|
|
278
|
+
const out = [];
|
|
279
|
+
for (const ch of str) {
|
|
280
|
+
codePointToBitPath(ch.codePointAt(0), out);
|
|
281
|
+
}
|
|
282
|
+
return out;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export function bitPathToString(bits) {
|
|
286
|
+
let i = 0;
|
|
287
|
+
let out = "";
|
|
288
|
+
while (i < bits.length) {
|
|
289
|
+
if (bits[i] === 0) {
|
|
290
|
+
const cp = readBitsMSB(bits, i + 1, 7);
|
|
291
|
+
out += String.fromCodePoint(cp);
|
|
292
|
+
i += 8;
|
|
293
|
+
continue;
|
|
157
294
|
}
|
|
158
|
-
if (
|
|
159
|
-
|
|
160
|
-
|
|
295
|
+
if (bits[i + 1] === 0) {
|
|
296
|
+
const cp = readBitsMSB(bits, i + 2, 11);
|
|
297
|
+
out += String.fromCodePoint(cp);
|
|
298
|
+
i += 13;
|
|
299
|
+
continue;
|
|
161
300
|
}
|
|
162
|
-
if (
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
301
|
+
if (bits[i + 2] === 0) {
|
|
302
|
+
i += 3;
|
|
303
|
+
const tag = readBitsMSB(bits, i, 3);
|
|
304
|
+
i += 3;
|
|
305
|
+
let hi;
|
|
306
|
+
if (tag === 0) {
|
|
307
|
+
hi = 0x08 + readBitsMSB(bits, i, 3);
|
|
308
|
+
i += 3;
|
|
309
|
+
} else if (tag === 1) {
|
|
310
|
+
hi = 0x10 + readBitsMSB(bits, i, 7);
|
|
311
|
+
i += 7;
|
|
312
|
+
} else if (tag === 2) {
|
|
313
|
+
hi = 0x80 + readBitsMSB(bits, i, 7);
|
|
314
|
+
i += 7;
|
|
315
|
+
} else if (tag === 3) {
|
|
316
|
+
hi = 0xd0 + readBitsMSB(bits, i, 3);
|
|
317
|
+
i += 3;
|
|
318
|
+
} else if (tag === 4) {
|
|
319
|
+
hi = 0xf9;
|
|
320
|
+
} else if (tag === 5) {
|
|
321
|
+
hi = 0xfa + readBitsMSB(bits, i, 1);
|
|
322
|
+
i += 1;
|
|
323
|
+
} else if (tag === 6) {
|
|
324
|
+
hi = 0xfc + readBitsMSB(bits, i, 1);
|
|
325
|
+
i += 1;
|
|
326
|
+
} else {
|
|
327
|
+
hi = 0xfe + readBitsMSB(bits, i, 1);
|
|
328
|
+
i += 1;
|
|
329
|
+
}
|
|
330
|
+
const lo = readBitsMSB(bits, i, 8);
|
|
331
|
+
i += 8;
|
|
332
|
+
out += String.fromCodePoint((hi << 8) | lo);
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
if (bits[i + 3] === 0) {
|
|
336
|
+
i += 4;
|
|
337
|
+
let hi;
|
|
338
|
+
if (bits[i] === 0) {
|
|
339
|
+
hi = 0xe0 + readBitsMSB(bits, i + 1, 4);
|
|
340
|
+
i += 5;
|
|
341
|
+
} else if (bits[i + 1] === 0) {
|
|
342
|
+
hi = 0xf0 + readBitsMSB(bits, i + 2, 3);
|
|
343
|
+
i += 5;
|
|
344
|
+
} else {
|
|
345
|
+
hi = 0xf8;
|
|
346
|
+
i += 2;
|
|
347
|
+
}
|
|
348
|
+
const lo = readBitsMSB(bits, i, 8);
|
|
349
|
+
i += 8;
|
|
350
|
+
out += String.fromCodePoint((hi << 8) | lo);
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
if (bits[i + 4] === 0) {
|
|
354
|
+
i += 5;
|
|
355
|
+
const mid = readBitsMSB(bits, i, 8);
|
|
356
|
+
const lo = readBitsMSB(bits, i + 8, 8);
|
|
357
|
+
i += 16;
|
|
358
|
+
out += String.fromCodePoint((1 << 16) | (mid << 8) | lo);
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
i += 5;
|
|
362
|
+
let plane;
|
|
363
|
+
if (bits[i] === 0 && bits[i + 1] === 0) {
|
|
364
|
+
plane = 2 + readBitsMSB(bits, i + 2, 1);
|
|
365
|
+
i += 3;
|
|
366
|
+
} else if (bits[i] === 0 && bits[i + 1] === 1) {
|
|
367
|
+
plane = 4 + readBitsMSB(bits, i + 2, 2);
|
|
368
|
+
i += 4;
|
|
369
|
+
} else if (bits[i] === 1 && bits[i + 1] === 0) {
|
|
370
|
+
plane = 8 + readBitsMSB(bits, i + 2, 3);
|
|
371
|
+
i += 5;
|
|
372
|
+
} else {
|
|
373
|
+
plane = 16;
|
|
374
|
+
i += 2;
|
|
375
|
+
}
|
|
376
|
+
const mid = readBitsMSB(bits, i, 8);
|
|
377
|
+
const lo = readBitsMSB(bits, i + 8, 8);
|
|
378
|
+
i += 16;
|
|
379
|
+
out += String.fromCodePoint((plane << 16) | (mid << 8) | lo);
|
|
380
|
+
}
|
|
381
|
+
return out;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function makeMutableNode() {
|
|
385
|
+
return { val: null, b0: null, b1: null };
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function insertMutableTrie(root, bits, kVal) {
|
|
389
|
+
let curr = root;
|
|
390
|
+
for (let i = 0; i < bits.length; i++) {
|
|
391
|
+
if (bits[i] === 0) {
|
|
392
|
+
if (!curr.b0) curr.b0 = makeMutableNode();
|
|
393
|
+
curr = curr.b0;
|
|
394
|
+
} else {
|
|
395
|
+
if (!curr.b1) curr.b1 = makeMutableNode();
|
|
396
|
+
curr = curr.b1;
|
|
166
397
|
}
|
|
167
|
-
const pair = requireProduct(current.value, `${where}.cons`);
|
|
168
|
-
result.push(pair.car);
|
|
169
|
-
current = pair.cdr;
|
|
170
398
|
}
|
|
399
|
+
curr.val = kVal;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function mutableNodeToK(node) {
|
|
403
|
+
if (!node) return NIL;
|
|
404
|
+
const valField = node.val !== null
|
|
405
|
+
? Value.variant("val", node.val)
|
|
406
|
+
: NIL;
|
|
407
|
+
const b0 = mutableNodeToK(node.b0);
|
|
408
|
+
const b1 = mutableNodeToK(node.b1);
|
|
409
|
+
if (node.val === null && b0 === NIL && b1 === NIL) {
|
|
410
|
+
return NIL;
|
|
411
|
+
}
|
|
412
|
+
return Value.variant("trie", Value.product({
|
|
413
|
+
val: valField,
|
|
414
|
+
0: b0,
|
|
415
|
+
1: b1
|
|
416
|
+
}));
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function collectTrieEntries(kNode, pathBits = [], entries = []) {
|
|
420
|
+
if (!isVariant(kNode)) {
|
|
421
|
+
throw new Error("djson: expected variant (nil or trie)");
|
|
422
|
+
}
|
|
423
|
+
if (kNode.tag === "nil") {
|
|
424
|
+
return entries;
|
|
425
|
+
}
|
|
426
|
+
if (kNode.tag !== "trie") {
|
|
427
|
+
throw new Error(`djson: unexpected tag ${kNode.tag}`);
|
|
428
|
+
}
|
|
429
|
+
const prod = requireProduct(kNode.value, "djson.trie");
|
|
430
|
+
if (!isVariant(prod.val)) {
|
|
431
|
+
throw new Error("djson.trie.val: expected variant");
|
|
432
|
+
}
|
|
433
|
+
if (prod.val.tag === "val") {
|
|
434
|
+
entries.push({ bits: pathBits.slice(), val: prod.val.value });
|
|
435
|
+
} else if (prod.val.tag !== "nil") {
|
|
436
|
+
throw new Error(
|
|
437
|
+
`djson.trie.val: unexpected tag ${prod.val.tag}`
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
pathBits.push(0);
|
|
441
|
+
collectTrieEntries(prod[0], pathBits, entries);
|
|
442
|
+
pathBits.pop();
|
|
443
|
+
pathBits.push(1);
|
|
444
|
+
collectTrieEntries(prod[1], pathBits, entries);
|
|
445
|
+
pathBits.pop();
|
|
446
|
+
return entries;
|
|
171
447
|
}
|
|
172
448
|
|
|
173
449
|
export function fromJsonValue(value) {
|
|
@@ -190,81 +466,96 @@ export function fromJsonValue(value) {
|
|
|
190
466
|
return Value.variant("string", textToStringValue(value));
|
|
191
467
|
}
|
|
192
468
|
if (Array.isArray(value)) {
|
|
193
|
-
|
|
469
|
+
if (value.length === 0) {
|
|
470
|
+
return Value.variant("list", NIL);
|
|
471
|
+
}
|
|
472
|
+
const root = makeMutableNode();
|
|
473
|
+
for (let i = 0; i < value.length; i++) {
|
|
474
|
+
insertMutableTrie(root, indexToBitPath(i), fromJsonValue(value[i]));
|
|
475
|
+
}
|
|
476
|
+
return Value.variant("list", mutableNodeToK(root));
|
|
194
477
|
}
|
|
195
478
|
if (value && typeof value === "object") {
|
|
196
|
-
const keys = Object.keys(value)
|
|
197
|
-
|
|
198
|
-
Value.
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
479
|
+
const keys = Object.keys(value);
|
|
480
|
+
if (keys.length === 0) {
|
|
481
|
+
return Value.variant("object", NIL);
|
|
482
|
+
}
|
|
483
|
+
const root = makeMutableNode();
|
|
484
|
+
for (const k of keys) {
|
|
485
|
+
insertMutableTrie(
|
|
486
|
+
root,
|
|
487
|
+
stringToBitPath(k),
|
|
488
|
+
fromJsonValue(value[k])
|
|
489
|
+
);
|
|
490
|
+
}
|
|
491
|
+
return Value.variant("object", mutableNodeToK(root));
|
|
204
492
|
}
|
|
205
493
|
throw new Error(`Unsupported JSON value: ${value}`);
|
|
206
494
|
}
|
|
207
495
|
|
|
208
496
|
export function toJsonValue(value) {
|
|
209
497
|
if (!isVariant(value)) {
|
|
210
|
-
throw new Error("djson
|
|
498
|
+
throw new Error("djson: expected top-level Variant");
|
|
211
499
|
}
|
|
212
|
-
|
|
213
500
|
if (value.tag === "null") {
|
|
214
501
|
requireProduct(value.value, "djson.null");
|
|
215
502
|
return null;
|
|
216
503
|
}
|
|
217
|
-
|
|
218
504
|
if (value.tag === "bool") {
|
|
219
505
|
if (!isVariant(value.value)) {
|
|
220
506
|
throw new Error("djson.bool: expected variant");
|
|
221
507
|
}
|
|
222
|
-
if (value.value.tag === "true")
|
|
223
|
-
|
|
224
|
-
return true;
|
|
225
|
-
}
|
|
226
|
-
if (value.value.tag === "false") {
|
|
227
|
-
requireProduct(value.value.value, "djson.bool.false");
|
|
228
|
-
return false;
|
|
229
|
-
}
|
|
508
|
+
if (value.value.tag === "true") return true;
|
|
509
|
+
if (value.value.tag === "false") return false;
|
|
230
510
|
throw new Error(`djson.bool: invalid tag ${value.value.tag}`);
|
|
231
511
|
}
|
|
232
|
-
|
|
233
512
|
if (value.tag === "number") {
|
|
234
513
|
return decodeValueToNumber(value.value);
|
|
235
514
|
}
|
|
236
|
-
|
|
237
515
|
if (value.tag === "string") {
|
|
238
516
|
return stringValueToText(value.value);
|
|
239
517
|
}
|
|
240
|
-
|
|
241
518
|
if (value.tag === "list") {
|
|
242
|
-
const
|
|
243
|
-
|
|
519
|
+
const entries = collectTrieEntries(value.value);
|
|
520
|
+
entries.sort((a, b) =>
|
|
521
|
+
bitPathToIndex(a.bits) - bitPathToIndex(b.bits)
|
|
522
|
+
);
|
|
523
|
+
return entries.map((e) => toJsonValue(e.val));
|
|
244
524
|
}
|
|
245
|
-
|
|
246
525
|
if (value.tag === "object") {
|
|
247
|
-
const
|
|
526
|
+
const entries = collectTrieEntries(value.value);
|
|
248
527
|
const result = {};
|
|
249
|
-
for (const
|
|
250
|
-
|
|
251
|
-
const key = stringValueToText(pair.key);
|
|
252
|
-
result[key] = toJsonValue(pair.val);
|
|
528
|
+
for (const e of entries) {
|
|
529
|
+
result[bitPathToString(e.bits)] = toJsonValue(e.val);
|
|
253
530
|
}
|
|
254
531
|
return result;
|
|
255
532
|
}
|
|
256
|
-
|
|
257
|
-
throw new Error(`Unsupported djson tag: ${value.tag}`);
|
|
533
|
+
throw new Error(`djson: unsupported tag ${value.tag}`);
|
|
258
534
|
}
|
|
259
535
|
|
|
260
|
-
export
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
536
|
+
export const DJSON_BTRIE_PATTERN = DJSON_PATTERN;
|
|
537
|
+
export const pattern = DJSON_PATTERN;
|
|
538
|
+
|
|
539
|
+
export function parse(rawText) {
|
|
540
|
+
const text = String(rawText).trim();
|
|
541
|
+
if (!text) throw new Error("Empty JSON input");
|
|
542
|
+
const parsed = JSON.parse(text);
|
|
543
|
+
const val = fromJsonValue(parsed);
|
|
544
|
+
return withPattern(val, DJSON_PATTERN);
|
|
264
545
|
}
|
|
265
546
|
|
|
266
547
|
export function print(value) {
|
|
267
548
|
return JSON.stringify(toJsonValue(value));
|
|
268
549
|
}
|
|
269
550
|
|
|
551
|
+
export const codec = {
|
|
552
|
+
name: "djson",
|
|
553
|
+
doc,
|
|
554
|
+
pattern: DJSON_PATTERN,
|
|
555
|
+
parse,
|
|
556
|
+
print
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
export default codec;
|
|
560
|
+
|
|
270
561
|
runCodecCLI(import.meta.url, { parse, print, doc });
|