@bcts/dcbor-pattern 1.0.0-alpha.11

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.
Files changed (73) hide show
  1. package/LICENSE +48 -0
  2. package/README.md +14 -0
  3. package/dist/index.cjs +6561 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.cts +2732 -0
  6. package/dist/index.d.cts.map +1 -0
  7. package/dist/index.d.mts +2732 -0
  8. package/dist/index.d.mts.map +1 -0
  9. package/dist/index.iife.js +6562 -0
  10. package/dist/index.iife.js.map +1 -0
  11. package/dist/index.mjs +6244 -0
  12. package/dist/index.mjs.map +1 -0
  13. package/package.json +85 -0
  14. package/src/error.ts +333 -0
  15. package/src/format.ts +299 -0
  16. package/src/index.ts +20 -0
  17. package/src/interval.ts +230 -0
  18. package/src/parse/index.ts +95 -0
  19. package/src/parse/meta/and-parser.ts +47 -0
  20. package/src/parse/meta/capture-parser.ts +56 -0
  21. package/src/parse/meta/index.ts +13 -0
  22. package/src/parse/meta/not-parser.ts +28 -0
  23. package/src/parse/meta/or-parser.ts +47 -0
  24. package/src/parse/meta/primary-parser.ts +420 -0
  25. package/src/parse/meta/repeat-parser.ts +133 -0
  26. package/src/parse/meta/search-parser.ts +56 -0
  27. package/src/parse/parse-registry.ts +31 -0
  28. package/src/parse/structure/array-parser.ts +210 -0
  29. package/src/parse/structure/index.ts +9 -0
  30. package/src/parse/structure/map-parser.ts +128 -0
  31. package/src/parse/structure/tagged-parser.ts +269 -0
  32. package/src/parse/token.ts +997 -0
  33. package/src/parse/value/bool-parser.ts +33 -0
  34. package/src/parse/value/bytestring-parser.ts +42 -0
  35. package/src/parse/value/date-parser.ts +24 -0
  36. package/src/parse/value/digest-parser.ts +24 -0
  37. package/src/parse/value/index.ts +14 -0
  38. package/src/parse/value/known-value-parser.ts +24 -0
  39. package/src/parse/value/null-parser.ts +19 -0
  40. package/src/parse/value/number-parser.ts +19 -0
  41. package/src/parse/value/text-parser.ts +43 -0
  42. package/src/pattern/index.ts +740 -0
  43. package/src/pattern/match-registry.ts +137 -0
  44. package/src/pattern/matcher.ts +388 -0
  45. package/src/pattern/meta/and-pattern.ts +56 -0
  46. package/src/pattern/meta/any-pattern.ts +43 -0
  47. package/src/pattern/meta/capture-pattern.ts +57 -0
  48. package/src/pattern/meta/index.ts +168 -0
  49. package/src/pattern/meta/not-pattern.ts +70 -0
  50. package/src/pattern/meta/or-pattern.ts +56 -0
  51. package/src/pattern/meta/repeat-pattern.ts +117 -0
  52. package/src/pattern/meta/search-pattern.ts +298 -0
  53. package/src/pattern/meta/sequence-pattern.ts +72 -0
  54. package/src/pattern/structure/array-pattern/assigner.ts +95 -0
  55. package/src/pattern/structure/array-pattern/backtrack.ts +240 -0
  56. package/src/pattern/structure/array-pattern/helpers.ts +140 -0
  57. package/src/pattern/structure/array-pattern/index.ts +502 -0
  58. package/src/pattern/structure/index.ts +122 -0
  59. package/src/pattern/structure/map-pattern.ts +255 -0
  60. package/src/pattern/structure/tagged-pattern.ts +190 -0
  61. package/src/pattern/value/bool-pattern.ts +67 -0
  62. package/src/pattern/value/bytes-utils.ts +48 -0
  63. package/src/pattern/value/bytestring-pattern.ts +111 -0
  64. package/src/pattern/value/date-pattern.ts +162 -0
  65. package/src/pattern/value/digest-pattern.ts +136 -0
  66. package/src/pattern/value/index.ts +168 -0
  67. package/src/pattern/value/known-value-pattern.ts +123 -0
  68. package/src/pattern/value/null-pattern.ts +46 -0
  69. package/src/pattern/value/number-pattern.ts +181 -0
  70. package/src/pattern/value/text-pattern.ts +82 -0
  71. package/src/pattern/vm.ts +619 -0
  72. package/src/quantifier.ts +185 -0
  73. package/src/reluctance.ts +65 -0
