@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,137 @@
1
+ /**
2
+ * Late-binding registry for pattern matching functions.
3
+ * This avoids circular dependencies between pattern modules.
4
+ *
5
+ * @module pattern/match-registry
6
+ */
7
+
8
+ import type { Cbor } from "@bcts/dcbor";
9
+ import type { Path } from "../format";
10
+
11
+ // Forward declare Pattern type to avoid circular import
12
+ // The actual Pattern type is defined in ./index.ts
13
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
+ type Pattern = any;
15
+
16
+ /**
17
+ * Match result with paths and captures.
18
+ */
19
+ export interface MatchResultInternal {
20
+ readonly paths: Path[];
21
+ readonly captures: Map<string, Path[]>;
22
+ }
23
+
24
+ /**
25
+ * Registry for the pattern matching function.
26
+ * This gets set by pattern/index.ts after all modules are loaded.
27
+ */
28
+ export let matchFn: ((pattern: Pattern, haystack: Cbor) => boolean) | undefined;
29
+
30
+ /**
31
+ * Registry for the pattern paths function.
32
+ * This gets set by pattern/index.ts after all modules are loaded.
33
+ */
34
+ export let pathsFn: ((pattern: Pattern, haystack: Cbor) => Path[]) | undefined;
35
+
36
+ /**
37
+ * Registry for the pattern paths with captures function (VM-based).
38
+ * This gets set by pattern/index.ts after all modules are loaded.
39
+ */
40
+ export let pathsWithCapturesFn:
41
+ | ((pattern: Pattern, haystack: Cbor) => MatchResultInternal)
42
+ | undefined;
43
+
44
+ /**
45
+ * Registry for the direct pattern paths with captures function (non-VM).
46
+ * This is used by the VM to avoid infinite recursion.
47
+ */
48
+ export let pathsWithCapturesDirectFn:
49
+ | ((pattern: Pattern, haystack: Cbor) => MatchResultInternal)
50
+ | undefined;
51
+
52
+ /**
53
+ * Sets the pattern matching function.
54
+ * Called by pattern/index.ts during module initialization.
55
+ */
56
+ export const setMatchFn = (fn: (pattern: Pattern, haystack: Cbor) => boolean): void => {
57
+ matchFn = fn;
58
+ };
59
+
60
+ /**
61
+ * Sets the pattern paths function.
62
+ * Called by pattern/index.ts during module initialization.
63
+ */
64
+ export const setPathsFn = (fn: (pattern: Pattern, haystack: Cbor) => Path[]): void => {
65
+ pathsFn = fn;
66
+ };
67
+
68
+ /**
69
+ * Sets the pattern paths with captures function.
70
+ * Called by pattern/index.ts during module initialization.
71
+ */
72
+ export const setPathsWithCapturesFn = (
73
+ fn: (pattern: Pattern, haystack: Cbor) => MatchResultInternal,
74
+ ): void => {
75
+ pathsWithCapturesFn = fn;
76
+ };
77
+
78
+ /**
79
+ * Sets the direct pattern paths with captures function (non-VM).
80
+ * Called by pattern/index.ts during module initialization.
81
+ */
82
+ export const setPathsWithCapturesDirectFn = (
83
+ fn: (pattern: Pattern, haystack: Cbor) => MatchResultInternal,
84
+ ): void => {
85
+ pathsWithCapturesDirectFn = fn;
86
+ };
87
+
88
+ /**
89
+ * Matches a pattern against a CBOR value using the registered function.
90
+ * @throws Error if the match function hasn't been registered yet.
91
+ */
92
+ export const matchPattern = (pattern: Pattern, haystack: Cbor): boolean => {
93
+ if (matchFn === undefined) {
94
+ throw new Error("Pattern match function not initialized");
95
+ }
96
+ return matchFn(pattern, haystack);
97
+ };
98
+
99
+ /**
100
+ * Gets paths for a pattern against a CBOR value using the registered function.
101
+ * @throws Error if the paths function hasn't been registered yet.
102
+ */
103
+ export const getPatternPaths = (pattern: Pattern, haystack: Cbor): Path[] => {
104
+ if (pathsFn === undefined) {
105
+ throw new Error("Pattern paths function not initialized");
106
+ }
107
+ return pathsFn(pattern, haystack);
108
+ };
109
+
110
+ /**
111
+ * Gets paths with captures for a pattern against a CBOR value (VM-based).
112
+ * @throws Error if the function hasn't been registered yet.
113
+ */
114
+ export const getPatternPathsWithCaptures = (
115
+ pattern: Pattern,
116
+ haystack: Cbor,
117
+ ): MatchResultInternal => {
118
+ if (pathsWithCapturesFn === undefined) {
119
+ throw new Error("Pattern paths with captures function not initialized");
120
+ }
121
+ return pathsWithCapturesFn(pattern, haystack);
122
+ };
123
+
124
+ /**
125
+ * Gets paths with captures directly without the VM (non-recursive).
126
+ * This is used by the VM to avoid infinite recursion.
127
+ * @throws Error if the function hasn't been registered yet.
128
+ */
129
+ export const getPatternPathsWithCapturesDirect = (
130
+ pattern: Pattern,
131
+ haystack: Cbor,
132
+ ): MatchResultInternal => {
133
+ if (pathsWithCapturesDirectFn === undefined) {
134
+ throw new Error("Direct pattern paths with captures function not initialized");
135
+ }
136
+ return pathsWithCapturesDirectFn(pattern, haystack);
137
+ };
@@ -0,0 +1,388 @@
1
+ /**
2
+ * Matcher interface for dCBOR pattern matching.
3
+ *
4
+ * @module pattern/matcher
5
+ */
6
+
7
+ import type { Cbor } from "@bcts/dcbor";
8
+ import type { Path } from "../format";
9
+ import type { Instr, Program } from "./vm";
10
+ import type { Pattern } from "./index";
11
+ import type { StructurePattern } from "./structure";
12
+ import type { MetaPattern } from "./meta";
13
+
14
+ /**
15
+ * Result of pattern matching with captures.
16
+ */
17
+ export interface MatchWithCaptures {
18
+ readonly paths: Path[];
19
+ readonly captures: Map<string, Path[]>;
20
+ }
21
+
22
+ /**
23
+ * Interface for objects that can match against CBOR values.
24
+ *
25
+ * This interface defines the contract for all pattern types in the system.
26
+ * Implementations handle matching, path collection, and VM bytecode compilation.
27
+ */
28
+ export interface Matcher {
29
+ /**
30
+ * Return all matching paths along with any named captures.
31
+ *
32
+ * @param haystack - The CBOR value to match against
33
+ * @returns A tuple of paths and captures map
34
+ */
35
+ pathsWithCaptures(haystack: Cbor): MatchWithCaptures;
36
+
37
+ /**
38
+ * Return only the matching paths, discarding any captures.
39
+ *
40
+ * @param haystack - The CBOR value to match against
41
+ * @returns Array of paths to matching elements
42
+ */
43
+ paths(haystack: Cbor): Path[];
44
+
45
+ /**
46
+ * Check if the pattern matches the given CBOR value.
47
+ *
48
+ * @param haystack - The CBOR value to test
49
+ * @returns true if the pattern matches
50
+ */
51
+ matches(haystack: Cbor): boolean;
52
+
53
+ /**
54
+ * Compile this pattern into VM bytecode.
55
+ *
56
+ * @param code - The instruction array to append to
57
+ * @param literals - The literals array to append to
58
+ * @param captures - The capture names array
59
+ */
60
+ compile(code: Instr[], literals: Pattern[], captures: string[]): void;
61
+
62
+ /**
63
+ * Recursively collect all capture names from this pattern.
64
+ *
65
+ * @param names - The array to collect names into
66
+ */
67
+ collectCaptureNames(names: string[]): void;
68
+
69
+ /**
70
+ * Check if the pattern display is "complex" (requires parentheses).
71
+ *
72
+ * @returns true if the pattern requires grouping
73
+ */
74
+ isComplex(): boolean;
75
+
76
+ /**
77
+ * Format the pattern as a string.
78
+ */
79
+ toString(): string;
80
+ }
81
+
82
+ /**
83
+ * Default implementation helpers for Matcher.
84
+ */
85
+ export const MatcherDefaults = {
86
+ /**
87
+ * Default paths implementation using pathsWithCaptures.
88
+ */
89
+ paths(matcher: Pick<Matcher, "pathsWithCaptures">, haystack: Cbor): Path[] {
90
+ return matcher.pathsWithCaptures(haystack).paths;
91
+ },
92
+
93
+ /**
94
+ * Default matches implementation using paths.
95
+ */
96
+ matches(matcher: Pick<Matcher, "paths">, haystack: Cbor): boolean {
97
+ return matcher.paths(haystack).length > 0;
98
+ },
99
+
100
+ /**
101
+ * Default pathsWithCaptures throws not implemented.
102
+ */
103
+ pathsWithCaptures(_haystack: Cbor): MatchWithCaptures {
104
+ throw new Error("pathsWithCaptures not implemented");
105
+ },
106
+
107
+ /**
108
+ * Default compile throws not implemented.
109
+ */
110
+ compile(_code: Instr[], _literals: Pattern[], _captures: string[]): void {
111
+ throw new Error("compile not implemented");
112
+ },
113
+
114
+ /**
115
+ * Default collectCaptureNames does nothing.
116
+ */
117
+ collectCaptureNames(_names: string[]): void {
118
+ // Default implementation does nothing
119
+ },
120
+
121
+ /**
122
+ * Default isComplex returns false.
123
+ */
124
+ isComplex(): boolean {
125
+ return false;
126
+ },
127
+ };
128
+
129
+ /**
130
+ * Compiles a pattern into a VM program.
131
+ *
132
+ * @param pattern - The pattern to compile
133
+ * @returns A compiled program ready for execution
134
+ */
135
+ export const compilePattern = (pattern: Pattern): Program => {
136
+ const code: Instr[] = [];
137
+ const literals: Pattern[] = [];
138
+ const captureNames: string[] = [];
139
+
140
+ // Collect all capture names first
141
+ collectPatternCaptureNames(pattern, captureNames);
142
+
143
+ // Compile the pattern
144
+ compilePatternToCode(pattern, code, literals, captureNames);
145
+
146
+ // Add final Accept instruction
147
+ code.push({ type: "Accept" });
148
+
149
+ return {
150
+ code,
151
+ literals,
152
+ captureNames,
153
+ };
154
+ };
155
+
156
+ /**
157
+ * Recursively collects capture names from a pattern.
158
+ */
159
+ const collectPatternCaptureNames = (pattern: Pattern, names: string[]): void => {
160
+ switch (pattern.kind) {
161
+ case "Value":
162
+ // Value patterns don't have captures
163
+ break;
164
+ case "Structure":
165
+ // Structure patterns may have element patterns with captures
166
+ collectStructurePatternCaptureNames(pattern.pattern, names);
167
+ break;
168
+ case "Meta":
169
+ collectMetaPatternCaptureNames(pattern.pattern, names);
170
+ break;
171
+ }
172
+ };
173
+
174
+ /**
175
+ * Collects capture names from structure patterns.
176
+ */
177
+ const collectStructurePatternCaptureNames = (pattern: StructurePattern, names: string[]): void => {
178
+ switch (pattern.type) {
179
+ case "Array":
180
+ if (pattern.pattern.variant === "Elements") {
181
+ collectPatternCaptureNames(pattern.pattern.pattern, names);
182
+ }
183
+ break;
184
+ case "Map":
185
+ if (pattern.pattern.variant === "Constraints") {
186
+ for (const constraint of pattern.pattern.constraints) {
187
+ // Constraints are tuples: [keyPattern, valuePattern]
188
+ collectPatternCaptureNames(constraint[0], names);
189
+ collectPatternCaptureNames(constraint[1], names);
190
+ }
191
+ }
192
+ break;
193
+ case "Tagged":
194
+ // Tagged patterns (Tag, Name, Regex variants) have a `pattern` property
195
+ if (pattern.pattern.variant !== "Any") {
196
+ collectPatternCaptureNames(pattern.pattern.pattern, names);
197
+ }
198
+ break;
199
+ }
200
+ };
201
+
202
+ /**
203
+ * Collects capture names from meta patterns.
204
+ */
205
+ const collectMetaPatternCaptureNames = (pattern: MetaPattern, names: string[]): void => {
206
+ switch (pattern.type) {
207
+ case "Capture":
208
+ if (!names.includes(pattern.pattern.name)) {
209
+ names.push(pattern.pattern.name);
210
+ }
211
+ collectPatternCaptureNames(pattern.pattern.pattern, names);
212
+ break;
213
+ case "And":
214
+ for (const p of pattern.pattern.patterns) {
215
+ collectPatternCaptureNames(p, names);
216
+ }
217
+ break;
218
+ case "Or":
219
+ for (const p of pattern.pattern.patterns) {
220
+ collectPatternCaptureNames(p, names);
221
+ }
222
+ break;
223
+ case "Not":
224
+ collectPatternCaptureNames(pattern.pattern.pattern, names);
225
+ break;
226
+ case "Repeat":
227
+ collectPatternCaptureNames(pattern.pattern.pattern, names);
228
+ break;
229
+ case "Search":
230
+ collectPatternCaptureNames(pattern.pattern.pattern, names);
231
+ break;
232
+ case "Sequence":
233
+ for (const p of pattern.pattern.patterns) {
234
+ collectPatternCaptureNames(p, names);
235
+ }
236
+ break;
237
+ case "Any":
238
+ // Any patterns don't have captures
239
+ break;
240
+ }
241
+ };
242
+
243
+ /**
244
+ * Compiles a pattern to VM bytecode.
245
+ */
246
+ const compilePatternToCode = (
247
+ pattern: Pattern,
248
+ code: Instr[],
249
+ literals: Pattern[],
250
+ captureNames: string[],
251
+ ): void => {
252
+ switch (pattern.kind) {
253
+ case "Value":
254
+ // Value patterns use MatchPredicate
255
+ literals.push(pattern);
256
+ code.push({ type: "MatchPredicate", literalIndex: literals.length - 1 });
257
+ break;
258
+ case "Structure":
259
+ // Structure patterns use MatchStructure
260
+ literals.push(pattern);
261
+ code.push({ type: "MatchStructure", literalIndex: literals.length - 1 });
262
+ break;
263
+ case "Meta":
264
+ compileMetaPattern(pattern.pattern, code, literals, captureNames);
265
+ break;
266
+ }
267
+ };
268
+
269
+ /**
270
+ * Compiles meta patterns to VM bytecode.
271
+ */
272
+ const compileMetaPattern = (
273
+ pattern: MetaPattern,
274
+ code: Instr[],
275
+ literals: Pattern[],
276
+ captureNames: string[],
277
+ ): void => {
278
+ switch (pattern.type) {
279
+ case "Any":
280
+ // Any matches everything - compile as predicate
281
+ literals.push({ kind: "Meta", pattern });
282
+ code.push({ type: "MatchPredicate", literalIndex: literals.length - 1 });
283
+ break;
284
+
285
+ case "And": {
286
+ // All patterns must match
287
+ for (const p of pattern.pattern.patterns) {
288
+ compilePatternToCode(p, code, literals, captureNames);
289
+ }
290
+ break;
291
+ }
292
+
293
+ case "Or": {
294
+ // Use splits to try each alternative
295
+ const patterns = pattern.pattern.patterns;
296
+ if (patterns.length === 0) break;
297
+ if (patterns.length === 1) {
298
+ compilePatternToCode(patterns[0], code, literals, captureNames);
299
+ break;
300
+ }
301
+
302
+ // Create split chain for alternatives
303
+ const jumpAddrs: number[] = [];
304
+ for (let i = 0; i < patterns.length - 1; i++) {
305
+ const splitAddr = code.length;
306
+ code.push({ type: "Split", a: 0, b: 0 }); // Placeholder
307
+
308
+ // First alternative starts right after split
309
+ (code[splitAddr] as { type: "Split"; a: number; b: number }).a = code.length;
310
+ compilePatternToCode(patterns[i], code, literals, captureNames);
311
+ jumpAddrs.push(code.length);
312
+ code.push({ type: "Jump", address: 0 }); // Jump to end, placeholder
313
+
314
+ // Second alternative address
315
+ (code[splitAddr] as { type: "Split"; a: number; b: number }).b = code.length;
316
+ }
317
+ // Last pattern
318
+ compilePatternToCode(patterns[patterns.length - 1], code, literals, captureNames);
319
+
320
+ // Fix up jump addresses
321
+ const endAddr = code.length;
322
+ for (const addr of jumpAddrs) {
323
+ (code[addr] as { type: "Jump"; address: number }).address = endAddr;
324
+ }
325
+ break;
326
+ }
327
+
328
+ case "Not": {
329
+ // Store the inner pattern and use NotMatch instruction
330
+ literals.push(pattern.pattern.pattern);
331
+ code.push({ type: "NotMatch", patternIndex: literals.length - 1 });
332
+ break;
333
+ }
334
+
335
+ case "Repeat": {
336
+ literals.push(pattern.pattern.pattern);
337
+ code.push({
338
+ type: "Repeat",
339
+ patternIndex: literals.length - 1,
340
+ quantifier: pattern.pattern.quantifier,
341
+ });
342
+ break;
343
+ }
344
+
345
+ case "Capture": {
346
+ const captureIndex = captureNames.indexOf(pattern.pattern.name);
347
+ code.push({ type: "CaptureStart", captureIndex });
348
+ compilePatternToCode(pattern.pattern.pattern, code, literals, captureNames);
349
+ code.push({ type: "CaptureEnd", captureIndex });
350
+ break;
351
+ }
352
+
353
+ case "Search": {
354
+ // Build capture map for the search pattern
355
+ const captureMap: [string, number][] = [];
356
+ const innerNames: string[] = [];
357
+ collectPatternCaptureNames(pattern.pattern.pattern, innerNames);
358
+ for (const name of innerNames) {
359
+ const idx = captureNames.indexOf(name);
360
+ if (idx >= 0) {
361
+ captureMap.push([name, idx]);
362
+ }
363
+ }
364
+
365
+ literals.push(pattern.pattern.pattern);
366
+ code.push({
367
+ type: "Search",
368
+ patternIndex: literals.length - 1,
369
+ captureMap,
370
+ });
371
+ break;
372
+ }
373
+
374
+ case "Sequence": {
375
+ const patterns = pattern.pattern.patterns;
376
+ if (patterns.length === 0) break;
377
+
378
+ compilePatternToCode(patterns[0], code, literals, captureNames);
379
+
380
+ for (let i = 1; i < patterns.length; i++) {
381
+ code.push({ type: "ExtendSequence" });
382
+ compilePatternToCode(patterns[i], code, literals, captureNames);
383
+ code.push({ type: "CombineSequence" });
384
+ }
385
+ break;
386
+ }
387
+ }
388
+ };
@@ -0,0 +1,56 @@
1
+ /**
2
+ * And pattern for dCBOR pattern matching.
3
+ * Matches if all contained patterns match.
4
+ *
5
+ * @module pattern/meta/and-pattern
6
+ */
7
+
8
+ import type { Cbor } from "@bcts/dcbor";
9
+ import type { Path } from "../../format";
10
+ import type { Pattern } from "../index";
11
+ import { matchPattern } from "../match-registry";
12
+
13
+ /**
14
+ * A pattern that matches if all contained patterns match.
15
+ */
16
+ export interface AndPattern {
17
+ readonly variant: "And";
18
+ readonly patterns: Pattern[];
19
+ }
20
+
21
+ /**
22
+ * Creates an AndPattern with the given patterns.
23
+ */
24
+ export const andPattern = (patterns: Pattern[]): AndPattern => ({
25
+ variant: "And",
26
+ patterns,
27
+ });
28
+
29
+ /**
30
+ * Tests if a CBOR value matches this and pattern.
31
+ * All patterns must match.
32
+ */
33
+ export const andPatternMatches = (pattern: AndPattern, haystack: Cbor): boolean => {
34
+ return pattern.patterns.every((p: Pattern) => matchPattern(p, haystack));
35
+ };
36
+
37
+ /**
38
+ * Returns paths to matching values.
39
+ */
40
+ export const andPatternPaths = (pattern: AndPattern, haystack: Cbor): Path[] => {
41
+ if (andPatternMatches(pattern, haystack)) {
42
+ return [[haystack]];
43
+ }
44
+ return [];
45
+ };
46
+
47
+ /**
48
+ * Formats an AndPattern as a string.
49
+ */
50
+ export const andPatternDisplay = (
51
+ pattern: AndPattern,
52
+ patternDisplay: (p: Pattern) => string,
53
+ ): string => {
54
+ const parts = pattern.patterns.map(patternDisplay);
55
+ return parts.join(" & ");
56
+ };
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Any pattern for dCBOR pattern matching.
3
+ * Always matches any CBOR value.
4
+ *
5
+ * @module pattern/meta/any-pattern
6
+ */
7
+
8
+ import type { Cbor } from "@bcts/dcbor";
9
+ import type { Path } from "../../format";
10
+
11
+ /**
12
+ * A pattern that always matches any CBOR value.
13
+ */
14
+ export interface AnyPattern {
15
+ readonly variant: "Any";
16
+ }
17
+
18
+ /**
19
+ * Creates an AnyPattern.
20
+ */
21
+ export const anyPattern = (): AnyPattern => ({ variant: "Any" });
22
+
23
+ /**
24
+ * Tests if a CBOR value matches this any pattern.
25
+ * Always returns true.
26
+ */
27
+ export const anyPatternMatches = (_pattern: AnyPattern, _haystack: Cbor): boolean => {
28
+ return true;
29
+ };
30
+
31
+ /**
32
+ * Returns paths to matching values.
33
+ */
34
+ export const anyPatternPaths = (_pattern: AnyPattern, haystack: Cbor): Path[] => {
35
+ return [[haystack]];
36
+ };
37
+
38
+ /**
39
+ * Formats an AnyPattern as a string.
40
+ */
41
+ export const anyPatternDisplay = (_pattern: AnyPattern): string => {
42
+ return "*";
43
+ };
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Capture pattern for dCBOR pattern matching.
3
+ * Captures matched values with a name.
4
+ *
5
+ * @module pattern/meta/capture-pattern
6
+ */
7
+
8
+ import type { Cbor } from "@bcts/dcbor";
9
+ import type { Path } from "../../format";
10
+ import type { Pattern } from "../index";
11
+ import { matchPattern } from "../match-registry";
12
+
13
+ /**
14
+ * A pattern that captures matched values with a name.
15
+ */
16
+ export interface CapturePattern {
17
+ readonly variant: "Capture";
18
+ readonly name: string;
19
+ readonly pattern: Pattern;
20
+ }
21
+
22
+ /**
23
+ * Creates a CapturePattern with the given name and inner pattern.
24
+ */
25
+ export const capturePattern = (name: string, pattern: Pattern): CapturePattern => ({
26
+ variant: "Capture",
27
+ name,
28
+ pattern,
29
+ });
30
+
31
+ /**
32
+ * Tests if a CBOR value matches this capture pattern.
33
+ * Capture itself doesn't affect matching - it delegates to inner pattern.
34
+ */
35
+ export const capturePatternMatches = (pattern: CapturePattern, haystack: Cbor): boolean => {
36
+ return matchPattern(pattern.pattern, haystack);
37
+ };
38
+
39
+ /**
40
+ * Returns paths to matching values.
41
+ */
42
+ export const capturePatternPaths = (pattern: CapturePattern, haystack: Cbor): Path[] => {
43
+ if (capturePatternMatches(pattern, haystack)) {
44
+ return [[haystack]];
45
+ }
46
+ return [];
47
+ };
48
+
49
+ /**
50
+ * Formats a CapturePattern as a string.
51
+ */
52
+ export const capturePatternDisplay = (
53
+ pattern: CapturePattern,
54
+ patternDisplay: (p: Pattern) => string,
55
+ ): string => {
56
+ return `@${pattern.name}(${patternDisplay(pattern.pattern)})`;
57
+ };