@fraczak/k 7.1.0 → 7.2.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 +33 -0
- package/DOCS/CODECS.md +37 -2
- package/DOCS/CODEC_ARCHITECTURE.md +12 -5
- package/DOCS/REPL.md +11 -3
- package/DOCS/WEB_REPL_REQUIREMENTS.md +1 -1
- package/README.md +2 -1
- package/Value.mjs +28 -3
- package/backend-api.mjs +5 -1
- package/browser/tree-view.mjs +47 -8
- package/browser/vfs-data.mjs +6 -5
- package/codecs/README.md +1 -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/package.json +2 -1
- package/repl.mjs +57 -6
- package/scripts/build-repl-html.mjs +8 -1
- package/tests/test-codecs.mjs +168 -0
- package/tests/test-repl-html.mjs +1 -0
- package/tests/test-repl.mjs +33 -0
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
import { Value, withPattern, isProduct, isVariant } from "../../Value.mjs";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import {
|
|
3
|
+
refinePatternForValue,
|
|
4
|
+
coerceValueForPattern,
|
|
5
|
+
canonicalizePattern,
|
|
6
|
+
NODE_KIND
|
|
7
|
+
} from "./codec.mjs";
|
|
8
|
+
import {
|
|
9
|
+
patternToPropertyList,
|
|
10
|
+
propertyListToPattern
|
|
11
|
+
} from "./pattern-json.mjs";
|
|
12
|
+
import {
|
|
13
|
+
textToStringValue,
|
|
14
|
+
stringValueToText,
|
|
15
|
+
unicodeValueToCodePoint,
|
|
16
|
+
setLazyStringDecoder
|
|
17
|
+
} from "./unicode-string.mjs";
|
|
5
18
|
|
|
6
19
|
const CORE_PATTERN_PROPERTY_LIST = [
|
|
7
20
|
["closed-union", [["cons", 1], ["nil", 3]]],
|
|
@@ -34,19 +47,37 @@ const CORE_PATTERN_PROPERTY_LIST = [
|
|
|
34
47
|
|
|
35
48
|
const CORE_PATTERN_PATTERN = propertyListToPattern(CORE_PATTERN_PROPERTY_LIST);
|
|
36
49
|
const UNIT = Value.product({});
|
|
50
|
+
const BIT0 = Value.variant("0", UNIT);
|
|
51
|
+
const BIT1 = Value.variant("1", UNIT);
|
|
37
52
|
|
|
38
53
|
class BitWriter {
|
|
39
|
-
constructor() {
|
|
40
|
-
this.
|
|
54
|
+
constructor(initialCapacity = 4096) {
|
|
55
|
+
this.buffer = new Uint8Array(initialCapacity);
|
|
56
|
+
this.byteLength = 0;
|
|
41
57
|
this.current = 0;
|
|
42
58
|
this.bitCount = 0;
|
|
43
59
|
}
|
|
44
60
|
|
|
61
|
+
ensureCapacity(extraBytes) {
|
|
62
|
+
if (this.byteLength + extraBytes > this.buffer.length) {
|
|
63
|
+
const newCap = Math.max(
|
|
64
|
+
this.buffer.length * 2,
|
|
65
|
+
this.byteLength + extraBytes + 1024
|
|
66
|
+
);
|
|
67
|
+
const newBuf = new Uint8Array(newCap);
|
|
68
|
+
newBuf.set(this.buffer.subarray(0, this.byteLength));
|
|
69
|
+
this.buffer = newBuf;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
45
73
|
writeBit(bit) {
|
|
46
74
|
this.current = (this.current << 1) | (bit ? 1 : 0);
|
|
47
75
|
this.bitCount += 1;
|
|
48
76
|
if (this.bitCount === 8) {
|
|
49
|
-
this.
|
|
77
|
+
if (this.byteLength >= this.buffer.length) {
|
|
78
|
+
this.ensureCapacity(1);
|
|
79
|
+
}
|
|
80
|
+
this.buffer[this.byteLength++] = this.current;
|
|
50
81
|
this.current = 0;
|
|
51
82
|
this.bitCount = 0;
|
|
52
83
|
}
|
|
@@ -67,9 +98,19 @@ class BitWriter {
|
|
|
67
98
|
|
|
68
99
|
toBuffer() {
|
|
69
100
|
if (this.bitCount > 0) {
|
|
70
|
-
|
|
101
|
+
if (this.byteLength >= this.buffer.length) {
|
|
102
|
+
this.ensureCapacity(1);
|
|
103
|
+
}
|
|
104
|
+
this.buffer[this.byteLength++] =
|
|
105
|
+
this.current << (8 - this.bitCount);
|
|
106
|
+
this.current = 0;
|
|
107
|
+
this.bitCount = 0;
|
|
71
108
|
}
|
|
72
|
-
return Buffer.from(
|
|
109
|
+
return Buffer.from(
|
|
110
|
+
this.buffer.buffer,
|
|
111
|
+
this.buffer.byteOffset,
|
|
112
|
+
this.byteLength
|
|
113
|
+
);
|
|
73
114
|
}
|
|
74
115
|
}
|
|
75
116
|
|
|
@@ -95,6 +136,8 @@ class BitReader {
|
|
|
95
136
|
}
|
|
96
137
|
|
|
97
138
|
readBits(width) {
|
|
139
|
+
if (width === 0) return 0;
|
|
140
|
+
if (width === 1) return this.readBit();
|
|
98
141
|
let value = 0;
|
|
99
142
|
for (let i = 0; i < width; i++) {
|
|
100
143
|
value = (value << 1) | this.readBit();
|
|
@@ -136,16 +179,32 @@ function encodeNode(writer, value, pattern, patternNodeId) {
|
|
|
136
179
|
case NODE_KIND.OPEN_PRODUCT:
|
|
137
180
|
case NODE_KIND.CLOSED_PRODUCT: {
|
|
138
181
|
if (!isProduct(frame.value)) {
|
|
139
|
-
throw new Error(
|
|
182
|
+
throw new Error(
|
|
183
|
+
`Expected Product for pattern node ${frame.patternNodeId}`
|
|
184
|
+
);
|
|
140
185
|
}
|
|
141
|
-
const
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
186
|
+
const p = frame.value.product;
|
|
187
|
+
const edges = patternNode.edges;
|
|
188
|
+
const edgeCount = edges.length;
|
|
189
|
+
if (edgeCount === 0) {
|
|
190
|
+
for (const _ in p) {
|
|
191
|
+
throw new Error(
|
|
192
|
+
`Product fields do not match pattern node ` +
|
|
193
|
+
`${frame.patternNodeId}`
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
break;
|
|
145
197
|
}
|
|
146
|
-
for (let i =
|
|
147
|
-
const edge =
|
|
148
|
-
|
|
198
|
+
for (let i = edgeCount - 1; i >= 0; i--) {
|
|
199
|
+
const edge = edges[i];
|
|
200
|
+
const childVal = p[edge.label];
|
|
201
|
+
if (childVal === undefined) {
|
|
202
|
+
throw new Error(
|
|
203
|
+
`Product fields do not match pattern node ` +
|
|
204
|
+
`${frame.patternNodeId}`
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
stack.push({ value: childVal, patternNodeId: edge.target });
|
|
149
208
|
}
|
|
150
209
|
break;
|
|
151
210
|
}
|
|
@@ -153,15 +212,34 @@ function encodeNode(writer, value, pattern, patternNodeId) {
|
|
|
153
212
|
case NODE_KIND.OPEN_UNION:
|
|
154
213
|
case NODE_KIND.CLOSED_UNION: {
|
|
155
214
|
if (!isVariant(frame.value)) {
|
|
156
|
-
throw new Error(
|
|
215
|
+
throw new Error(
|
|
216
|
+
`Expected Variant for pattern node ${frame.patternNodeId}`
|
|
217
|
+
);
|
|
157
218
|
}
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
219
|
+
const tag = frame.value.tag;
|
|
220
|
+
const edges = patternNode.edges;
|
|
221
|
+
let tagOrdinal = patternNode.tagIndex !== undefined
|
|
222
|
+
? patternNode.tagIndex[tag]
|
|
223
|
+
: undefined;
|
|
224
|
+
if (tagOrdinal === undefined) {
|
|
225
|
+
tagOrdinal = edges.findIndex((edge) => edge.label === tag);
|
|
226
|
+
if (tagOrdinal === -1) {
|
|
227
|
+
throw new Error(
|
|
228
|
+
`Variant tag '${tag}' is not present in pattern node ` +
|
|
229
|
+
`${frame.patternNodeId}`
|
|
230
|
+
);
|
|
231
|
+
}
|
|
161
232
|
}
|
|
162
|
-
const width =
|
|
163
|
-
|
|
164
|
-
|
|
233
|
+
const width = patternNode.choiceBits !== undefined
|
|
234
|
+
? patternNode.choiceBits
|
|
235
|
+
: choiceWidth(edges.length);
|
|
236
|
+
if (width > 0) {
|
|
237
|
+
writer.writeBits(tagOrdinal, width);
|
|
238
|
+
}
|
|
239
|
+
stack.push({
|
|
240
|
+
value: frame.value.value,
|
|
241
|
+
patternNodeId: edges[tagOrdinal].target
|
|
242
|
+
});
|
|
165
243
|
break;
|
|
166
244
|
}
|
|
167
245
|
|
|
@@ -171,20 +249,15 @@ function encodeNode(writer, value, pattern, patternNodeId) {
|
|
|
171
249
|
}
|
|
172
250
|
}
|
|
173
251
|
|
|
174
|
-
function decodeNode(reader, pattern,
|
|
252
|
+
function decodeNode(reader, pattern, rootPatternNodeId) {
|
|
175
253
|
let result;
|
|
176
|
-
const stack = [
|
|
177
|
-
patternNodeId,
|
|
178
|
-
assign(value) {
|
|
179
|
-
result = value;
|
|
180
|
-
}
|
|
181
|
-
}];
|
|
254
|
+
const stack = [[rootPatternNodeId, null, null]];
|
|
182
255
|
|
|
183
256
|
while (stack.length > 0) {
|
|
184
|
-
const
|
|
185
|
-
const patternNode = pattern.nodes[
|
|
257
|
+
const [patternNodeId, parent, key] = stack.pop();
|
|
258
|
+
const patternNode = pattern.nodes[patternNodeId];
|
|
186
259
|
if (!patternNode) {
|
|
187
|
-
throw new Error(`Unknown pattern node ${
|
|
260
|
+
throw new Error(`Unknown pattern node ${patternNodeId}`);
|
|
188
261
|
}
|
|
189
262
|
|
|
190
263
|
switch (patternNode.kind) {
|
|
@@ -194,38 +267,46 @@ function decodeNode(reader, pattern, patternNodeId) {
|
|
|
194
267
|
case NODE_KIND.OPEN_PRODUCT:
|
|
195
268
|
case NODE_KIND.CLOSED_PRODUCT: {
|
|
196
269
|
const product = {};
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
270
|
+
const val = Value.product(product);
|
|
271
|
+
if (parent) {
|
|
272
|
+
parent[key] = val;
|
|
273
|
+
} else {
|
|
274
|
+
result = val;
|
|
275
|
+
}
|
|
276
|
+
const edges = patternNode.edges;
|
|
277
|
+
for (let i = edges.length - 1; i >= 0; i--) {
|
|
278
|
+
const edge = edges[i];
|
|
279
|
+
stack.push([edge.target, product, edge.label]);
|
|
206
280
|
}
|
|
207
281
|
break;
|
|
208
282
|
}
|
|
209
283
|
|
|
210
284
|
case NODE_KIND.OPEN_UNION:
|
|
211
285
|
case NODE_KIND.CLOSED_UNION: {
|
|
212
|
-
|
|
213
|
-
|
|
286
|
+
const edges = patternNode.edges;
|
|
287
|
+
if (edges.length === 0) {
|
|
288
|
+
throw new Error(
|
|
289
|
+
`Union pattern node ${patternNodeId} has no tags`
|
|
290
|
+
);
|
|
214
291
|
}
|
|
215
|
-
const width =
|
|
292
|
+
const width = patternNode.choiceBits !== undefined
|
|
293
|
+
? patternNode.choiceBits
|
|
294
|
+
: choiceWidth(edges.length);
|
|
216
295
|
const tagOrdinal = reader.readBits(width);
|
|
217
|
-
if (tagOrdinal >=
|
|
218
|
-
throw new Error(
|
|
296
|
+
if (tagOrdinal >= edges.length) {
|
|
297
|
+
throw new Error(
|
|
298
|
+
`Choice ${tagOrdinal} is out of range for pattern node ` +
|
|
299
|
+
`${patternNodeId}`
|
|
300
|
+
);
|
|
219
301
|
}
|
|
220
|
-
const edge =
|
|
302
|
+
const edge = edges[tagOrdinal];
|
|
221
303
|
const variant = Value.variant(edge.label, undefined);
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
});
|
|
304
|
+
if (parent) {
|
|
305
|
+
parent[key] = variant;
|
|
306
|
+
} else {
|
|
307
|
+
result = variant;
|
|
308
|
+
}
|
|
309
|
+
stack.push([edge.target, variant, "value"]);
|
|
229
310
|
break;
|
|
230
311
|
}
|
|
231
312
|
|
|
@@ -237,23 +318,57 @@ function decodeNode(reader, pattern, patternNodeId) {
|
|
|
237
318
|
return result;
|
|
238
319
|
}
|
|
239
320
|
|
|
321
|
+
function isClosedPropertyList(propertyList) {
|
|
322
|
+
if (!Array.isArray(propertyList) || propertyList.length === 0) {
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
for (let i = 0; i < propertyList.length; i++) {
|
|
326
|
+
const kind = propertyList[i]?.[0];
|
|
327
|
+
if (kind !== "closed-product" && kind !== "closed-union") {
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return true;
|
|
332
|
+
}
|
|
333
|
+
|
|
240
334
|
function preparePatternAndValue(value, propertyList) {
|
|
241
335
|
const valuePattern = propertyList || value.pattern;
|
|
242
|
-
|
|
243
|
-
|
|
336
|
+
if (valuePattern && isClosedPropertyList(valuePattern)) {
|
|
337
|
+
const basePattern = propertyListToPattern(valuePattern);
|
|
338
|
+
const canonical = canonicalizePattern(basePattern);
|
|
339
|
+
return {
|
|
340
|
+
coercedValue: value,
|
|
341
|
+
refinedPattern: canonical,
|
|
342
|
+
propertyList: patternToPropertyList(canonical)
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
const basePattern = valuePattern
|
|
346
|
+
? propertyListToPattern(valuePattern)
|
|
347
|
+
: deriveClosedPatternFromValue(value);
|
|
348
|
+
const coercedValue = valuePattern
|
|
349
|
+
? coerceValueForPattern(basePattern, value)
|
|
350
|
+
: value;
|
|
244
351
|
const refinedPattern = refinePatternForValue(basePattern, coercedValue);
|
|
245
|
-
return {
|
|
352
|
+
return {
|
|
353
|
+
coercedValue,
|
|
354
|
+
refinedPattern,
|
|
355
|
+
propertyList: patternToPropertyList(refinedPattern)
|
|
356
|
+
};
|
|
246
357
|
}
|
|
247
358
|
|
|
248
359
|
function deriveClosedPatternFromValue(value) {
|
|
249
360
|
const nodes = [];
|
|
361
|
+
const labelSet = new Set();
|
|
250
362
|
|
|
251
363
|
function visit(node) {
|
|
252
364
|
const nodeId = nodes.length;
|
|
253
365
|
if (isProduct(node)) {
|
|
254
366
|
const placeholder = { kind: NODE_KIND.CLOSED_PRODUCT, edges: [] };
|
|
255
367
|
nodes.push(placeholder);
|
|
256
|
-
const labels = Object.keys(node.product).sort((a, b) =>
|
|
368
|
+
const labels = Object.keys(node.product).sort((a, b) =>
|
|
369
|
+
Buffer.compare(Buffer.from(a, "utf8"), Buffer.from(b, "utf8"))
|
|
370
|
+
);
|
|
371
|
+
for (let i = 0; i < labels.length; i++) labelSet.add(labels[i]);
|
|
257
372
|
placeholder.edges = labels.map((label) => ({
|
|
258
373
|
label,
|
|
259
374
|
symbolId: 0,
|
|
@@ -264,6 +379,7 @@ function deriveClosedPatternFromValue(value) {
|
|
|
264
379
|
if (isVariant(node)) {
|
|
265
380
|
const placeholder = { kind: NODE_KIND.OPEN_UNION, edges: [] };
|
|
266
381
|
nodes.push(placeholder);
|
|
382
|
+
labelSet.add(node.tag);
|
|
267
383
|
placeholder.edges = [{
|
|
268
384
|
label: node.tag,
|
|
269
385
|
symbolId: 0,
|
|
@@ -271,12 +387,17 @@ function deriveClosedPatternFromValue(value) {
|
|
|
271
387
|
}];
|
|
272
388
|
return nodeId;
|
|
273
389
|
}
|
|
274
|
-
throw new Error(
|
|
390
|
+
throw new Error(
|
|
391
|
+
`Unsupported runtime value node: ${
|
|
392
|
+
node?.constructor?.name || typeof node
|
|
393
|
+
}`
|
|
394
|
+
);
|
|
275
395
|
}
|
|
276
396
|
|
|
277
397
|
visit(value);
|
|
278
|
-
const dictionary =
|
|
279
|
-
|
|
398
|
+
const dictionary = Array.from(labelSet).sort((a, b) =>
|
|
399
|
+
Buffer.compare(Buffer.from(a, "utf8"), Buffer.from(b, "utf8"))
|
|
400
|
+
);
|
|
280
401
|
const symbolIds = new Map(dictionary.map((label, index) => [label, index]));
|
|
281
402
|
|
|
282
403
|
return {
|
|
@@ -429,21 +550,458 @@ function encodeToWire(value, propertyList) {
|
|
|
429
550
|
return writer.toBuffer();
|
|
430
551
|
}
|
|
431
552
|
|
|
432
|
-
function
|
|
553
|
+
function readBitsAt(buffer, bitOffset, width) {
|
|
554
|
+
if (width === 0) return 0;
|
|
555
|
+
if ((bitOffset + width - 1) >> 3 >= buffer.length) {
|
|
556
|
+
throw new Error("Unexpected end of bitstream");
|
|
557
|
+
}
|
|
558
|
+
if (width === 1) {
|
|
559
|
+
return (buffer[bitOffset >> 3] >> (7 - (bitOffset & 7))) & 1;
|
|
560
|
+
}
|
|
561
|
+
let v = 0;
|
|
562
|
+
for (let i = 0; i < width; i++) {
|
|
563
|
+
const pos = bitOffset + i;
|
|
564
|
+
v = (v << 1) | ((buffer[pos >> 3] >> (7 - (pos & 7))) & 1);
|
|
565
|
+
}
|
|
566
|
+
return v;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
const SKIP_STACK = new Int32Array(2048);
|
|
570
|
+
|
|
571
|
+
function fastSkipSubtree(
|
|
572
|
+
buffer,
|
|
573
|
+
startBitOffset,
|
|
574
|
+
pattern,
|
|
575
|
+
rootPatternNodeId
|
|
576
|
+
) {
|
|
577
|
+
let bitOffset = startBitOffset;
|
|
578
|
+
let stack = SKIP_STACK;
|
|
579
|
+
stack[0] = rootPatternNodeId;
|
|
580
|
+
let stackPtr = 1;
|
|
581
|
+
const nodes = pattern.nodes;
|
|
582
|
+
|
|
583
|
+
while (stackPtr > 0) {
|
|
584
|
+
const nodeId = stack[--stackPtr];
|
|
585
|
+
const node = nodes[nodeId];
|
|
586
|
+
if (node.fixedBitWidth !== undefined) {
|
|
587
|
+
bitOffset += node.fixedBitWidth;
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
590
|
+
if (
|
|
591
|
+
node.kind === NODE_KIND.CLOSED_PRODUCT ||
|
|
592
|
+
node.kind === NODE_KIND.OPEN_PRODUCT
|
|
593
|
+
) {
|
|
594
|
+
const edges = node.edges;
|
|
595
|
+
const count = edges.length;
|
|
596
|
+
if (stackPtr + count > stack.length) {
|
|
597
|
+
const newStack = new Int32Array(stack.length * 2);
|
|
598
|
+
newStack.set(stack);
|
|
599
|
+
stack = newStack;
|
|
600
|
+
}
|
|
601
|
+
for (let i = count - 1; i >= 0; i--) {
|
|
602
|
+
stack[stackPtr++] = edges[i].target;
|
|
603
|
+
}
|
|
604
|
+
} else if (
|
|
605
|
+
node.kind === NODE_KIND.CLOSED_UNION ||
|
|
606
|
+
node.kind === NODE_KIND.OPEN_UNION
|
|
607
|
+
) {
|
|
608
|
+
const edges = node.edges;
|
|
609
|
+
const width = node.choiceBits !== undefined
|
|
610
|
+
? node.choiceBits
|
|
611
|
+
: choiceWidth(edges.length);
|
|
612
|
+
const tagOrdinal = width > 0
|
|
613
|
+
? readBitsAt(buffer, bitOffset, width)
|
|
614
|
+
: 0;
|
|
615
|
+
bitOffset += width;
|
|
616
|
+
if (tagOrdinal >= edges.length) {
|
|
617
|
+
throw new Error(
|
|
618
|
+
`Choice ${tagOrdinal} is out of range for pattern ` +
|
|
619
|
+
`node ${nodeId}`
|
|
620
|
+
);
|
|
621
|
+
}
|
|
622
|
+
if (stackPtr >= stack.length) {
|
|
623
|
+
const newStack = new Int32Array(stack.length * 2);
|
|
624
|
+
newStack.set(stack);
|
|
625
|
+
stack = newStack;
|
|
626
|
+
}
|
|
627
|
+
stack[stackPtr++] = edges[tagOrdinal].target;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
return bitOffset;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
class LazyVariant extends Value {
|
|
634
|
+
constructor(tag, buffer, bitOffset, pattern, targetId) {
|
|
635
|
+
super();
|
|
636
|
+
this.type = "<>";
|
|
637
|
+
this.tag = tag;
|
|
638
|
+
this.pattern = null;
|
|
639
|
+
this._buffer = buffer;
|
|
640
|
+
this._bitOffset = bitOffset;
|
|
641
|
+
this._pattern = pattern;
|
|
642
|
+
this._targetId = targetId;
|
|
643
|
+
Object.defineProperty(this, "value", {
|
|
644
|
+
get: this._resolveValue,
|
|
645
|
+
set: this._setValue,
|
|
646
|
+
enumerable: true,
|
|
647
|
+
configurable: true
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
_resolveValue() {
|
|
652
|
+
const val = decodeLazyNode(
|
|
653
|
+
this._buffer,
|
|
654
|
+
this._bitOffset,
|
|
655
|
+
this._pattern,
|
|
656
|
+
this._targetId
|
|
657
|
+
);
|
|
658
|
+
Object.defineProperty(this, "value", {
|
|
659
|
+
value: val,
|
|
660
|
+
writable: true,
|
|
661
|
+
enumerable: true,
|
|
662
|
+
configurable: true
|
|
663
|
+
});
|
|
664
|
+
return val;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
_setValue(v) {
|
|
668
|
+
this._buffer = null;
|
|
669
|
+
Object.defineProperty(this, "value", {
|
|
670
|
+
value: v,
|
|
671
|
+
writable: true,
|
|
672
|
+
enumerable: true,
|
|
673
|
+
configurable: true
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
function createLazyVariant(buffer, bitOffset, pattern, patternNodeId) {
|
|
679
|
+
const node = pattern.nodes[patternNodeId];
|
|
680
|
+
const edges = node.edges;
|
|
681
|
+
if (edges.length === 0) {
|
|
682
|
+
throw new Error(`Union pattern node ${patternNodeId} has no tags`);
|
|
683
|
+
}
|
|
684
|
+
const width = node.choiceBits !== undefined
|
|
685
|
+
? node.choiceBits
|
|
686
|
+
: choiceWidth(edges.length);
|
|
687
|
+
const tagOrdinal = width > 0 ? readBitsAt(buffer, bitOffset, width) : 0;
|
|
688
|
+
if (tagOrdinal >= edges.length) {
|
|
689
|
+
throw new Error(
|
|
690
|
+
`Choice ${tagOrdinal} is out of range for pattern ` +
|
|
691
|
+
`node ${patternNodeId}`
|
|
692
|
+
);
|
|
693
|
+
}
|
|
694
|
+
const edge = edges[tagOrdinal];
|
|
695
|
+
|
|
696
|
+
// Fast-path: terminal variant whose target is empty product {}
|
|
697
|
+
const targetNode = pattern.nodes[edge.target];
|
|
698
|
+
if (
|
|
699
|
+
targetNode &&
|
|
700
|
+
(targetNode.kind === NODE_KIND.CLOSED_PRODUCT ||
|
|
701
|
+
targetNode.kind === NODE_KIND.OPEN_PRODUCT) &&
|
|
702
|
+
targetNode.edges.length === 0
|
|
703
|
+
) {
|
|
704
|
+
if (edge.label === "0") return BIT0;
|
|
705
|
+
if (edge.label === "1") return BIT1;
|
|
706
|
+
return Value.variant(edge.label, UNIT);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
const variant = new LazyVariant(
|
|
710
|
+
edge.label,
|
|
711
|
+
buffer,
|
|
712
|
+
bitOffset + width,
|
|
713
|
+
pattern,
|
|
714
|
+
edge.target
|
|
715
|
+
);
|
|
716
|
+
if (edge.fixedTargetBitWidth !== undefined) {
|
|
717
|
+
variant.bitWidth = width + edge.fixedTargetBitWidth;
|
|
718
|
+
}
|
|
719
|
+
return variant;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
class LazyConsProduct extends Value {
|
|
723
|
+
constructor(buffer, bitOffset, pattern, carTarget, cdrTarget) {
|
|
724
|
+
super();
|
|
725
|
+
this.type = "{}";
|
|
726
|
+
this.pattern = null;
|
|
727
|
+
this.buffer = buffer;
|
|
728
|
+
this.bitOffset = bitOffset;
|
|
729
|
+
this.pattern = pattern;
|
|
730
|
+
this.carTarget = carTarget;
|
|
731
|
+
this.cdrTarget = cdrTarget;
|
|
732
|
+
let carVal = undefined;
|
|
733
|
+
let cdrVal = undefined;
|
|
734
|
+
let cdrOffset = -1;
|
|
735
|
+
|
|
736
|
+
this.product = {
|
|
737
|
+
get car() {
|
|
738
|
+
if (carVal === undefined) {
|
|
739
|
+
carVal = decodeLazyNode(buffer, bitOffset, pattern, carTarget);
|
|
740
|
+
}
|
|
741
|
+
return carVal;
|
|
742
|
+
},
|
|
743
|
+
set car(v) {
|
|
744
|
+
carVal = v;
|
|
745
|
+
},
|
|
746
|
+
get cdr() {
|
|
747
|
+
if (cdrVal === undefined) {
|
|
748
|
+
if (cdrOffset === -1) {
|
|
749
|
+
if (carVal?.bitWidth !== undefined) {
|
|
750
|
+
cdrOffset = bitOffset + carVal.bitWidth;
|
|
751
|
+
} else if (pattern.nodes[carTarget].fixedBitWidth !== undefined) {
|
|
752
|
+
cdrOffset = bitOffset + pattern.nodes[carTarget].fixedBitWidth;
|
|
753
|
+
} else {
|
|
754
|
+
cdrOffset = fastSkipSubtree(
|
|
755
|
+
buffer,
|
|
756
|
+
bitOffset,
|
|
757
|
+
pattern,
|
|
758
|
+
carTarget
|
|
759
|
+
);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
cdrVal = decodeLazyNode(buffer, cdrOffset, pattern, cdrTarget);
|
|
763
|
+
}
|
|
764
|
+
return cdrVal;
|
|
765
|
+
},
|
|
766
|
+
set cdr(v) {
|
|
767
|
+
cdrVal = v;
|
|
768
|
+
}
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
class LazyGeneralProduct extends Value {
|
|
774
|
+
constructor(buffer, bitOffset, pattern, patternNodeId) {
|
|
775
|
+
super();
|
|
776
|
+
this.type = "{}";
|
|
777
|
+
this.pattern = null;
|
|
778
|
+
const node = pattern.nodes[patternNodeId];
|
|
779
|
+
const edges = node.edges;
|
|
780
|
+
const edgeCount = edges.length;
|
|
781
|
+
const product = {};
|
|
782
|
+
this.product = product;
|
|
783
|
+
|
|
784
|
+
if (node.staticOffsets !== undefined) {
|
|
785
|
+
const staticOffsets = node.staticOffsets;
|
|
786
|
+
for (let i = 0; i < edgeCount; i++) {
|
|
787
|
+
const edge = edges[i];
|
|
788
|
+
const off = bitOffset + staticOffsets[i];
|
|
789
|
+
let cachedVal = undefined;
|
|
790
|
+
let resolved = false;
|
|
791
|
+
|
|
792
|
+
Object.defineProperty(product, edge.label, {
|
|
793
|
+
get() {
|
|
794
|
+
if (!resolved) {
|
|
795
|
+
cachedVal = decodeLazyNode(buffer, off, pattern, edge.target);
|
|
796
|
+
resolved = true;
|
|
797
|
+
}
|
|
798
|
+
return cachedVal;
|
|
799
|
+
},
|
|
800
|
+
set(v) {
|
|
801
|
+
cachedVal = v;
|
|
802
|
+
resolved = true;
|
|
803
|
+
},
|
|
804
|
+
enumerable: true,
|
|
805
|
+
configurable: true
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
const edgeOffsets = new Array(edgeCount);
|
|
812
|
+
edgeOffsets[0] = bitOffset;
|
|
813
|
+
let nextKnownOffsetIndex = 1;
|
|
814
|
+
|
|
815
|
+
function ensureOffset(targetIndex) {
|
|
816
|
+
while (nextKnownOffsetIndex <= targetIndex) {
|
|
817
|
+
const prevIdx = nextKnownOffsetIndex - 1;
|
|
818
|
+
const prevStart = edgeOffsets[prevIdx];
|
|
819
|
+
const prevTargetId = edges[prevIdx].target;
|
|
820
|
+
edgeOffsets[nextKnownOffsetIndex] = fastSkipSubtree(
|
|
821
|
+
buffer,
|
|
822
|
+
prevStart,
|
|
823
|
+
pattern,
|
|
824
|
+
prevTargetId
|
|
825
|
+
);
|
|
826
|
+
nextKnownOffsetIndex++;
|
|
827
|
+
}
|
|
828
|
+
return edgeOffsets[targetIndex];
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
for (let i = 0; i < edgeCount; i++) {
|
|
832
|
+
const edge = edges[i];
|
|
833
|
+
const edgeIndex = i;
|
|
834
|
+
let cachedVal = undefined;
|
|
835
|
+
let resolved = false;
|
|
836
|
+
|
|
837
|
+
Object.defineProperty(product, edge.label, {
|
|
838
|
+
get() {
|
|
839
|
+
if (!resolved) {
|
|
840
|
+
const start = ensureOffset(edgeIndex);
|
|
841
|
+
cachedVal = decodeLazyNode(
|
|
842
|
+
buffer,
|
|
843
|
+
start,
|
|
844
|
+
pattern,
|
|
845
|
+
edge.target
|
|
846
|
+
);
|
|
847
|
+
resolved = true;
|
|
848
|
+
}
|
|
849
|
+
return cachedVal;
|
|
850
|
+
},
|
|
851
|
+
set(v) {
|
|
852
|
+
cachedVal = v;
|
|
853
|
+
resolved = true;
|
|
854
|
+
},
|
|
855
|
+
enumerable: true,
|
|
856
|
+
configurable: true
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
function decodeLazyNode(buffer, bitOffset, pattern, patternNodeId) {
|
|
863
|
+
const node = pattern.nodes[patternNodeId];
|
|
864
|
+
if (!node) {
|
|
865
|
+
throw new Error(`Unknown pattern node ${patternNodeId}`);
|
|
866
|
+
}
|
|
867
|
+
if (
|
|
868
|
+
node.kind === NODE_KIND.CLOSED_PRODUCT ||
|
|
869
|
+
node.kind === NODE_KIND.OPEN_PRODUCT
|
|
870
|
+
) {
|
|
871
|
+
if (node.edges.length === 0) return UNIT;
|
|
872
|
+
if (
|
|
873
|
+
node.edges.length === 2 &&
|
|
874
|
+
node.edges[0].label === "car" &&
|
|
875
|
+
node.edges[1].label === "cdr"
|
|
876
|
+
) {
|
|
877
|
+
return new LazyConsProduct(
|
|
878
|
+
buffer,
|
|
879
|
+
bitOffset,
|
|
880
|
+
pattern,
|
|
881
|
+
node.edges[0].target,
|
|
882
|
+
node.edges[1].target
|
|
883
|
+
);
|
|
884
|
+
}
|
|
885
|
+
return new LazyGeneralProduct(
|
|
886
|
+
buffer,
|
|
887
|
+
bitOffset,
|
|
888
|
+
pattern,
|
|
889
|
+
patternNodeId
|
|
890
|
+
);
|
|
891
|
+
}
|
|
892
|
+
return createLazyVariant(buffer, bitOffset, pattern, patternNodeId);
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
function decodeWire(buffer, options = {}) {
|
|
433
896
|
if (!Buffer.isBuffer(buffer)) {
|
|
434
897
|
throw new Error("Wire input must be a Buffer");
|
|
435
898
|
}
|
|
436
899
|
const reader = new BitReader(buffer);
|
|
437
|
-
const patternPropertyList = patternFromKValue(
|
|
900
|
+
const patternPropertyList = patternFromKValue(
|
|
901
|
+
decodeNode(reader, CORE_PATTERN_PATTERN, 0)
|
|
902
|
+
);
|
|
438
903
|
const pattern = propertyListToPattern(patternPropertyList);
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
904
|
+
|
|
905
|
+
if (options.eager) {
|
|
906
|
+
const value = decodeNode(reader, pattern, 0);
|
|
907
|
+
reader.assertZeroPadding();
|
|
908
|
+
return {
|
|
909
|
+
pattern: patternPropertyList,
|
|
910
|
+
value: withPattern(value, patternPropertyList)
|
|
911
|
+
};
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
const valBitOffset = reader.byteOffset * 8 + reader.bitOffset;
|
|
915
|
+
const lazyRoot = decodeLazyNode(buffer, valBitOffset, pattern, 0);
|
|
916
|
+
lazyRoot.pattern = patternPropertyList;
|
|
917
|
+
return {
|
|
918
|
+
pattern: patternPropertyList,
|
|
919
|
+
value: withPattern(lazyRoot, patternPropertyList)
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
function decodeWireLazy(buffer) {
|
|
924
|
+
return decodeWire(buffer, { eager: false });
|
|
442
925
|
}
|
|
443
926
|
|
|
927
|
+
function isLazyCanonicalString(val) {
|
|
928
|
+
if (!val || typeof val !== "object" || !val._buffer || !val._pattern) {
|
|
929
|
+
return false;
|
|
930
|
+
}
|
|
931
|
+
const nodes = val._pattern.nodes;
|
|
932
|
+
if (!nodes || nodes.length < 4) return false;
|
|
933
|
+
const n0 = nodes[0];
|
|
934
|
+
const n1 = nodes[1];
|
|
935
|
+
const n2 = nodes[2];
|
|
936
|
+
const n3 = nodes[3];
|
|
937
|
+
return (
|
|
938
|
+
n0?.edges?.length === 2 &&
|
|
939
|
+
n0.edges[0]?.label === "cons" &&
|
|
940
|
+
n0.edges[1]?.label === "nil" &&
|
|
941
|
+
n1?.edges?.length === 2 &&
|
|
942
|
+
n1.edges[0]?.label === "car" &&
|
|
943
|
+
n1.edges[1]?.label === "cdr" &&
|
|
944
|
+
n2?.edges?.[0]?.label === "ascii" &&
|
|
945
|
+
n3?.edges?.length === 7
|
|
946
|
+
);
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
function fastDecodeLazyString(lazyVariant) {
|
|
950
|
+
if (!isLazyCanonicalString(lazyVariant)) return null;
|
|
951
|
+
if (lazyVariant.tag === "nil") return "";
|
|
952
|
+
if (lazyVariant.tag !== "cons") return null;
|
|
953
|
+
|
|
954
|
+
const buffer = lazyVariant._buffer;
|
|
955
|
+
const pattern = lazyVariant._pattern;
|
|
956
|
+
let bitOffset = lazyVariant._bitOffset;
|
|
957
|
+
|
|
958
|
+
const chunks = [];
|
|
959
|
+
let chunk = "";
|
|
960
|
+
|
|
961
|
+
while (true) {
|
|
962
|
+
const carTag = readBitsAt(buffer, bitOffset, 3);
|
|
963
|
+
if (carTag === 0) {
|
|
964
|
+
let cp = 0;
|
|
965
|
+
for (let i = 0; i < 7; i++) {
|
|
966
|
+
cp |= (readBitsAt(buffer, bitOffset + 3 + i, 1) << i);
|
|
967
|
+
}
|
|
968
|
+
chunk += String.fromCharCode(cp);
|
|
969
|
+
if (chunk.length >= 8192) {
|
|
970
|
+
chunks.push(chunk);
|
|
971
|
+
chunk = "";
|
|
972
|
+
}
|
|
973
|
+
bitOffset += 10;
|
|
974
|
+
} else {
|
|
975
|
+
const charVal = decodeLazyNode(buffer, bitOffset, pattern, 2);
|
|
976
|
+
const cp = unicodeValueToCodePoint(charVal);
|
|
977
|
+
chunk += String.fromCodePoint(cp);
|
|
978
|
+
if (chunk.length >= 8192) {
|
|
979
|
+
chunks.push(chunk);
|
|
980
|
+
chunk = "";
|
|
981
|
+
}
|
|
982
|
+
bitOffset = fastSkipSubtree(buffer, bitOffset, pattern, 2);
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
const listTag = readBitsAt(buffer, bitOffset, 1);
|
|
986
|
+
bitOffset += 1;
|
|
987
|
+
if (listTag !== 0) {
|
|
988
|
+
break;
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
if (chunk.length > 0) chunks.push(chunk);
|
|
993
|
+
return chunks.join("");
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
setLazyStringDecoder(fastDecodeLazyString);
|
|
997
|
+
|
|
444
998
|
export {
|
|
445
999
|
encodeToWire,
|
|
446
1000
|
decodeWire,
|
|
1001
|
+
decodeWireLazy,
|
|
1002
|
+
decodeLazyNode,
|
|
1003
|
+
fastSkipSubtree,
|
|
1004
|
+
readBitsAt,
|
|
447
1005
|
propertyListToPattern,
|
|
448
1006
|
patternToPropertyList,
|
|
449
1007
|
choiceWidth,
|
|
@@ -452,6 +1010,10 @@ export {
|
|
|
452
1010
|
export default {
|
|
453
1011
|
encodeToWire,
|
|
454
1012
|
decodeWire,
|
|
1013
|
+
decodeWireLazy,
|
|
1014
|
+
decodeLazyNode,
|
|
1015
|
+
fastSkipSubtree,
|
|
1016
|
+
readBitsAt,
|
|
455
1017
|
propertyListToPattern,
|
|
456
1018
|
patternToPropertyList,
|
|
457
1019
|
choiceWidth,
|