@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,72 @@
1
+ /**
2
+ * Sequence pattern for dCBOR pattern matching.
3
+ * Matches a sequence of patterns in order.
4
+ *
5
+ * @module pattern/meta/sequence-pattern
6
+ */
7
+
8
+ import type { Cbor } from "@bcts/dcbor";
9
+ import type { Path } from "../../format";
10
+ import type { Pattern } from "../index";
11
+
12
+ /**
13
+ * A pattern that matches a sequence of patterns in order.
14
+ * Used primarily for matching array elements.
15
+ */
16
+ export interface SequencePattern {
17
+ readonly variant: "Sequence";
18
+ readonly patterns: Pattern[];
19
+ }
20
+
21
+ /**
22
+ * Creates a SequencePattern with the given patterns.
23
+ */
24
+ export const sequencePattern = (patterns: Pattern[]): SequencePattern => ({
25
+ variant: "Sequence",
26
+ patterns,
27
+ });
28
+
29
+ /**
30
+ * Tests if a CBOR value matches this sequence pattern.
31
+ *
32
+ * Note: Sequence patterns are used within array patterns for matching
33
+ * consecutive elements. When used standalone (not within an array),
34
+ * they return false/empty as the actual sequence matching logic is
35
+ * handled by the VM and array pattern matching.
36
+ */
37
+ export const sequencePatternMatches = (_pattern: SequencePattern, _haystack: Cbor): boolean => {
38
+ // Sequence patterns are meant for array element matching.
39
+ // When used standalone, they don't match any single CBOR value.
40
+ // The VM handles actual sequence matching within arrays.
41
+ return false;
42
+ };
43
+
44
+ /**
45
+ * Returns paths to matching values.
46
+ *
47
+ * Note: Sequence patterns return empty paths when used directly.
48
+ * The actual sequence matching is handled by the VM within array contexts.
49
+ */
50
+ export const sequencePatternPaths = (_pattern: SequencePattern, _haystack: Cbor): Path[] => {
51
+ // Sequence patterns return empty paths when used directly.
52
+ // This matches Rust behavior where sequence matching is VM-based.
53
+ return [];
54
+ };
55
+
56
+ /**
57
+ * Formats a SequencePattern as a string.
58
+ */
59
+ export const sequencePatternDisplay = (
60
+ pattern: SequencePattern,
61
+ patternDisplay: (p: Pattern) => string,
62
+ ): string => {
63
+ const parts = pattern.patterns.map(patternDisplay);
64
+ return parts.join(", ");
65
+ };
66
+
67
+ /**
68
+ * Gets the patterns in this sequence.
69
+ */
70
+ export const sequencePatternPatterns = (pattern: SequencePattern): Pattern[] => {
71
+ return pattern.patterns;
72
+ };
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Sequence assigner for array pattern matching.
3
+ *
4
+ * Handles element-to-pattern assignment logic, encapsulating the complex logic
5
+ * for mapping array elements to sequence patterns.
6
+ *
7
+ * @module pattern/structure/array-pattern/assigner
8
+ */
9
+
10
+ import type { Cbor } from "@bcts/dcbor";
11
+ import type { Pattern } from "../../index";
12
+ import { hasRepeatPatternsInSlice } from "./helpers";
13
+ import { GenericBacktracker, BooleanBacktrackState, AssignmentBacktrackState } from "./backtrack";
14
+
15
+ /**
16
+ * Helper class for handling element-to-pattern assignment logic.
17
+ * Encapsulates the complex logic for mapping array elements to sequence
18
+ * patterns that was previously duplicated between matching and capture
19
+ * collection.
20
+ */
21
+ export class SequenceAssigner {
22
+ readonly #patterns: Pattern[];
23
+ readonly #arr: Cbor[];
24
+ readonly #matchFn: (pattern: Pattern, value: Cbor) => boolean;
25
+
26
+ constructor(
27
+ patterns: Pattern[],
28
+ arr: Cbor[],
29
+ matchFn: (pattern: Pattern, value: Cbor) => boolean,
30
+ ) {
31
+ this.#patterns = patterns;
32
+ this.#arr = arr;
33
+ this.#matchFn = matchFn;
34
+ }
35
+
36
+ /**
37
+ * Check if the sequence can match against the array elements (boolean result).
38
+ */
39
+ canMatch(): boolean {
40
+ // Simple case: if no patterns, then empty array should match
41
+ if (this.#patterns.length === 0) {
42
+ return this.#arr.length === 0;
43
+ }
44
+
45
+ // Check if we have any repeat patterns that require backtracking
46
+ const hasRepeatPatterns = hasRepeatPatternsInSlice(this.#patterns);
47
+
48
+ // Simple case: if pattern count equals element count AND no repeat patterns
49
+ if (this.#patterns.length === this.#arr.length && !hasRepeatPatterns) {
50
+ // Try one-to-one matching
51
+ return this.#patterns.every((pattern, i) => this.#matchFn(pattern, this.#arr[i]));
52
+ }
53
+
54
+ // Complex case: use generic backtracking framework
55
+ const backtracker = new GenericBacktracker(this.#patterns, this.#arr, this.#matchFn);
56
+ const state = new BooleanBacktrackState();
57
+ return backtracker.backtrack(state, 0, 0);
58
+ }
59
+
60
+ /**
61
+ * Find the element-to-pattern assignments (returns assignment pairs).
62
+ */
63
+ findAssignments(): [number, number][] | undefined {
64
+ // Simple case: if no patterns, then empty array should match
65
+ if (this.#patterns.length === 0) {
66
+ return this.#arr.length === 0 ? [] : undefined;
67
+ }
68
+
69
+ // Check if we have any repeat patterns that require backtracking
70
+ const hasRepeatPatterns = hasRepeatPatternsInSlice(this.#patterns);
71
+
72
+ // Simple case: if pattern count equals element count AND no repeat patterns
73
+ if (this.#patterns.length === this.#arr.length && !hasRepeatPatterns) {
74
+ const assignments: [number, number][] = [];
75
+ for (let patternIdx = 0; patternIdx < this.#patterns.length; patternIdx++) {
76
+ const pattern = this.#patterns[patternIdx];
77
+ const element = this.#arr[patternIdx];
78
+ if (this.#matchFn(pattern, element)) {
79
+ assignments.push([patternIdx, patternIdx]);
80
+ } else {
81
+ return undefined; // Pattern doesn't match its corresponding element
82
+ }
83
+ }
84
+ return assignments;
85
+ }
86
+
87
+ // Complex case: use generic backtracking framework
88
+ const backtracker = new GenericBacktracker(this.#patterns, this.#arr, this.#matchFn);
89
+ const state = new AssignmentBacktrackState();
90
+ if (backtracker.backtrack(state, 0, 0)) {
91
+ return state.assignments;
92
+ }
93
+ return undefined;
94
+ }
95
+ }
@@ -0,0 +1,240 @@
1
+ /**
2
+ * Generic backtracking framework for array pattern matching.
3
+ *
4
+ * This module provides a generic backtracking algorithm that can work with
5
+ * different types of state management (boolean matching vs assignment tracking).
6
+ *
7
+ * @module pattern/structure/array-pattern/backtrack
8
+ */
9
+
10
+ import type { Cbor } from "@bcts/dcbor";
11
+ import type { Pattern } from "../../index";
12
+ import type { RepeatPattern } from "../../meta/repeat-pattern";
13
+ import { extractCaptureWithRepeat, calculateRepeatBounds, canRepeatMatch } from "./helpers";
14
+
15
+ /**
16
+ * Generic backtracking state interface.
17
+ * Abstracts the differences between boolean matching and assignment tracking.
18
+ */
19
+ export interface BacktrackState<T> {
20
+ /**
21
+ * Try to advance the state with a new assignment and return true if successful.
22
+ */
23
+ tryAdvance(patternIdx: number, elementIdx: number): boolean;
24
+
25
+ /**
26
+ * Backtrack by removing the last state change.
27
+ */
28
+ backtrack(): void;
29
+
30
+ /**
31
+ * Check if we've reached a successful final state.
32
+ */
33
+ isSuccess(
34
+ patternIdx: number,
35
+ elementIdx: number,
36
+ patternsLen: number,
37
+ elementsLen: number,
38
+ ): boolean;
39
+
40
+ /**
41
+ * Get the final result.
42
+ */
43
+ getResult(): T;
44
+ }
45
+
46
+ /**
47
+ * Boolean backtracking state - just tracks success/failure.
48
+ */
49
+ export class BooleanBacktrackState implements BacktrackState<boolean> {
50
+ tryAdvance(_patternIdx: number, _elementIdx: number): boolean {
51
+ return true; // Always allow advancement for boolean matching
52
+ }
53
+
54
+ backtrack(): void {
55
+ // Nothing to backtrack for boolean state
56
+ }
57
+
58
+ isSuccess(
59
+ patternIdx: number,
60
+ elementIdx: number,
61
+ patternsLen: number,
62
+ elementsLen: number,
63
+ ): boolean {
64
+ return patternIdx >= patternsLen && elementIdx >= elementsLen;
65
+ }
66
+
67
+ getResult(): boolean {
68
+ return true; // If we get here, we succeeded
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Assignment tracking backtracking state - collects pattern-element pairs.
74
+ */
75
+ export class AssignmentBacktrackState implements BacktrackState<[number, number][]> {
76
+ readonly assignments: [number, number][] = [];
77
+
78
+ tryAdvance(patternIdx: number, elementIdx: number): boolean {
79
+ this.assignments.push([patternIdx, elementIdx]);
80
+ return true;
81
+ }
82
+
83
+ backtrack(): void {
84
+ this.assignments.pop();
85
+ }
86
+
87
+ isSuccess(
88
+ patternIdx: number,
89
+ elementIdx: number,
90
+ patternsLen: number,
91
+ elementsLen: number,
92
+ ): boolean {
93
+ return patternIdx >= patternsLen && elementIdx >= elementsLen;
94
+ }
95
+
96
+ getResult(): [number, number][] {
97
+ return this.assignments;
98
+ }
99
+
100
+ len(): number {
101
+ return this.assignments.length;
102
+ }
103
+
104
+ truncate(len: number): void {
105
+ this.assignments.length = len;
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Generic backtracking algorithm that works with any BacktrackState.
111
+ */
112
+ export class GenericBacktracker {
113
+ readonly #patterns: Pattern[];
114
+ readonly #arr: Cbor[];
115
+ readonly #matchFn: (pattern: Pattern, value: Cbor) => boolean;
116
+
117
+ constructor(
118
+ patterns: Pattern[],
119
+ arr: Cbor[],
120
+ matchFn: (pattern: Pattern, value: Cbor) => boolean,
121
+ ) {
122
+ this.#patterns = patterns;
123
+ this.#arr = arr;
124
+ this.#matchFn = matchFn;
125
+ }
126
+
127
+ /**
128
+ * Generic backtracking algorithm that works with any state type.
129
+ */
130
+ backtrack<T>(state: BacktrackState<T>, patternIdx: number, elementIdx: number): boolean {
131
+ // Base case: if we've matched all patterns
132
+ if (state.isSuccess(patternIdx, elementIdx, this.#patterns.length, this.#arr.length)) {
133
+ return true;
134
+ }
135
+
136
+ if (patternIdx >= this.#patterns.length) {
137
+ return false; // No more patterns but still have elements
138
+ }
139
+
140
+ const currentPattern = this.#patterns[patternIdx];
141
+
142
+ // Check if this is a repeat pattern
143
+ if (currentPattern.kind === "Meta" && currentPattern.pattern.type === "Repeat") {
144
+ const repeatPattern = currentPattern.pattern.pattern;
145
+ return this.tryRepeatBacktrack(repeatPattern, state, patternIdx, elementIdx);
146
+ }
147
+
148
+ // Check if this is a capture pattern
149
+ if (currentPattern.kind === "Meta" && currentPattern.pattern.type === "Capture") {
150
+ // Check if the capture pattern contains a repeat pattern
151
+ const repeatPattern = extractCaptureWithRepeat(currentPattern);
152
+ if (repeatPattern !== undefined) {
153
+ // Handle this like a repeat pattern
154
+ return this.tryRepeatBacktrack(repeatPattern, state, patternIdx, elementIdx);
155
+ }
156
+
157
+ // Handle as a normal single-element capture
158
+ if (elementIdx < this.#arr.length) {
159
+ const element = this.#arr[elementIdx];
160
+ const matches = this.#matchFn(currentPattern, element);
161
+
162
+ if (matches && state.tryAdvance(patternIdx, elementIdx)) {
163
+ if (this.backtrack(state, patternIdx + 1, elementIdx + 1)) {
164
+ return true;
165
+ }
166
+ // Backtracking is handled by the recursive call failing
167
+ state.backtrack();
168
+ }
169
+ }
170
+ return false;
171
+ }
172
+
173
+ // Non-repeat pattern: must match exactly one element
174
+ if (elementIdx < this.#arr.length) {
175
+ const element = this.#arr[elementIdx];
176
+ const matches = this.#matchFn(currentPattern, element);
177
+
178
+ if (matches && state.tryAdvance(patternIdx, elementIdx)) {
179
+ if (this.backtrack(state, patternIdx + 1, elementIdx + 1)) {
180
+ return true;
181
+ }
182
+ // Backtracking is handled by the recursive call failing
183
+ state.backtrack();
184
+ }
185
+ }
186
+ return false;
187
+ }
188
+
189
+ /**
190
+ * Helper for repeat pattern backtracking with generic state.
191
+ */
192
+ private tryRepeatBacktrack<T>(
193
+ repeatPattern: RepeatPattern,
194
+ state: BacktrackState<T>,
195
+ patternIdx: number,
196
+ elementIdx: number,
197
+ ): boolean {
198
+ const quantifier = repeatPattern.quantifier;
199
+ const [minCount, maxCount] = calculateRepeatBounds(quantifier, elementIdx, this.#arr.length);
200
+
201
+ // Try different numbers of repetitions (greedy: start with max)
202
+ for (let repCount = maxCount; repCount >= minCount; repCount--) {
203
+ if (
204
+ elementIdx + repCount <= this.#arr.length &&
205
+ canRepeatMatch(repeatPattern, this.#arr, elementIdx, repCount, this.#matchFn)
206
+ ) {
207
+ // Record state for all consumed elements
208
+ let advancedCount = 0;
209
+ let canAdvance = true;
210
+ for (let i = 0; i < repCount; i++) {
211
+ if (!state.tryAdvance(patternIdx, elementIdx + i)) {
212
+ // If we can't advance, backtrack what we've added
213
+ // and try next repCount
214
+ for (let j = 0; j < advancedCount; j++) {
215
+ state.backtrack();
216
+ }
217
+ canAdvance = false;
218
+ break;
219
+ }
220
+ advancedCount++;
221
+ }
222
+
223
+ if (!canAdvance) {
224
+ continue;
225
+ }
226
+
227
+ // Try to match the rest of the sequence recursively
228
+ if (this.backtrack(state, patternIdx + 1, elementIdx + repCount)) {
229
+ return true;
230
+ }
231
+
232
+ // Backtrack: undo all the advances we made for this repCount
233
+ for (let i = 0; i < repCount; i++) {
234
+ state.backtrack();
235
+ }
236
+ }
237
+ }
238
+ return false;
239
+ }
240
+ }
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Helper functions for array pattern matching.
3
+ *
4
+ * @module pattern/structure/array-pattern/helpers
5
+ */
6
+
7
+ import type { Cbor } from "@bcts/dcbor";
8
+ import type { Pattern } from "../../index";
9
+ import type { RepeatPattern } from "../../meta/repeat-pattern";
10
+ import type { Quantifier } from "../../../quantifier";
11
+
12
+ /**
13
+ * Check if a pattern is a repeat pattern.
14
+ */
15
+ export const isRepeatPattern = (pattern: Pattern): boolean => {
16
+ return pattern.kind === "Meta" && pattern.pattern.type === "Repeat";
17
+ };
18
+
19
+ /**
20
+ * Check if a pattern is a capture pattern containing a repeat pattern.
21
+ * Returns the inner repeat pattern if found.
22
+ */
23
+ export const extractCaptureWithRepeat = (pattern: Pattern): RepeatPattern | undefined => {
24
+ if (pattern.kind === "Meta" && pattern.pattern.type === "Capture") {
25
+ const capturePattern = pattern.pattern.pattern;
26
+ const innerPattern = capturePattern.pattern;
27
+ if (innerPattern.kind === "Meta" && innerPattern.pattern.type === "Repeat") {
28
+ return innerPattern.pattern.pattern;
29
+ }
30
+ }
31
+ return undefined;
32
+ };
33
+
34
+ /**
35
+ * Extract any repeat pattern from a pattern, whether direct or within a capture.
36
+ */
37
+ export const extractRepeatPattern = (pattern: Pattern): RepeatPattern | undefined => {
38
+ if (pattern.kind === "Meta") {
39
+ if (pattern.pattern.type === "Repeat") {
40
+ return pattern.pattern.pattern;
41
+ }
42
+ if (pattern.pattern.type === "Capture") {
43
+ const capturePattern = pattern.pattern.pattern;
44
+ const innerPattern = capturePattern.pattern;
45
+ if (innerPattern.kind === "Meta" && innerPattern.pattern.type === "Repeat") {
46
+ return innerPattern.pattern.pattern;
47
+ }
48
+ }
49
+ }
50
+ return undefined;
51
+ };
52
+
53
+ /**
54
+ * Check if a slice of patterns contains any repeat patterns (direct or in captures).
55
+ */
56
+ export const hasRepeatPatternsInSlice = (patterns: Pattern[]): boolean => {
57
+ return patterns.some((p) => extractRepeatPattern(p) !== undefined);
58
+ };
59
+
60
+ /**
61
+ * Calculate the bounds for repeat pattern matching based on quantifier and
62
+ * available elements.
63
+ */
64
+ export const calculateRepeatBounds = (
65
+ quantifier: Quantifier,
66
+ elementIdx: number,
67
+ arrLen: number,
68
+ ): [number, number] => {
69
+ const minCount = quantifier.min();
70
+ const remainingElements = Math.max(0, arrLen - elementIdx);
71
+ const maxCount = Math.min(quantifier.max() ?? remainingElements, remainingElements);
72
+ return [minCount, maxCount];
73
+ };
74
+
75
+ /**
76
+ * Check if a repeat pattern can match a specific number of elements starting
77
+ * at elementIdx.
78
+ */
79
+ export const canRepeatMatch = (
80
+ repeatPattern: RepeatPattern,
81
+ arr: Cbor[],
82
+ elementIdx: number,
83
+ repCount: number,
84
+ matchFn: (pattern: Pattern, value: Cbor) => boolean,
85
+ ): boolean => {
86
+ if (repCount === 0) {
87
+ return true; // Zero repetitions always match
88
+ }
89
+ for (let i = 0; i < repCount; i++) {
90
+ const element = arr[elementIdx + i];
91
+ if (!matchFn(repeatPattern.pattern, element)) {
92
+ return false;
93
+ }
94
+ }
95
+ return true;
96
+ };
97
+
98
+ /**
99
+ * Build a simple array context path: [arrayCbor, element]
100
+ */
101
+ export const buildSimpleArrayContextPath = (arrayCbor: Cbor, element: Cbor): Cbor[] => {
102
+ return [arrayCbor, element];
103
+ };
104
+
105
+ /**
106
+ * Build an extended array context path: [arrayCbor, element] + capturedPath
107
+ * (skip first element)
108
+ */
109
+ export const buildExtendedArrayContextPath = (
110
+ arrayCbor: Cbor,
111
+ element: Cbor,
112
+ capturedPath: Cbor[],
113
+ ): Cbor[] => {
114
+ const arrayPath: Cbor[] = [arrayCbor, element];
115
+ if (capturedPath.length > 1) {
116
+ arrayPath.push(...capturedPath.slice(1));
117
+ }
118
+ return arrayPath;
119
+ };
120
+
121
+ /**
122
+ * Transform nested captures to include array context, extending allCaptures.
123
+ */
124
+ export const transformCapturesWithArrayContext = (
125
+ arrayCbor: Cbor,
126
+ element: Cbor,
127
+ nestedCaptures: Map<string, Cbor[][]>,
128
+ allCaptures: Map<string, Cbor[][]>,
129
+ ): void => {
130
+ for (const [captureName, capturedPaths] of nestedCaptures) {
131
+ const arrayContextPaths: Cbor[][] = [];
132
+ for (const capturedPath of capturedPaths) {
133
+ const arrayPath = buildExtendedArrayContextPath(arrayCbor, element, capturedPath);
134
+ arrayContextPaths.push(arrayPath);
135
+ }
136
+ const existing = allCaptures.get(captureName) ?? [];
137
+ existing.push(...arrayContextPaths);
138
+ allCaptures.set(captureName, existing);
139
+ }
140
+ };