@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.
- package/LICENSE +48 -0
- package/README.md +13 -0
- package/dist/index.cjs +6781 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2628 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +2628 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.iife.js +6781 -0
- package/dist/index.iife.js.map +1 -0
- package/dist/index.mjs +6545 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +77 -0
- package/src/error.ts +262 -0
- package/src/format.ts +375 -0
- package/src/index.ts +27 -0
- package/src/parse/index.ts +923 -0
- package/src/parse/token.ts +906 -0
- package/src/parse/utils.ts +339 -0
- package/src/pattern/index.ts +719 -0
- package/src/pattern/leaf/array-pattern.ts +273 -0
- package/src/pattern/leaf/bool-pattern.ts +140 -0
- package/src/pattern/leaf/byte-string-pattern.ts +172 -0
- package/src/pattern/leaf/cbor-pattern.ts +355 -0
- package/src/pattern/leaf/date-pattern.ts +178 -0
- package/src/pattern/leaf/index.ts +280 -0
- package/src/pattern/leaf/known-value-pattern.ts +192 -0
- package/src/pattern/leaf/map-pattern.ts +152 -0
- package/src/pattern/leaf/null-pattern.ts +110 -0
- package/src/pattern/leaf/number-pattern.ts +248 -0
- package/src/pattern/leaf/tagged-pattern.ts +228 -0
- package/src/pattern/leaf/text-pattern.ts +165 -0
- package/src/pattern/matcher.ts +88 -0
- package/src/pattern/meta/and-pattern.ts +109 -0
- package/src/pattern/meta/any-pattern.ts +81 -0
- package/src/pattern/meta/capture-pattern.ts +111 -0
- package/src/pattern/meta/group-pattern.ts +110 -0
- package/src/pattern/meta/index.ts +269 -0
- package/src/pattern/meta/not-pattern.ts +91 -0
- package/src/pattern/meta/or-pattern.ts +146 -0
- package/src/pattern/meta/search-pattern.ts +201 -0
- package/src/pattern/meta/traverse-pattern.ts +146 -0
- package/src/pattern/structure/assertions-pattern.ts +244 -0
- package/src/pattern/structure/digest-pattern.ts +225 -0
- package/src/pattern/structure/index.ts +272 -0
- package/src/pattern/structure/leaf-structure-pattern.ts +85 -0
- package/src/pattern/structure/node-pattern.ts +188 -0
- package/src/pattern/structure/object-pattern.ts +149 -0
- package/src/pattern/structure/obscured-pattern.ts +159 -0
- package/src/pattern/structure/predicate-pattern.ts +151 -0
- package/src/pattern/structure/subject-pattern.ts +152 -0
- package/src/pattern/structure/wrapped-pattern.ts +195 -0
- package/src/pattern/vm.ts +1021 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @bcts/envelope-pattern - Array pattern matching
|
|
3
|
+
*
|
|
4
|
+
* This is a 1:1 TypeScript port of bc-envelope-pattern-rust array_pattern.rs
|
|
5
|
+
*
|
|
6
|
+
* @module envelope-pattern/pattern/leaf/array-pattern
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { Envelope } from "@bcts/envelope";
|
|
10
|
+
import { asCborArray, type Cbor } from "@bcts/dcbor";
|
|
11
|
+
import {
|
|
12
|
+
type Pattern as DCBORPattern,
|
|
13
|
+
Interval,
|
|
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 createLeafArrayPattern: ((pattern: ArrayPattern) => Pattern) | undefined;
|
|
25
|
+
|
|
26
|
+
export function registerArrayPatternFactory(factory: (pattern: ArrayPattern) => Pattern): void {
|
|
27
|
+
createLeafArrayPattern = factory;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Pattern for matching array values.
|
|
32
|
+
*
|
|
33
|
+
* Corresponds to the Rust `ArrayPattern` enum in array_pattern.rs
|
|
34
|
+
*/
|
|
35
|
+
export type ArrayPatternType =
|
|
36
|
+
| { readonly type: "Any" }
|
|
37
|
+
| { readonly type: "Interval"; readonly interval: Interval }
|
|
38
|
+
| { readonly type: "DCBORPattern"; readonly pattern: DCBORPattern }
|
|
39
|
+
| { readonly type: "WithPatterns"; readonly patterns: Pattern[] };
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Pattern for matching array values in envelope leaf nodes.
|
|
43
|
+
*
|
|
44
|
+
* Corresponds to the Rust `ArrayPattern` struct in array_pattern.rs
|
|
45
|
+
*/
|
|
46
|
+
export class ArrayPattern implements Matcher {
|
|
47
|
+
readonly #pattern: ArrayPatternType;
|
|
48
|
+
|
|
49
|
+
private constructor(pattern: ArrayPatternType) {
|
|
50
|
+
this.#pattern = pattern;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Creates a new ArrayPattern that matches any array.
|
|
55
|
+
*/
|
|
56
|
+
static any(): ArrayPattern {
|
|
57
|
+
return new ArrayPattern({ type: "Any" });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Creates a new ArrayPattern that matches arrays with a specific length.
|
|
62
|
+
*/
|
|
63
|
+
static count(count: number): ArrayPattern {
|
|
64
|
+
return new ArrayPattern({
|
|
65
|
+
type: "Interval",
|
|
66
|
+
interval: Interval.exactly(count),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Creates a new ArrayPattern that matches arrays within a length range.
|
|
72
|
+
*/
|
|
73
|
+
static interval(min: number, max?: number): ArrayPattern {
|
|
74
|
+
const interval = max !== undefined ? Interval.from(min, max) : Interval.atLeast(min);
|
|
75
|
+
return new ArrayPattern({ type: "Interval", interval });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Creates a new ArrayPattern from a dcbor-pattern.
|
|
80
|
+
*/
|
|
81
|
+
static fromDcborPattern(dcborPattern: DCBORPattern): ArrayPattern {
|
|
82
|
+
return new ArrayPattern({ type: "DCBORPattern", pattern: dcborPattern });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Creates a new ArrayPattern with envelope patterns for element matching.
|
|
87
|
+
*/
|
|
88
|
+
static withPatterns(patterns: Pattern[]): ArrayPattern {
|
|
89
|
+
return new ArrayPattern({ type: "WithPatterns", patterns });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Gets the pattern type.
|
|
94
|
+
*/
|
|
95
|
+
get pattern(): ArrayPatternType {
|
|
96
|
+
return this.#pattern;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
pathsWithCaptures(haystack: Envelope): [Path[], Map<string, Path[]>] {
|
|
100
|
+
// Try to extract CBOR from the envelope
|
|
101
|
+
const cbor = haystack.asLeaf();
|
|
102
|
+
if (cbor === undefined) {
|
|
103
|
+
return [[], new Map<string, Path[]>()];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Check if it's an array
|
|
107
|
+
const array = asCborArray(cbor);
|
|
108
|
+
if (array === undefined) {
|
|
109
|
+
return [[], new Map<string, Path[]>()];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
switch (this.#pattern.type) {
|
|
113
|
+
case "Any":
|
|
114
|
+
return [[[haystack]], new Map<string, Path[]>()];
|
|
115
|
+
|
|
116
|
+
case "Interval": {
|
|
117
|
+
const length = array.length;
|
|
118
|
+
if (this.#pattern.interval.contains(length)) {
|
|
119
|
+
return [[[haystack]], new Map<string, Path[]>()];
|
|
120
|
+
}
|
|
121
|
+
return [[], new Map<string, Path[]>()];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
case "DCBORPattern": {
|
|
125
|
+
// Delegate to dcbor-pattern for matching
|
|
126
|
+
const { paths: dcborPaths, captures: dcborCaptures } = dcborPatternPathsWithCaptures(
|
|
127
|
+
this.#pattern.pattern,
|
|
128
|
+
cbor,
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
if (dcborPaths.length > 0) {
|
|
132
|
+
// Convert dcbor paths to envelope paths
|
|
133
|
+
const envelopePaths: Path[] = dcborPaths.map((dcborPath: Cbor[]) => {
|
|
134
|
+
const envPath: Path = [haystack];
|
|
135
|
+
// Skip the first element (root) and convert rest to envelopes
|
|
136
|
+
for (let i = 1; i < dcborPath.length; i++) {
|
|
137
|
+
const elem = dcborPath[i];
|
|
138
|
+
if (elem !== undefined) {
|
|
139
|
+
envPath.push(Envelope.newLeaf(elem));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return envPath;
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// Convert dcbor captures to envelope captures
|
|
146
|
+
const envelopeCaptures = new Map<string, Path[]>();
|
|
147
|
+
for (const [name, capturePaths] of dcborCaptures) {
|
|
148
|
+
const envCapturePaths: Path[] = capturePaths.map((dcborPath: Cbor[]) => {
|
|
149
|
+
const envPath: Path = [haystack];
|
|
150
|
+
for (let i = 1; i < dcborPath.length; i++) {
|
|
151
|
+
const elem = dcborPath[i];
|
|
152
|
+
if (elem !== undefined) {
|
|
153
|
+
envPath.push(Envelope.newLeaf(elem));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return envPath;
|
|
157
|
+
});
|
|
158
|
+
envelopeCaptures.set(name, envCapturePaths);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return [envelopePaths, envelopeCaptures];
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return [[], new Map<string, Path[]>()];
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
case "WithPatterns":
|
|
168
|
+
// For envelope patterns, match if array length equals patterns count
|
|
169
|
+
// Full element-by-element matching would require additional implementation
|
|
170
|
+
if (array.length === this.#pattern.patterns.length) {
|
|
171
|
+
return [[[haystack]], new Map<string, Path[]>()];
|
|
172
|
+
}
|
|
173
|
+
return [[], new Map<string, Path[]>()];
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
paths(haystack: Envelope): Path[] {
|
|
178
|
+
return this.pathsWithCaptures(haystack)[0];
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
matches(haystack: Envelope): boolean {
|
|
182
|
+
return this.paths(haystack).length > 0;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
compile(code: Instr[], literals: Pattern[], captures: string[]): void {
|
|
186
|
+
if (createLeafArrayPattern === undefined) {
|
|
187
|
+
throw new Error("ArrayPattern factory not registered");
|
|
188
|
+
}
|
|
189
|
+
compileAsAtomic(createLeafArrayPattern(this), code, literals, captures);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
isComplex(): boolean {
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
toString(): string {
|
|
197
|
+
switch (this.#pattern.type) {
|
|
198
|
+
case "Any":
|
|
199
|
+
return "[*]";
|
|
200
|
+
case "Interval":
|
|
201
|
+
return `[{${this.#pattern.interval.toString()}}]`;
|
|
202
|
+
case "DCBORPattern":
|
|
203
|
+
return dcborPatternDisplay(this.#pattern.pattern);
|
|
204
|
+
case "WithPatterns":
|
|
205
|
+
return `[${this.#pattern.patterns.map(String).join(", ")}]`;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Equality comparison.
|
|
211
|
+
*/
|
|
212
|
+
equals(other: ArrayPattern): boolean {
|
|
213
|
+
if (this.#pattern.type !== other.#pattern.type) {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
switch (this.#pattern.type) {
|
|
217
|
+
case "Any":
|
|
218
|
+
return true;
|
|
219
|
+
case "Interval":
|
|
220
|
+
return this.#pattern.interval.equals(
|
|
221
|
+
(other.#pattern as { type: "Interval"; interval: Interval }).interval,
|
|
222
|
+
);
|
|
223
|
+
case "DCBORPattern":
|
|
224
|
+
// Compare using display representation
|
|
225
|
+
return (
|
|
226
|
+
dcborPatternDisplay(this.#pattern.pattern) ===
|
|
227
|
+
dcborPatternDisplay(
|
|
228
|
+
(other.#pattern as { type: "DCBORPattern"; pattern: DCBORPattern }).pattern,
|
|
229
|
+
)
|
|
230
|
+
);
|
|
231
|
+
case "WithPatterns": {
|
|
232
|
+
const otherPatterns = (other.#pattern as { type: "WithPatterns"; patterns: Pattern[] })
|
|
233
|
+
.patterns;
|
|
234
|
+
if (this.#pattern.patterns.length !== otherPatterns.length) return false;
|
|
235
|
+
for (let i = 0; i < this.#pattern.patterns.length; i++) {
|
|
236
|
+
if (this.#pattern.patterns[i] !== otherPatterns[i]) return false;
|
|
237
|
+
}
|
|
238
|
+
return true;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Hash code for use in Maps/Sets.
|
|
245
|
+
*/
|
|
246
|
+
hashCode(): number {
|
|
247
|
+
switch (this.#pattern.type) {
|
|
248
|
+
case "Any":
|
|
249
|
+
return 0;
|
|
250
|
+
case "Interval":
|
|
251
|
+
// Simple hash based on min/max
|
|
252
|
+
return this.#pattern.interval.min() * 31 + (this.#pattern.interval.max() ?? 0);
|
|
253
|
+
case "DCBORPattern":
|
|
254
|
+
// Simple hash based on display string
|
|
255
|
+
return simpleStringHash(dcborPatternDisplay(this.#pattern.pattern));
|
|
256
|
+
case "WithPatterns":
|
|
257
|
+
return this.#pattern.patterns.length;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Simple string hash function for hashCode implementations.
|
|
264
|
+
*/
|
|
265
|
+
function simpleStringHash(str: string): number {
|
|
266
|
+
let hash = 0;
|
|
267
|
+
for (let i = 0; i < str.length; i++) {
|
|
268
|
+
const char = str.charCodeAt(i);
|
|
269
|
+
hash = (hash << 5) - hash + char;
|
|
270
|
+
hash = hash & hash; // Convert to 32-bit integer
|
|
271
|
+
}
|
|
272
|
+
return hash;
|
|
273
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @bcts/envelope-pattern - Boolean pattern matching
|
|
3
|
+
*
|
|
4
|
+
* This is a 1:1 TypeScript port of bc-envelope-pattern-rust bool_pattern.rs
|
|
5
|
+
*
|
|
6
|
+
* @module envelope-pattern/pattern/leaf/bool-pattern
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Envelope } from "@bcts/envelope";
|
|
10
|
+
import {
|
|
11
|
+
type BoolPattern as DCBORBoolPattern,
|
|
12
|
+
boolPatternAny,
|
|
13
|
+
boolPatternValue,
|
|
14
|
+
boolPatternPaths as dcborBoolPatternPaths,
|
|
15
|
+
boolPatternDisplay,
|
|
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 createLeafBoolPattern: ((pattern: BoolPattern) => Pattern) | undefined;
|
|
25
|
+
|
|
26
|
+
export function registerBoolPatternFactory(factory: (pattern: BoolPattern) => Pattern): void {
|
|
27
|
+
createLeafBoolPattern = factory;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Pattern for matching boolean values.
|
|
32
|
+
*
|
|
33
|
+
* This is a wrapper around dcbor_pattern::BoolPattern that provides
|
|
34
|
+
* envelope-specific integration.
|
|
35
|
+
*
|
|
36
|
+
* Corresponds to the Rust `BoolPattern` struct in bool_pattern.rs
|
|
37
|
+
*/
|
|
38
|
+
export class BoolPattern implements Matcher {
|
|
39
|
+
readonly #inner: DCBORBoolPattern;
|
|
40
|
+
|
|
41
|
+
private constructor(inner: DCBORBoolPattern) {
|
|
42
|
+
this.#inner = inner;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Creates a new BoolPattern that matches any boolean value.
|
|
47
|
+
*/
|
|
48
|
+
static any(): BoolPattern {
|
|
49
|
+
return new BoolPattern(boolPatternAny());
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Creates a new BoolPattern that matches the specific boolean value.
|
|
54
|
+
*/
|
|
55
|
+
static value(value: boolean): BoolPattern {
|
|
56
|
+
return new BoolPattern(boolPatternValue(value));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Creates a new BoolPattern from a dcbor-pattern BoolPattern.
|
|
61
|
+
*/
|
|
62
|
+
static fromDcborPattern(dcborPattern: DCBORBoolPattern): BoolPattern {
|
|
63
|
+
return new BoolPattern(dcborPattern);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Gets the underlying dcbor-pattern BoolPattern.
|
|
68
|
+
*/
|
|
69
|
+
get inner(): DCBORBoolPattern {
|
|
70
|
+
return this.#inner;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
pathsWithCaptures(haystack: Envelope): [Path[], Map<string, Path[]>] {
|
|
74
|
+
// For leaf envelopes, extract the CBOR and delegate to dcbor-pattern
|
|
75
|
+
const cbor = haystack.asLeaf();
|
|
76
|
+
if (cbor !== undefined) {
|
|
77
|
+
// Delegate to dcbor-pattern for CBOR matching
|
|
78
|
+
const dcborPaths = dcborBoolPatternPaths(this.#inner, cbor);
|
|
79
|
+
|
|
80
|
+
// For simple leaf patterns, if dcbor-pattern found matches, return the envelope
|
|
81
|
+
if (dcborPaths.length > 0) {
|
|
82
|
+
const envelopePaths: Path[] = [[haystack]];
|
|
83
|
+
const envelopeCaptures = new Map<string, Path[]>();
|
|
84
|
+
return [envelopePaths, envelopeCaptures];
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Not a leaf envelope or no match
|
|
89
|
+
return [[], new Map<string, Path[]>()];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
paths(haystack: Envelope): Path[] {
|
|
93
|
+
return this.pathsWithCaptures(haystack)[0];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
matches(haystack: Envelope): boolean {
|
|
97
|
+
return this.paths(haystack).length > 0;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
compile(code: Instr[], literals: Pattern[], captures: string[]): void {
|
|
101
|
+
if (createLeafBoolPattern === undefined) {
|
|
102
|
+
throw new Error("BoolPattern factory not registered");
|
|
103
|
+
}
|
|
104
|
+
compileAsAtomic(createLeafBoolPattern(this), code, literals, captures);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
isComplex(): boolean {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
toString(): string {
|
|
112
|
+
return boolPatternDisplay(this.#inner);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Equality comparison.
|
|
117
|
+
*/
|
|
118
|
+
equals(other: BoolPattern): boolean {
|
|
119
|
+
// Compare by variant and value
|
|
120
|
+
if (this.#inner.variant !== other.#inner.variant) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
if (this.#inner.variant === "Value" && other.#inner.variant === "Value") {
|
|
124
|
+
return this.#inner.value === other.#inner.value;
|
|
125
|
+
}
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Hash code for use in Maps/Sets.
|
|
131
|
+
*/
|
|
132
|
+
hashCode(): number {
|
|
133
|
+
// Simple hash based on variant and value
|
|
134
|
+
let hash = this.#inner.variant === "Any" ? 0 : 1;
|
|
135
|
+
if (this.#inner.variant === "Value") {
|
|
136
|
+
hash = hash * 31 + (this.#inner.value ? 1 : 0);
|
|
137
|
+
}
|
|
138
|
+
return hash;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @bcts/envelope-pattern - Byte string pattern matching
|
|
3
|
+
*
|
|
4
|
+
* This is a 1:1 TypeScript port of bc-envelope-pattern-rust byte_string_pattern.rs
|
|
5
|
+
*
|
|
6
|
+
* @module envelope-pattern/pattern/leaf/byte-string-pattern
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Envelope } from "@bcts/envelope";
|
|
10
|
+
import {
|
|
11
|
+
type ByteStringPattern as DCBORByteStringPattern,
|
|
12
|
+
byteStringPatternAny,
|
|
13
|
+
byteStringPatternValue,
|
|
14
|
+
byteStringPatternBinaryRegex,
|
|
15
|
+
byteStringPatternPaths as dcborByteStringPatternPaths,
|
|
16
|
+
byteStringPatternDisplay,
|
|
17
|
+
} from "@bcts/dcbor-pattern";
|
|
18
|
+
import type { Path } from "../../format";
|
|
19
|
+
import type { Matcher } from "../matcher";
|
|
20
|
+
import { compileAsAtomic } from "../matcher";
|
|
21
|
+
import type { Instr } from "../vm";
|
|
22
|
+
import type { Pattern } from "../index";
|
|
23
|
+
|
|
24
|
+
// Forward declaration for Pattern factory
|
|
25
|
+
let createLeafByteStringPattern: ((pattern: ByteStringPattern) => Pattern) | undefined;
|
|
26
|
+
|
|
27
|
+
export function registerByteStringPatternFactory(
|
|
28
|
+
factory: (pattern: ByteStringPattern) => Pattern,
|
|
29
|
+
): void {
|
|
30
|
+
createLeafByteStringPattern = factory;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Pattern for matching byte string values.
|
|
35
|
+
*
|
|
36
|
+
* This is a wrapper around dcbor_pattern::ByteStringPattern that provides
|
|
37
|
+
* envelope-specific integration.
|
|
38
|
+
*
|
|
39
|
+
* Corresponds to the Rust `ByteStringPattern` struct in byte_string_pattern.rs
|
|
40
|
+
*/
|
|
41
|
+
export class ByteStringPattern implements Matcher {
|
|
42
|
+
readonly #inner: DCBORByteStringPattern;
|
|
43
|
+
|
|
44
|
+
private constructor(inner: DCBORByteStringPattern) {
|
|
45
|
+
this.#inner = inner;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Creates a new ByteStringPattern that matches any byte string.
|
|
50
|
+
*/
|
|
51
|
+
static any(): ByteStringPattern {
|
|
52
|
+
return new ByteStringPattern(byteStringPatternAny());
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Creates a new ByteStringPattern that matches the specific byte string.
|
|
57
|
+
*/
|
|
58
|
+
static value(value: Uint8Array): ByteStringPattern {
|
|
59
|
+
return new ByteStringPattern(byteStringPatternValue(value));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Creates a new ByteStringPattern that matches byte strings matching the binary regex.
|
|
64
|
+
*/
|
|
65
|
+
static regex(pattern: RegExp): ByteStringPattern {
|
|
66
|
+
return new ByteStringPattern(byteStringPatternBinaryRegex(pattern));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Creates a new ByteStringPattern from a dcbor-pattern ByteStringPattern.
|
|
71
|
+
*/
|
|
72
|
+
static fromDcborPattern(dcborPattern: DCBORByteStringPattern): ByteStringPattern {
|
|
73
|
+
return new ByteStringPattern(dcborPattern);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Gets the underlying dcbor-pattern ByteStringPattern.
|
|
78
|
+
*/
|
|
79
|
+
get inner(): DCBORByteStringPattern {
|
|
80
|
+
return this.#inner;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
pathsWithCaptures(haystack: Envelope): [Path[], Map<string, Path[]>] {
|
|
84
|
+
// For leaf envelopes, extract the CBOR and delegate to dcbor-pattern
|
|
85
|
+
const cbor = haystack.asLeaf();
|
|
86
|
+
if (cbor !== undefined) {
|
|
87
|
+
// Delegate to dcbor-pattern for CBOR matching
|
|
88
|
+
const dcborPaths = dcborByteStringPatternPaths(this.#inner, cbor);
|
|
89
|
+
|
|
90
|
+
// For simple leaf patterns, if dcbor-pattern found matches, return the envelope
|
|
91
|
+
if (dcborPaths.length > 0) {
|
|
92
|
+
const envelopePaths: Path[] = [[haystack]];
|
|
93
|
+
return [envelopePaths, new Map<string, Path[]>()];
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return [[], new Map<string, Path[]>()];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
paths(haystack: Envelope): Path[] {
|
|
101
|
+
return this.pathsWithCaptures(haystack)[0];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
matches(haystack: Envelope): boolean {
|
|
105
|
+
return this.paths(haystack).length > 0;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
compile(code: Instr[], literals: Pattern[], captures: string[]): void {
|
|
109
|
+
if (createLeafByteStringPattern === undefined) {
|
|
110
|
+
throw new Error("ByteStringPattern factory not registered");
|
|
111
|
+
}
|
|
112
|
+
compileAsAtomic(createLeafByteStringPattern(this), code, literals, captures);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
isComplex(): boolean {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
toString(): string {
|
|
120
|
+
return byteStringPatternDisplay(this.#inner);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Equality comparison.
|
|
125
|
+
*/
|
|
126
|
+
equals(other: ByteStringPattern): boolean {
|
|
127
|
+
if (this.#inner.variant !== other.#inner.variant) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
switch (this.#inner.variant) {
|
|
131
|
+
case "Any":
|
|
132
|
+
return true;
|
|
133
|
+
case "Value": {
|
|
134
|
+
const a = (this.#inner as { value: Uint8Array }).value;
|
|
135
|
+
const b = (other.#inner as { value: Uint8Array }).value;
|
|
136
|
+
if (a.length !== b.length) return false;
|
|
137
|
+
for (let i = 0; i < a.length; i++) {
|
|
138
|
+
if (a[i] !== b[i]) return false;
|
|
139
|
+
}
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
case "BinaryRegex":
|
|
143
|
+
return (
|
|
144
|
+
(this.#inner as { pattern: RegExp }).pattern.source ===
|
|
145
|
+
(other.#inner as { pattern: RegExp }).pattern.source
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Hash code for use in Maps/Sets.
|
|
152
|
+
*/
|
|
153
|
+
hashCode(): number {
|
|
154
|
+
let hash = 0;
|
|
155
|
+
switch (this.#inner.variant) {
|
|
156
|
+
case "Any":
|
|
157
|
+
hash = 1;
|
|
158
|
+
break;
|
|
159
|
+
case "Value": {
|
|
160
|
+
const val = (this.#inner as { value: Uint8Array }).value;
|
|
161
|
+
for (const byte of val) {
|
|
162
|
+
hash = hash * 31 + byte;
|
|
163
|
+
}
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
case "BinaryRegex":
|
|
167
|
+
hash = 3 * 31 + (this.#inner as { pattern: RegExp }).pattern.source.length;
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
return hash;
|
|
171
|
+
}
|
|
172
|
+
}
|