@bcts/envelope-pattern 1.0.0-alpha.12

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 (53) hide show
  1. package/LICENSE +48 -0
  2. package/README.md +13 -0
  3. package/dist/index.cjs +6781 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.cts +2628 -0
  6. package/dist/index.d.cts.map +1 -0
  7. package/dist/index.d.mts +2628 -0
  8. package/dist/index.d.mts.map +1 -0
  9. package/dist/index.iife.js +6781 -0
  10. package/dist/index.iife.js.map +1 -0
  11. package/dist/index.mjs +6545 -0
  12. package/dist/index.mjs.map +1 -0
  13. package/package.json +77 -0
  14. package/src/error.ts +262 -0
  15. package/src/format.ts +375 -0
  16. package/src/index.ts +27 -0
  17. package/src/parse/index.ts +923 -0
  18. package/src/parse/token.ts +906 -0
  19. package/src/parse/utils.ts +339 -0
  20. package/src/pattern/index.ts +719 -0
  21. package/src/pattern/leaf/array-pattern.ts +273 -0
  22. package/src/pattern/leaf/bool-pattern.ts +140 -0
  23. package/src/pattern/leaf/byte-string-pattern.ts +172 -0
  24. package/src/pattern/leaf/cbor-pattern.ts +355 -0
  25. package/src/pattern/leaf/date-pattern.ts +178 -0
  26. package/src/pattern/leaf/index.ts +280 -0
  27. package/src/pattern/leaf/known-value-pattern.ts +192 -0
  28. package/src/pattern/leaf/map-pattern.ts +152 -0
  29. package/src/pattern/leaf/null-pattern.ts +110 -0
  30. package/src/pattern/leaf/number-pattern.ts +248 -0
  31. package/src/pattern/leaf/tagged-pattern.ts +228 -0
  32. package/src/pattern/leaf/text-pattern.ts +165 -0
  33. package/src/pattern/matcher.ts +88 -0
  34. package/src/pattern/meta/and-pattern.ts +109 -0
  35. package/src/pattern/meta/any-pattern.ts +81 -0
  36. package/src/pattern/meta/capture-pattern.ts +111 -0
  37. package/src/pattern/meta/group-pattern.ts +110 -0
  38. package/src/pattern/meta/index.ts +269 -0
  39. package/src/pattern/meta/not-pattern.ts +91 -0
  40. package/src/pattern/meta/or-pattern.ts +146 -0
  41. package/src/pattern/meta/search-pattern.ts +201 -0
  42. package/src/pattern/meta/traverse-pattern.ts +146 -0
  43. package/src/pattern/structure/assertions-pattern.ts +244 -0
  44. package/src/pattern/structure/digest-pattern.ts +225 -0
  45. package/src/pattern/structure/index.ts +272 -0
  46. package/src/pattern/structure/leaf-structure-pattern.ts +85 -0
  47. package/src/pattern/structure/node-pattern.ts +188 -0
  48. package/src/pattern/structure/object-pattern.ts +149 -0
  49. package/src/pattern/structure/obscured-pattern.ts +159 -0
  50. package/src/pattern/structure/predicate-pattern.ts +151 -0
  51. package/src/pattern/structure/subject-pattern.ts +152 -0
  52. package/src/pattern/structure/wrapped-pattern.ts +195 -0
  53. package/src/pattern/vm.ts +1021 -0