@@ -0,0 +1,502 @@
1
+ /**
2
+ * Array pattern for dCBOR pattern matching.
3
+ *
4
+ * @module pattern/structure/array-pattern
5
+ */
6
+
7
+ import type { Cbor } from "@bcts/dcbor";
8
+ import { isArray, arrayLength, arrayItem, cbor } from "@bcts/dcbor";
9
+ import type { Path } from "../../../format";
10
+ import type { Pattern } from "../../index";
11
+ import type { SequencePattern } from "../../meta/sequence-pattern";
12
+ import type { RepeatPattern } from "../../meta/repeat-pattern";
13
+ import { Interval } from "../../../interval";
14
+ import { matchPattern } from "../../match-registry";
15
+ import {
16
+ hasRepeatPatternsInSlice,
17
+ extractCaptureWithRepeat,
18
+ isRepeatPattern,
19
+ buildSimpleArrayContextPath,
20
+ } from "./helpers";
21
+ import { SequenceAssigner } from "./assigner";
22
+
23
+ // Re-export helper modules
24
+ export * from "./helpers";
25
+ export * from "./backtrack";
26
+ export * from "./assigner";
27
+
28
+ /**
29
+ * Pattern for matching CBOR array structures.
30
+ */
31
+ export type ArrayPattern =
32
+ | { readonly variant: "Any" }
33
+ | { readonly variant: "Elements"; readonly pattern: Pattern }
34
+ | { readonly variant: "Length"; readonly length: Interval };
35
+
36
+ /**
37
+ * Creates an ArrayPattern that matches any array.
38
+ */
39
+ export const arrayPatternAny = (): ArrayPattern => ({ variant: "Any" });
40
+
41
+ /**
42
+ * Creates an ArrayPattern that matches arrays with elements matching the pattern.
43
+ */
44
+ export const arrayPatternWithElements = (pattern: Pattern): ArrayPattern => ({
45
+ variant: "Elements",
46
+ pattern,
47
+ });
48
+
49
+ /**
50
+ * Creates an ArrayPattern that matches arrays with a specific length.
51
+ */
52
+ export const arrayPatternWithLength = (length: number): ArrayPattern => ({
53
+ variant: "Length",
54
+ length: Interval.exactly(length),
55
+ });
56
+
57
+ /**
58
+ * Creates an ArrayPattern that matches arrays with length in a range.
59
+ */
60
+ export const arrayPatternWithLengthRange = (min: number, max?: number): ArrayPattern => ({
61
+ variant: "Length",
62
+ length: max !== undefined ? Interval.from(min, max) : Interval.atLeast(min),
63
+ });
64
+
65
+ /**
66
+ * Creates an ArrayPattern that matches arrays with length in an interval.
67
+ */
68
+ export const arrayPatternWithLengthInterval = (interval: Interval): ArrayPattern => ({
69
+ variant: "Length",
70
+ length: interval,
71
+ });
72
+
73
+ /**
74
+ * Gets array elements as Cbor array.
75
+ */
76
+ const getArrayElements = (haystack: Cbor): Cbor[] | undefined => {
77
+ if (!isArray(haystack)) {
78
+ return undefined;
79
+ }
80
+ const len = arrayLength(haystack);
81
+ if (len === undefined) {
82
+ return undefined;
83
+ }
84
+ const elements: Cbor[] = [];
85
+ for (let i = 0; i < len; i++) {
86
+ const item = arrayItem(haystack, i);
87
+ if (item === undefined) {
88
+ return undefined;
89
+ }
90
+ elements.push(item);
91
+ }
92
+ return elements;
93
+ };
94
+
95
+ /**
96
+ * Match a single repeat pattern against array elements.
97
+ */
98
+ const matchRepeatPatternAgainstArray = (repeatPattern: RepeatPattern, arr: Cbor[]): boolean => {
99
+ const quantifier = repeatPattern.quantifier;
100
+ const minCount = quantifier.min();
101
+ const maxCount = quantifier.max() ?? arr.length;
102
+
103
+ // Check if the array length is within the valid range for this repeat
104
+ if (arr.length < minCount || arr.length > maxCount) {
105
+ return false;
106
+ }
107
+
108
+ // Check if all elements match the repeated pattern
109
+ return arr.every((element) => matchPattern(repeatPattern.pattern, element));
110
+ };
111
+
112
+ /**
113
+ * Match a sequence of patterns against array elements using backtracking.
114
+ */
115
+ const matchSequencePatternsAgainstArray = (seqPattern: SequencePattern, arr: Cbor[]): boolean => {
116
+ const patterns = seqPattern.patterns;
117
+ const assigner = new SequenceAssigner(patterns, arr, matchPattern);
118
+ return assigner.canMatch();
119
+ };
120
+
121
+ /**
122
+ * Check if a sequence pattern can match against array elements.
123
+ */
124
+ const canMatchSequenceAgainstArray = (pattern: Pattern, arr: Cbor[]): boolean => {
125
+ if (pattern.kind === "Meta") {
126
+ if (pattern.pattern.type === "Sequence") {
127
+ return matchSequencePatternsAgainstArray(pattern.pattern.pattern, arr);
128
+ }
129
+ if (pattern.pattern.type === "Repeat") {
130
+ return matchRepeatPatternAgainstArray(pattern.pattern.pattern, arr);
131
+ }
132
+ }
133
+ // For non-sequence patterns, fall back to simple matching
134
+ const arrayCbor = cbor(arr);
135
+ return matchPattern(pattern, arrayCbor);
136
+ };
137
+
138
+ /**
139
+ * Match a complex sequence against array elements.
140
+ */
141
+ const matchComplexSequence = (haystack: Cbor, pattern: Pattern): Path[] => {
142
+ const arr = getArrayElements(haystack);
143
+ if (arr === undefined) {
144
+ return [];
145
+ }
146
+
147
+ const canMatch = canMatchSequenceAgainstArray(pattern, arr);
148
+ if (canMatch) {
149
+ return [[haystack]];
150
+ }
151
+ return [];
152
+ };
153
+
154
+ /**
155
+ * Find which array elements are assigned to which sequence patterns.
156
+ */
157
+ const findSequenceElementAssignments = (
158
+ seqPattern: SequencePattern,
159
+ arr: Cbor[],
160
+ ): [number, number][] | undefined => {
161
+ const patterns = seqPattern.patterns;
162
+ const assigner = new SequenceAssigner(patterns, arr, matchPattern);
163
+ return assigner.findAssignments();
164
+ };
165
+
166
+ /**
167
+ * Handle sequence patterns with captures by manually matching elements
168
+ * and collecting captures with proper array context.
169
+ */
170
+ const handleSequenceCaptures = (
171
+ seqPattern: SequencePattern,
172
+ arrayCbor: Cbor,
173
+ arr: Cbor[],
174
+ ): [Path[], Map<string, Path[]>] => {
175
+ const assignments = findSequenceElementAssignments(seqPattern, arr);
176
+ if (assignments === undefined) {
177
+ return [[], new Map<string, Path[]>()];
178
+ }
179
+
180
+ const allCaptures = new Map<string, Path[]>();
181
+
182
+ // Process each pattern and its assigned elements
183
+ for (let patternIdx = 0; patternIdx < seqPattern.patterns.length; patternIdx++) {
184
+ const pattern = seqPattern.patterns[patternIdx];
185
+
186
+ // Check if this is a capture pattern containing a repeat pattern
187
+ if (pattern.kind === "Meta" && pattern.pattern.type === "Capture") {
188
+ const capturePattern = pattern.pattern.pattern;
189
+
190
+ // Check if the capture contains a repeat pattern
191
+ if (extractCaptureWithRepeat(pattern) !== undefined) {
192
+ // This is a capture pattern with a repeat (like @rest((*)*)
193
+ // We need to capture the sub-array of matched elements
194
+ const capturedElements: Cbor[] = assignments
195
+ .filter(([pIdx, _]) => pIdx === patternIdx)
196
+ .map(([_, eIdx]) => arr[eIdx]);
197
+
198
+ // Create a sub-array from the captured elements
199
+ const subArray = cbor(capturedElements);
200
+
201
+ // For capture patterns, we directly capture the sub-array with the capture name
202
+ const captureName = capturePattern.name;
203
+ const arrayContextPath = buildSimpleArrayContextPath(arrayCbor, subArray);
204
+
205
+ const existing = allCaptures.get(captureName) ?? [];
206
+ existing.push(arrayContextPath);
207
+ allCaptures.set(captureName, existing);
208
+ continue;
209
+ }
210
+ }
211
+
212
+ // Check if this is a direct repeat pattern that might capture multiple elements
213
+ if (isRepeatPattern(pattern) && pattern.kind === "Meta" && pattern.pattern.type === "Repeat") {
214
+ const repeatPattern = pattern.pattern.pattern;
215
+
216
+ // For repeat patterns, check if they have captures
217
+ // by looking at the inner pattern
218
+ const innerPattern = repeatPattern.pattern;
219
+ if (innerPattern.kind === "Meta" && innerPattern.pattern.type === "Capture") {
220
+ // This is a repeat pattern with captures
221
+ const capturedElements: Cbor[] = assignments
222
+ .filter(([pIdx, _]) => pIdx === patternIdx)
223
+ .map(([_, eIdx]) => arr[eIdx]);
224
+
225
+ // Create a sub-array from the captured elements
226
+ const subArray = cbor(capturedElements);
227
+
228
+ // Get the capture name from the inner capture pattern
229
+ const captureName = innerPattern.pattern.pattern.name;
230
+ const arrayContextPath = buildSimpleArrayContextPath(arrayCbor, subArray);
231
+
232
+ const existing = allCaptures.get(captureName) ?? [];
233
+ existing.push(arrayContextPath);
234
+ allCaptures.set(captureName, existing);
235
+ continue;
236
+ }
237
+ }
238
+
239
+ // For non-repeat patterns or repeat patterns without captures,
240
+ // process each assigned element individually
241
+ const elementIndices = assignments
242
+ .filter(([pIdx, _]) => pIdx === patternIdx)
243
+ .map(([_, eIdx]) => eIdx);
244
+
245
+ for (const elementIdx of elementIndices) {
246
+ const element = arr[elementIdx];
247
+
248
+ // Check if this pattern has any captures
249
+ if (pattern.kind === "Meta" && pattern.pattern.type === "Capture") {
250
+ const captureName = pattern.pattern.pattern.name;
251
+ const arrayContextPath = buildSimpleArrayContextPath(arrayCbor, element);
252
+
253
+ const existing = allCaptures.get(captureName) ?? [];
254
+ existing.push(arrayContextPath);
255
+ allCaptures.set(captureName, existing);
256
+ }
257
+ }
258
+ }
259
+
260
+ // Return the array path and all captures
261
+ return [[[arrayCbor]], allCaptures];
262
+ };
263
+
264
+ /**
265
+ * Tests if a CBOR value matches this array pattern.
266
+ */
267
+ export const arrayPatternMatches = (pattern: ArrayPattern, haystack: Cbor): boolean => {
268
+ if (!isArray(haystack)) {
269
+ return false;
270
+ }
271
+
272
+ switch (pattern.variant) {
273
+ case "Any":
274
+ return true;
275
+ case "Elements": {
276
+ const arr = getArrayElements(haystack);
277
+ if (arr === undefined) {
278
+ return false;
279
+ }
280
+
281
+ const elemPattern = pattern.pattern;
282
+
283
+ // Check pattern type for appropriate matching
284
+ if (elemPattern.kind === "Meta") {
285
+ if (elemPattern.pattern.type === "Sequence") {
286
+ // Use sequence matching with backtracking
287
+ return matchSequencePatternsAgainstArray(elemPattern.pattern.pattern, arr);
288
+ }
289
+ if (elemPattern.pattern.type === "Repeat") {
290
+ // Use repeat matching
291
+ return matchRepeatPatternAgainstArray(elemPattern.pattern.pattern, arr);
292
+ }
293
+ if (elemPattern.pattern.type === "Capture") {
294
+ // For capture patterns, check if any element matches
295
+ return arr.some((element) => matchPattern(elemPattern, element));
296
+ }
297
+ }
298
+
299
+ // For value/structure patterns, require exactly one element that matches
300
+ if (
301
+ elemPattern.kind === "Value" ||
302
+ elemPattern.kind === "Structure" ||
303
+ (elemPattern.kind === "Meta" && elemPattern.pattern.type === "Any")
304
+ ) {
305
+ if (arr.length !== 1) {
306
+ return false;
307
+ }
308
+ return matchPattern(elemPattern, arr[0]);
309
+ }
310
+
311
+ // For other meta patterns (or, and, etc.), check if any element matches
312
+ return arr.some((element) => matchPattern(elemPattern, element));
313
+ }
314
+ case "Length": {
315
+ const len = arrayLength(haystack);
316
+ return len !== undefined && pattern.length.contains(len);
317
+ }
318
+ }
319
+ };
320
+
321
+ /**
322
+ * Returns paths to matching array values.
323
+ */
324
+ export const arrayPatternPaths = (pattern: ArrayPattern, haystack: Cbor): Path[] => {
325
+ if (!isArray(haystack)) {
326
+ return [];
327
+ }
328
+
329
+ const arr = getArrayElements(haystack);
330
+ if (arr === undefined) {
331
+ return [];
332
+ }
333
+
334
+ switch (pattern.variant) {
335
+ case "Any":
336
+ return [[haystack]];
337
+
338
+ case "Elements": {
339
+ const elemPattern = pattern.pattern;
340
+
341
+ // Check pattern type for appropriate matching
342
+ if (elemPattern.kind === "Meta") {
343
+ if (elemPattern.pattern.type === "Sequence") {
344
+ const seqPattern = elemPattern.pattern.pattern;
345
+ const hasRepeats = hasRepeatPatternsInSlice(seqPattern.patterns);
346
+
347
+ if (hasRepeats) {
348
+ // Use complex sequence matching
349
+ return matchComplexSequence(haystack, elemPattern);
350
+ }
351
+
352
+ // Simple sequence: match each pattern against consecutive elements
353
+ if (seqPattern.patterns.length === arr.length) {
354
+ for (let i = 0; i < seqPattern.patterns.length; i++) {
355
+ if (!matchPattern(seqPattern.patterns[i], arr[i])) {
356
+ return [];
357
+ }
358
+ }
359
+ return [[haystack]];
360
+ }
361
+ return [];
362
+ }
363
+
364
+ if (elemPattern.pattern.type === "Repeat") {
365
+ return matchComplexSequence(haystack, elemPattern);
366
+ }
367
+
368
+ if (elemPattern.pattern.type === "Capture") {
369
+ // For capture patterns, check if any element matches
370
+ const hasMatch = arr.some((element) => matchPattern(elemPattern, element));
371
+ return hasMatch ? [[haystack]] : [];
372
+ }
373
+ }
374
+
375
+ // For value/structure patterns, require exactly one element that matches
376
+ if (
377
+ elemPattern.kind === "Value" ||
378
+ elemPattern.kind === "Structure" ||
379
+ (elemPattern.kind === "Meta" && elemPattern.pattern.type === "Any")
380
+ ) {
381
+ if (arr.length !== 1) {
382
+ return [];
383
+ }
384
+ return matchPattern(elemPattern, arr[0]) ? [[haystack]] : [];
385
+ }
386
+
387
+ // For other meta patterns, check if any element matches
388
+ const hasMatch = arr.some((element) => matchPattern(elemPattern, element));
389
+ return hasMatch ? [[haystack]] : [];
390
+ }
391
+
392
+ case "Length":
393
+ return pattern.length.contains(arr.length) ? [[haystack]] : [];
394
+ }
395
+ };
396
+
397
+ /**
398
+ * Returns paths with captures for array patterns.
399
+ */
400
+ export const arrayPatternPathsWithCaptures = (
401
+ pattern: ArrayPattern,
402
+ haystack: Cbor,
403
+ ): [Path[], Map<string, Path[]>] => {
404
+ if (!isArray(haystack)) {
405
+ return [[], new Map<string, Path[]>()];
406
+ }
407
+
408
+ const arr = getArrayElements(haystack);
409
+ if (arr === undefined) {
410
+ return [[], new Map<string, Path[]>()];
411
+ }
412
+
413
+ switch (pattern.variant) {
414
+ case "Any":
415
+ case "Length":
416
+ return [arrayPatternPaths(pattern, haystack), new Map<string, Path[]>()];
417
+
418
+ case "Elements": {
419
+ const elemPattern = pattern.pattern;
420
+
421
+ // Check for sequence patterns with captures
422
+ if (elemPattern.kind === "Meta" && elemPattern.pattern.type === "Sequence") {
423
+ const seqPattern = elemPattern.pattern.pattern;
424
+
425
+ // First check if this pattern matches
426
+ if (!arrayPatternMatches(pattern, haystack)) {
427
+ return [[], new Map<string, Path[]>()];
428
+ }
429
+
430
+ return handleSequenceCaptures(seqPattern, haystack, arr);
431
+ }
432
+
433
+ // For capture patterns
434
+ if (elemPattern.kind === "Meta" && elemPattern.pattern.type === "Capture") {
435
+ const capturePattern = elemPattern.pattern.pattern;
436
+ const matchingElements = arr.filter((element) => matchPattern(elemPattern, element));
437
+
438
+ if (matchingElements.length === 0) {
439
+ return [[], new Map<string, Path[]>()];
440
+ }
441
+
442
+ const captures = new Map<string, Path[]>();
443
+ const paths: Path[] = [];
444
+
445
+ for (const element of matchingElements) {
446
+ paths.push(buildSimpleArrayContextPath(haystack, element));
447
+ }
448
+
449
+ captures.set(capturePattern.name, paths);
450
+ return [[[haystack]], captures];
451
+ }
452
+
453
+ // Default: no captures
454
+ return [arrayPatternPaths(pattern, haystack), new Map<string, Path[]>()];
455
+ }
456
+ }
457
+ };
458
+
459
+ /**
460
+ * Formats an ArrayPattern as a string.
461
+ */
462
+ export const arrayPatternDisplay = (
463
+ pattern: ArrayPattern,
464
+ patternDisplay: (p: Pattern) => string,
465
+ ): string => {
466
+ switch (pattern.variant) {
467
+ case "Any":
468
+ return "array";
469
+ case "Elements": {
470
+ const elemPattern = pattern.pattern;
471
+ // For sequence patterns within arrays, format elements with commas
472
+ if (elemPattern.kind === "Meta" && elemPattern.pattern.type === "Sequence") {
473
+ const parts = elemPattern.pattern.pattern.patterns.map(patternDisplay);
474
+ return `[${parts.join(", ")}]`;
475
+ }
476
+ return `[${patternDisplay(pattern.pattern)}]`;
477
+ }
478
+ case "Length":
479
+ return `[${pattern.length.toString()}]`;
480
+ }
481
+ };
482
+
483
+ /**
484
+ * Compares two ArrayPatterns for equality.
485
+ */
486
+ export const arrayPatternEquals = (
487
+ a: ArrayPattern,
488
+ b: ArrayPattern,
489
+ patternEquals: (p1: Pattern, p2: Pattern) => boolean,
490
+ ): boolean => {
491
+ if (a.variant !== b.variant) {
492
+ return false;
493
+ }
494
+ switch (a.variant) {
495
+ case "Any":
496
+ return true;
497
+ case "Elements":
498
+ return patternEquals(a.pattern, (b as typeof a).pattern);
499
+ case "Length":
500
+ return a.length.equals((b as typeof a).length);
501
+ }
502
+ };
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Structure patterns for dCBOR pattern matching.
3
+ *
4
+ * @module pattern/structure
5
+ */
6
+
7
+ export * from "./array-pattern";
8
+ export * from "./map-pattern";
9
+ export * from "./tagged-pattern";
10
+
11
+ import type { Cbor } from "@bcts/dcbor";
12
+ import type { Path } from "../../format";
13
+ import type { Pattern } from "../index";
14
+
15
+ import {
16
+ type ArrayPattern,
17
+ arrayPatternPaths,
18
+ arrayPatternDisplay,
19
+ arrayPatternPathsWithCaptures,
20
+ } from "./array-pattern";
21
+ import {
22
+ type MapPattern,
23
+ mapPatternPaths,
24
+ mapPatternDisplay,
25
+ mapPatternPathsWithCaptures,
26
+ } from "./map-pattern";
27
+ import {
28
+ type TaggedPattern,
29
+ taggedPatternPaths,
30
+ taggedPatternDisplay,
31
+ taggedPatternPathsWithCaptures,
32
+ } from "./tagged-pattern";
33
+
34
+ /**
35
+ * Union of all structure pattern types.
36
+ */
37
+ export type StructurePattern =
38
+ | { readonly type: "Array"; readonly pattern: ArrayPattern }
39
+ | { readonly type: "Map"; readonly pattern: MapPattern }
40
+ | { readonly type: "Tagged"; readonly pattern: TaggedPattern };
41
+
42
+ /**
43
+ * Returns paths to matching structures for a StructurePattern.
44
+ */
45
+ export const structurePatternPaths = (pattern: StructurePattern, haystack: Cbor): Path[] => {
46
+ switch (pattern.type) {
47
+ case "Array":
48
+ return arrayPatternPaths(pattern.pattern, haystack);
49
+ case "Map":
50
+ return mapPatternPaths(pattern.pattern, haystack);
51
+ case "Tagged":
52
+ return taggedPatternPaths(pattern.pattern, haystack);
53
+ }
54
+ };
55
+
56
+ /**
57
+ * Tests if a CBOR value matches a StructurePattern.
58
+ */
59
+ export const structurePatternMatches = (pattern: StructurePattern, haystack: Cbor): boolean => {
60
+ return structurePatternPaths(pattern, haystack).length > 0;
61
+ };
62
+
63
+ /**
64
+ * Returns paths with captures for a StructurePattern.
65
+ * Used internally by the VM to avoid infinite recursion.
66
+ */
67
+ export const structurePatternPathsWithCaptures = (
68
+ pattern: StructurePattern,
69
+ haystack: Cbor,
70
+ ): [Path[], Map<string, Path[]>] => {
71
+ switch (pattern.type) {
72
+ case "Array":
73
+ return arrayPatternPathsWithCaptures(pattern.pattern, haystack);
74
+ case "Map":
75
+ return mapPatternPathsWithCaptures(pattern.pattern, haystack);
76
+ case "Tagged":
77
+ return taggedPatternPathsWithCaptures(pattern.pattern, haystack);
78
+ }
79
+ };
80
+
81
+ /**
82
+ * Formats a StructurePattern as a string.
83
+ */
84
+ export const structurePatternDisplay = (
85
+ pattern: StructurePattern,
86
+ patternDisplay: (p: Pattern) => string,
87
+ ): string => {
88
+ switch (pattern.type) {
89
+ case "Array":
90
+ return arrayPatternDisplay(pattern.pattern, patternDisplay);
91
+ case "Map":
92
+ return mapPatternDisplay(pattern.pattern, patternDisplay);
93
+ case "Tagged":
94
+ return taggedPatternDisplay(pattern.pattern, patternDisplay);
95
+ }
96
+ };
97
+
98
+ // Convenience constructors for StructurePattern
99
+
100
+ /**
101
+ * Creates an Array StructurePattern.
102
+ */
103
+ export const structureArray = (pattern: ArrayPattern): StructurePattern => ({
104
+ type: "Array",
105
+ pattern,
106
+ });
107
+
108
+ /**
109
+ * Creates a Map StructurePattern.
110
+ */
111
+ export const structureMap = (pattern: MapPattern): StructurePattern => ({
112
+ type: "Map",
113
+ pattern,
114
+ });
115
+
116
+ /**
117
+ * Creates a Tagged StructurePattern.
118
+ */
119
+ export const structureTagged = (pattern: TaggedPattern): StructurePattern => ({
120
+ type: "Tagged",
121
+ pattern,
122
+ });