@@ -0,0 +1,355 @@
1
+ /**
2
+ * @bcts/envelope-pattern - CBOR pattern matching
3
+ *
4
+ * This is a 1:1 TypeScript port of bc-envelope-pattern-rust cbor_pattern.rs
5
+ *
6
+ * @module envelope-pattern/pattern/leaf/cbor-pattern
7
+ */
8
+
9
+ import { Envelope } from "@bcts/envelope";
10
+ import type { Cbor } from "@bcts/dcbor";
11
+ import { cbor as toCbor, type CborInput } from "@bcts/dcbor";
12
+ import {
13
+ type Pattern as DCBORPattern,
14
+ patternPathsWithCaptures as dcborPatternPathsWithCaptures,
15
+ patternDisplay as dcborPatternDisplay,
16
+ } from "@bcts/dcbor-pattern";
17
+ import type { Path } from "../../format";
18
+ import type { Matcher } from "../matcher";
19
+ import { compileAsAtomic } from "../matcher";
20
+ import type { Instr } from "../vm";
21
+ import type { Pattern } from "../index";
22
+
23
+ // Forward declaration for Pattern factory
24
+ let createLeafCBORPattern: ((pattern: CBORPattern) => Pattern) | undefined;
25
+
26
+ export function registerCBORPatternFactory(factory: (pattern: CBORPattern) => Pattern): void {
27
+ createLeafCBORPattern = factory;
28
+ }
29
+
30
+ /**
31
+ * Pattern type for CBOR pattern matching.
32
+ *
33
+ * Corresponds to the Rust `CBORPattern` enum in cbor_pattern.rs
34
+ */
35
+ export type CBORPatternType =
36
+ | { readonly type: "Any" }
37
+ | { readonly type: "Value"; readonly cbor: Cbor }
38
+ | { readonly type: "Pattern"; readonly pattern: DCBORPattern };
39
+
40
+ /**
41
+ * Pattern for matching CBOR values with support for exact values and advanced pattern matching.
42
+ *
43
+ * Corresponds to the Rust `CBORPattern` enum in cbor_pattern.rs
44
+ */
45
+ export class CBORPattern implements Matcher {
46
+ readonly #pattern: CBORPatternType;
47
+
48
+ private constructor(pattern: CBORPatternType) {
49
+ this.#pattern = pattern;
50
+ }
51
+
52
+ /**
53
+ * Creates a new CBORPattern that matches any CBOR value.
54
+ */
55
+ static any(): CBORPattern {
56
+ return new CBORPattern({ type: "Any" });
57
+ }
58
+
59
+ /**
60
+ * Creates a new CBORPattern that matches a specific CBOR value.
61
+ */
62
+ static value(value: CborInput): CBORPattern {
63
+ return new CBORPattern({ type: "Value", cbor: toCbor(value) });
64
+ }
65
+
66
+ /**
67
+ * Creates a new CBORPattern that matches CBOR values using dcbor-pattern expressions.
68
+ */
69
+ static pattern(dcborPattern: DCBORPattern): CBORPattern {
70
+ return new CBORPattern({ type: "Pattern", pattern: dcborPattern });
71
+ }
72
+
73
+ /**
74
+ * Creates a new CBORPattern from a dcbor-pattern Pattern.
75
+ */
76
+ static fromDcborPattern(dcborPattern: DCBORPattern): CBORPattern {
77
+ return new CBORPattern({ type: "Pattern", pattern: dcborPattern });
78
+ }
79
+
80
+ /**
81
+ * Gets the pattern type.
82
+ */
83
+ get pattern(): CBORPatternType {
84
+ return this.#pattern;
85
+ }
86
+
87
+ /**
88
+ * Convert dcbor captures to envelope captures.
89
+ */
90
+ #convertDcborCapturesToEnvelopeCaptures(
91
+ dcborCaptures: Map<string, Cbor[][]>,
92
+ baseEnvelope: Envelope,
93
+ baseCbor: Cbor,
94
+ ): Map<string, Path[]> {
95
+ const envelopeCaptures = new Map<string, Path[]>();
96
+
97
+ for (const [captureName, dcborCapturePaths] of dcborCaptures) {
98
+ const envelopeCapturePaths: Path[] = dcborCapturePaths.map((dcborPath) =>
99
+ this.#convertDcborPathToEnvelopePath(dcborPath, baseEnvelope, baseCbor),
100
+ );
101
+ envelopeCaptures.set(captureName, envelopeCapturePaths);
102
+ }
103
+
104
+ return envelopeCaptures;
105
+ }
106
+
107
+ /**
108
+ * Convert a single dcbor path to an envelope path.
109
+ */
110
+ #convertDcborPathToEnvelopePath(
111
+ dcborPath: Cbor[],
112
+ baseEnvelope: Envelope,
113
+ baseCbor: Cbor,
114
+ ): Envelope[] {
115
+ const envelopePath: Envelope[] = [baseEnvelope];
116
+
117
+ // Skip first element if it matches the base envelope's CBOR content (compare by diagnostic)
118
+ const skipFirst =
119
+ dcborPath.length > 0 && dcborPath[0]?.toDiagnostic() === baseCbor.toDiagnostic();
120
+
121
+ const elementsToAdd = skipFirst ? dcborPath.slice(1) : dcborPath;
122
+
123
+ for (const cborElement of elementsToAdd) {
124
+ // Use newLeaf to create envelope from CBOR value
125
+ envelopePath.push(Envelope.newLeaf(cborElement));
126
+ }
127
+
128
+ return envelopePath;
129
+ }
130
+
131
+ /**
132
+ * Collect capture names from a dcbor pattern.
133
+ */
134
+ #collectDcborCaptureNames(dcborPattern: DCBORPattern, names: string[]): void {
135
+ // Parse the pattern string to extract capture names
136
+ const patternStr = dcborPatternDisplay(dcborPattern);
137
+
138
+ // Simple parsing to find @name( patterns
139
+ let i = 0;
140
+ while (i < patternStr.length) {
141
+ if (patternStr[i] === "@") {
142
+ i++;
143
+ let name = "";
144
+ // Collect characters until we hit '('
145
+ while (i < patternStr.length && patternStr[i] !== "(") {
146
+ name += patternStr[i];
147
+ i++;
148
+ }
149
+ if (name.length > 0 && !names.includes(name)) {
150
+ names.push(name);
151
+ }
152
+ } else {
153
+ i++;
154
+ }
155
+ }
156
+ }
157
+
158
+ pathsWithCaptures(haystack: Envelope): [Path[], Map<string, Path[]>] {
159
+ const envCase = haystack.case();
160
+
161
+ // Special case for KnownValue envelope
162
+ if (envCase.type === "knownValue") {
163
+ const knownValue = envCase.value;
164
+ const knownValueCbor = knownValue.taggedCbor();
165
+
166
+ switch (this.#pattern.type) {
167
+ case "Any":
168
+ return [[[haystack]], new Map<string, Path[]>()];
169
+ case "Value": {
170
+ // Compare using diagnostic representation
171
+ if (knownValueCbor.toDiagnostic() === this.#pattern.cbor.toDiagnostic()) {
172
+ return [[[haystack]], new Map<string, Path[]>()];
173
+ }
174
+ return [[], new Map<string, Path[]>()];
175
+ }
176
+ case "Pattern": {
177
+ const { paths: dcborPaths, captures: dcborCaptures } = dcborPatternPathsWithCaptures(
178
+ this.#pattern.pattern,
179
+ knownValueCbor,
180
+ );
181
+
182
+ if (dcborPaths.length > 0) {
183
+ const basePath: Path = [haystack];
184
+ const envelopePaths: Path[] = dcborPaths.map((dcborPath: Cbor[]) => {
185
+ const extendedPath = [...basePath];
186
+ // Skip the first element as it represents the root CBOR
187
+ for (let i = 1; i < dcborPath.length; i++) {
188
+ const elem = dcborPath[i];
189
+ if (elem !== undefined) {
190
+ extendedPath.push(Envelope.newLeaf(elem));
191
+ }
192
+ }
193
+ return extendedPath;
194
+ });
195
+
196
+ const envelopeCaptures = this.#convertDcborCapturesToEnvelopeCaptures(
197
+ dcborCaptures,
198
+ haystack,
199
+ knownValueCbor,
200
+ );
201
+ return [envelopePaths, envelopeCaptures];
202
+ }
203
+ return [[], new Map<string, Path[]>()];
204
+ }
205
+ }
206
+ }
207
+
208
+ // Standard case for CBOR leaf
209
+ const leafCbor = haystack.asLeaf();
210
+ if (leafCbor === undefined) {
211
+ return [[], new Map<string, Path[]>()];
212
+ }
213
+
214
+ switch (this.#pattern.type) {
215
+ case "Any":
216
+ return [[[haystack]], new Map<string, Path[]>()];
217
+
218
+ case "Value":
219
+ // Compare using diagnostic representation
220
+ if (leafCbor.toDiagnostic() === this.#pattern.cbor.toDiagnostic()) {
221
+ return [[[haystack]], new Map<string, Path[]>()];
222
+ }
223
+ return [[], new Map<string, Path[]>()];
224
+
225
+ case "Pattern": {
226
+ const { paths: dcborPaths, captures: dcborCaptures } = dcborPatternPathsWithCaptures(
227
+ this.#pattern.pattern,
228
+ leafCbor,
229
+ );
230
+
231
+ if (dcborPaths.length > 0) {
232
+ const basePath: Path = [haystack];
233
+
234
+ const envelopePaths: Path[] = dcborPaths.map((dcborPath: Cbor[]) => {
235
+ const extendedPath = [...basePath];
236
+ // Skip the first element only if it exactly matches our root CBOR
237
+ const skipFirst =
238
+ dcborPath.length > 0 && dcborPath[0]?.toDiagnostic() === leafCbor.toDiagnostic();
239
+
240
+ const elementsToAdd = skipFirst ? dcborPath.slice(1) : dcborPath;
241
+
242
+ for (const cborElement of elementsToAdd) {
243
+ extendedPath.push(Envelope.newLeaf(cborElement));
244
+ }
245
+ return extendedPath;
246
+ });
247
+
248
+ const envelopeCaptures = this.#convertDcborCapturesToEnvelopeCaptures(
249
+ dcborCaptures,
250
+ haystack,
251
+ leafCbor,
252
+ );
253
+ return [envelopePaths, envelopeCaptures];
254
+ }
255
+ return [[], new Map<string, Path[]>()];
256
+ }
257
+ }
258
+ }
259
+
260
+ paths(haystack: Envelope): Path[] {
261
+ return this.pathsWithCaptures(haystack)[0];
262
+ }
263
+
264
+ matches(haystack: Envelope): boolean {
265
+ return this.paths(haystack).length > 0;
266
+ }
267
+
268
+ compile(code: Instr[], literals: Pattern[], captures: string[]): void {
269
+ // Register any capture names from this CBOR pattern
270
+ if (this.#pattern.type === "Pattern") {
271
+ const captureNames: string[] = [];
272
+ this.#collectDcborCaptureNames(this.#pattern.pattern, captureNames);
273
+ for (const name of captureNames) {
274
+ if (!captures.includes(name)) {
275
+ captures.push(name);
276
+ }
277
+ }
278
+ }
279
+
280
+ if (createLeafCBORPattern === undefined) {
281
+ throw new Error("CBORPattern factory not registered");
282
+ }
283
+ compileAsAtomic(createLeafCBORPattern(this), code, literals, captures);
284
+ }
285
+
286
+ isComplex(): boolean {
287
+ return false;
288
+ }
289
+
290
+ toString(): string {
291
+ switch (this.#pattern.type) {
292
+ case "Any":
293
+ return "cbor";
294
+ case "Value":
295
+ return `cbor(${this.#pattern.cbor.toDiagnostic()})`;
296
+ case "Pattern":
297
+ return `cbor(/${dcborPatternDisplay(this.#pattern.pattern)}/)`;
298
+ }
299
+ }
300
+
301
+ /**
302
+ * Equality comparison.
303
+ */
304
+ equals(other: CBORPattern): boolean {
305
+ if (this.#pattern.type !== other.#pattern.type) {
306
+ return false;
307
+ }
308
+ switch (this.#pattern.type) {
309
+ case "Any":
310
+ return true;
311
+ case "Value":
312
+ // Compare using diagnostic representation
313
+ return (
314
+ this.#pattern.cbor.toDiagnostic() ===
315
+ (other.#pattern as { type: "Value"; cbor: Cbor }).cbor.toDiagnostic()
316
+ );
317
+ case "Pattern":
318
+ // Compare using display representation
319
+ return (
320
+ dcborPatternDisplay(this.#pattern.pattern) ===
321
+ dcborPatternDisplay(
322
+ (other.#pattern as { type: "Pattern"; pattern: DCBORPattern }).pattern,
323
+ )
324
+ );
325
+ }
326
+ }
327
+
328
+ /**
329
+ * Hash code for use in Maps/Sets.
330
+ */
331
+ hashCode(): number {
332
+ switch (this.#pattern.type) {
333
+ case "Any":
334
+ return 0;
335
+ case "Value":
336
+ // Simple hash based on diagnostic string
337
+ return simpleStringHash(this.#pattern.cbor.toDiagnostic());
338
+ case "Pattern":
339
+ // Simple hash based on display string
340
+ return simpleStringHash(dcborPatternDisplay(this.#pattern.pattern));
341
+ }
342
+ }
343
+ }
344
+
345
+ /**
346
+ * Simple string hash function for hashCode implementations.
347
+ */
348
+ function simpleStringHash(str: string): number {
349
+ let hash = 0;
350
+ for (const char of str) {
351
+ hash = (hash << 5) - hash + char.charCodeAt(0);
352
+ hash = hash & hash; // Convert to 32-bit integer
353
+ }
354
+ return hash;
355
+ }
@@ -0,0 +1,178 @@
1
+ /**
2
+ * @bcts/envelope-pattern - Date pattern matching
3
+ *
4
+ * This is a 1:1 TypeScript port of bc-envelope-pattern-rust date_pattern.rs
5
+ *
6
+ * @module envelope-pattern/pattern/leaf/date-pattern
7
+ */
8
+
9
+ import type { Envelope } from "@bcts/envelope";
10
+ import type { CborDate } from "@bcts/dcbor";
11
+ import {
12
+ type DatePattern as DCBORDatePattern,
13
+ datePatternAny,
14
+ datePatternValue,
15
+ datePatternRange,
16
+ datePatternEarliest,
17
+ datePatternLatest,
18
+ datePatternStringValue,
19
+ datePatternRegex,
20
+ datePatternPaths as dcborDatePatternPaths,
21
+ datePatternDisplay,
22
+ } from "@bcts/dcbor-pattern";
23
+ import type { Path } from "../../format";
24
+ import type { Matcher } from "../matcher";
25
+ import { compileAsAtomic } from "../matcher";
26
+ import type { Instr } from "../vm";
27
+ import type { Pattern } from "../index";
28
+
29
+ // Forward declaration for Pattern factory
30
+ let createLeafDatePattern: ((pattern: DatePattern) => Pattern) | undefined;
31
+
32
+ export function registerDatePatternFactory(factory: (pattern: DatePattern) => Pattern): void {
33
+ createLeafDatePattern = factory;
34
+ }
35
+
36
+ /**
37
+ * Pattern for matching date values.
38
+ *
39
+ * This is a wrapper around dcbor_pattern::DatePattern that provides
40
+ * envelope-specific integration.
41
+ *
42
+ * Corresponds to the Rust `DatePattern` struct in date_pattern.rs
43
+ */
44
+ export class DatePattern implements Matcher {
45
+ readonly #inner: DCBORDatePattern;
46
+
47
+ private constructor(inner: DCBORDatePattern) {
48
+ this.#inner = inner;
49
+ }
50
+
51
+ /**
52
+ * Creates a new DatePattern that matches any date.
53
+ */
54
+ static any(): DatePattern {
55
+ return new DatePattern(datePatternAny());
56
+ }
57
+
58
+ /**
59
+ * Creates a new DatePattern that matches the specific date.
60
+ */
61
+ static value(date: CborDate): DatePattern {
62
+ return new DatePattern(datePatternValue(date));
63
+ }
64
+
65
+ /**
66
+ * Creates a new DatePattern that matches dates within a range (inclusive).
67
+ */
68
+ static range(start: CborDate, end: CborDate): DatePattern {
69
+ return new DatePattern(datePatternRange(start, end));
70
+ }
71
+
72
+ /**
73
+ * Creates a new DatePattern that matches dates on or after the specified date.
74
+ */
75
+ static earliest(date: CborDate): DatePattern {
76
+ return new DatePattern(datePatternEarliest(date));
77
+ }
78
+
79
+ /**
80
+ * Creates a new DatePattern that matches dates on or before the specified date.
81
+ */
82
+ static latest(date: CborDate): DatePattern {
83
+ return new DatePattern(datePatternLatest(date));
84
+ }
85
+
86
+ /**
87
+ * Creates a new DatePattern that matches dates by their ISO-8601 string representation.
88
+ */
89
+ static string(isoString: string): DatePattern {
90
+ return new DatePattern(datePatternStringValue(isoString));
91
+ }
92
+
93
+ /**
94
+ * Creates a new DatePattern that matches dates whose ISO-8601 string representation
95
+ * matches the given regular expression.
96
+ */
97
+ static regex(pattern: RegExp): DatePattern {
98
+ return new DatePattern(datePatternRegex(pattern));
99
+ }
100
+
101
+ /**
102
+ * Creates a new DatePattern from a dcbor-pattern DatePattern.
103
+ */
104
+ static fromDcborPattern(dcborPattern: DCBORDatePattern): DatePattern {
105
+ return new DatePattern(dcborPattern);
106
+ }
107
+
108
+ /**
109
+ * Gets the underlying dcbor-pattern DatePattern.
110
+ */
111
+ get inner(): DCBORDatePattern {
112
+ return this.#inner;
113
+ }
114
+
115
+ pathsWithCaptures(haystack: Envelope): [Path[], Map<string, Path[]>] {
116
+ // For leaf envelopes, extract the CBOR and delegate to dcbor-pattern
117
+ const cbor = haystack.asLeaf();
118
+ if (cbor !== undefined) {
119
+ // Delegate to dcbor-pattern for CBOR matching
120
+ const dcborPaths = dcborDatePatternPaths(this.#inner, cbor);
121
+
122
+ // For simple leaf patterns, if dcbor-pattern found matches, return the envelope
123
+ if (dcborPaths.length > 0) {
124
+ const envelopePaths: Path[] = [[haystack]];
125
+ return [envelopePaths, new Map<string, Path[]>()];
126
+ }
127
+ }
128
+
129
+ return [[], new Map<string, Path[]>()];
130
+ }
131
+
132
+ paths(haystack: Envelope): Path[] {
133
+ return this.pathsWithCaptures(haystack)[0];
134
+ }
135
+
136
+ matches(haystack: Envelope): boolean {
137
+ return this.paths(haystack).length > 0;
138
+ }
139
+
140
+ compile(code: Instr[], literals: Pattern[], captures: string[]): void {
141
+ if (createLeafDatePattern === undefined) {
142
+ throw new Error("DatePattern factory not registered");
143
+ }
144
+ compileAsAtomic(createLeafDatePattern(this), code, literals, captures);
145
+ }
146
+
147
+ isComplex(): boolean {
148
+ return false;
149
+ }
150
+
151
+ toString(): string {
152
+ return datePatternDisplay(this.#inner);
153
+ }
154
+
155
+ /**
156
+ * Equality comparison.
157
+ */
158
+ equals(other: DatePattern): boolean {
159
+ if (this.#inner.variant !== other.#inner.variant) {
160
+ return false;
161
+ }
162
+ // Simplified equality - compare variant names
163
+ return JSON.stringify(this.#inner) === JSON.stringify(other.#inner);
164
+ }
165
+
166
+ /**
167
+ * Hash code for use in Maps/Sets.
168
+ */
169
+ hashCode(): number {
170
+ // Simple hash based on variant
171
+ let hash = 0;
172
+ const str = this.#inner.variant;
173
+ for (let i = 0; i < str.length; i++) {
174
+ hash = hash * 31 + str.charCodeAt(i);
175
+ }
176
+ return hash;
177
+ }
178
+ }