@bcts/envelope-pattern 1.0.0-alpha.16 → 1.0.0-alpha.18
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/README.md +1 -1
- package/dist/index.cjs +1992 -1714
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +147 -31
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +147 -31
- package/dist/index.d.mts.map +1 -1
- package/dist/index.iife.js +1984 -1707
- package/dist/index.iife.js.map +1 -1
- package/dist/index.mjs +1966 -1698
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -9
- package/src/format.ts +32 -13
- package/src/parse/index.ts +138 -5
- package/src/parse/token.ts +59 -58
- package/src/pattern/index.ts +110 -2
- package/src/pattern/leaf/array-pattern.ts +26 -26
- package/src/pattern/leaf/bool-pattern.ts +12 -12
- package/src/pattern/leaf/byte-string-pattern.ts +15 -15
- package/src/pattern/leaf/cbor-pattern.ts +31 -31
- package/src/pattern/leaf/date-pattern.ts +9 -9
- package/src/pattern/leaf/index.ts +1 -2
- package/src/pattern/leaf/known-value-pattern.ts +21 -20
- package/src/pattern/leaf/map-pattern.ts +14 -14
- package/src/pattern/leaf/null-pattern.ts +8 -8
- package/src/pattern/leaf/number-pattern.ts +20 -20
- package/src/pattern/leaf/tagged-pattern.ts +20 -20
- package/src/pattern/leaf/text-pattern.ts +14 -14
- package/src/pattern/matcher.ts +88 -3
- package/src/pattern/meta/and-pattern.ts +19 -18
- package/src/pattern/meta/capture-pattern.ts +16 -17
- package/src/pattern/meta/group-pattern.ts +20 -17
- package/src/pattern/meta/not-pattern.ts +9 -8
- package/src/pattern/meta/or-pattern.ts +30 -25
- package/src/pattern/meta/search-pattern.ts +17 -17
- package/src/pattern/meta/traverse-pattern.ts +42 -18
- package/src/pattern/structure/assertions-pattern.ts +31 -32
- package/src/pattern/structure/digest-pattern.ts +23 -23
- package/src/pattern/structure/index.ts +1 -0
- package/src/pattern/structure/node-pattern.ts +17 -17
- package/src/pattern/structure/object-pattern.ts +14 -15
- package/src/pattern/structure/obscured-pattern.ts +7 -7
- package/src/pattern/structure/predicate-pattern.ts +14 -15
- package/src/pattern/structure/subject-pattern.ts +16 -17
- package/src/pattern/structure/wrapped-pattern.ts +40 -19
- package/src/pattern/vm.ts +12 -11
|
@@ -8,9 +8,10 @@
|
|
|
8
8
|
|
|
9
9
|
import type { Envelope } from "@bcts/envelope";
|
|
10
10
|
import type { Path } from "../../format";
|
|
11
|
-
import
|
|
11
|
+
import { dispatchPaths, dispatchPatternToString } from "../matcher";
|
|
12
12
|
import type { Instr } from "../vm";
|
|
13
13
|
import type { Pattern } from "../index";
|
|
14
|
+
import type { Matcher } from "../matcher";
|
|
14
15
|
|
|
15
16
|
// Forward declaration for Pattern factory (used for late binding)
|
|
16
17
|
export let createMetaSearchPattern: ((pattern: SearchPattern) => Pattern) | undefined;
|
|
@@ -25,10 +26,10 @@ export function registerSearchPatternFactory(factory: (pattern: SearchPattern) =
|
|
|
25
26
|
* Corresponds to the Rust `SearchPattern` struct in search_pattern.rs
|
|
26
27
|
*/
|
|
27
28
|
export class SearchPattern implements Matcher {
|
|
28
|
-
readonly
|
|
29
|
+
private readonly _pattern: Pattern;
|
|
29
30
|
|
|
30
31
|
private constructor(pattern: Pattern) {
|
|
31
|
-
this
|
|
32
|
+
this._pattern = pattern;
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
/**
|
|
@@ -42,20 +43,19 @@ export class SearchPattern implements Matcher {
|
|
|
42
43
|
* Gets the inner pattern.
|
|
43
44
|
*/
|
|
44
45
|
pattern(): Pattern {
|
|
45
|
-
return this
|
|
46
|
+
return this._pattern;
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
pathsWithCaptures(haystack: Envelope): [Path[], Map<string, Path[]>] {
|
|
49
50
|
const resultPaths: Path[] = [];
|
|
50
|
-
const matcher = this.#pattern as unknown as Matcher;
|
|
51
51
|
|
|
52
52
|
// Walk the envelope tree
|
|
53
|
-
this
|
|
53
|
+
this._walkEnvelope(haystack, [], (currentEnvelope, pathToCurrent) => {
|
|
54
54
|
// Create the path to this node
|
|
55
55
|
const newPath: Envelope[] = [...pathToCurrent, currentEnvelope];
|
|
56
56
|
|
|
57
57
|
// Test the pattern against this node
|
|
58
|
-
const patternPaths =
|
|
58
|
+
const patternPaths = dispatchPaths(this._pattern, currentEnvelope);
|
|
59
59
|
|
|
60
60
|
// If the pattern matches, emit the full paths
|
|
61
61
|
for (const patternPath of patternPaths) {
|
|
@@ -92,7 +92,7 @@ export class SearchPattern implements Matcher {
|
|
|
92
92
|
/**
|
|
93
93
|
* Walk the envelope tree recursively.
|
|
94
94
|
*/
|
|
95
|
-
|
|
95
|
+
private _walkEnvelope(
|
|
96
96
|
envelope: Envelope,
|
|
97
97
|
pathToCurrent: Envelope[],
|
|
98
98
|
visitor: (envelope: Envelope, path: Envelope[]) => void,
|
|
@@ -106,24 +106,24 @@ export class SearchPattern implements Matcher {
|
|
|
106
106
|
|
|
107
107
|
// Walk subject if it's different from this envelope
|
|
108
108
|
if (!subject.digest().equals(envelope.digest())) {
|
|
109
|
-
this
|
|
109
|
+
this._walkEnvelope(subject, newPath, visitor);
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
// Walk assertions
|
|
113
113
|
for (const assertion of envelope.assertions()) {
|
|
114
|
-
this
|
|
114
|
+
this._walkEnvelope(assertion, newPath, visitor);
|
|
115
115
|
|
|
116
116
|
// Walk predicate and object if available
|
|
117
117
|
const predicate = assertion.asPredicate?.();
|
|
118
118
|
if (predicate !== undefined) {
|
|
119
119
|
const assertionPath = [...newPath, assertion];
|
|
120
|
-
this
|
|
120
|
+
this._walkEnvelope(predicate, assertionPath, visitor);
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
const object = assertion.asObject?.();
|
|
124
124
|
if (object !== undefined) {
|
|
125
125
|
const assertionPath = [...newPath, assertion];
|
|
126
|
-
this
|
|
126
|
+
this._walkEnvelope(object, assertionPath, visitor);
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
129
|
|
|
@@ -131,7 +131,7 @@ export class SearchPattern implements Matcher {
|
|
|
131
131
|
if (subject.isWrapped()) {
|
|
132
132
|
const unwrapped = subject.tryUnwrap?.();
|
|
133
133
|
if (unwrapped !== undefined) {
|
|
134
|
-
this
|
|
134
|
+
this._walkEnvelope(unwrapped, newPath, visitor);
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
}
|
|
@@ -146,11 +146,11 @@ export class SearchPattern implements Matcher {
|
|
|
146
146
|
|
|
147
147
|
compile(code: Instr[], literals: Pattern[], captures: string[]): void {
|
|
148
148
|
const idx = literals.length;
|
|
149
|
-
literals.push(this
|
|
149
|
+
literals.push(this._pattern);
|
|
150
150
|
|
|
151
151
|
// Collect capture names from inner pattern
|
|
152
152
|
const innerNames: string[] = [];
|
|
153
|
-
collectCaptureNames(this
|
|
153
|
+
collectCaptureNames(this._pattern, innerNames);
|
|
154
154
|
|
|
155
155
|
const captureMap: [string, number][] = [];
|
|
156
156
|
for (const name of innerNames) {
|
|
@@ -170,14 +170,14 @@ export class SearchPattern implements Matcher {
|
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
toString(): string {
|
|
173
|
-
return `search(${(this
|
|
173
|
+
return `search(${dispatchPatternToString(this._pattern)})`;
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
/**
|
|
177
177
|
* Equality comparison.
|
|
178
178
|
*/
|
|
179
179
|
equals(other: SearchPattern): boolean {
|
|
180
|
-
return this
|
|
180
|
+
return this._pattern === other._pattern;
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
/**
|
|
@@ -21,18 +21,37 @@ export function registerTraversePatternFactory(
|
|
|
21
21
|
createMetaTraversePattern = factory;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
// Late-binding dispatch functions to avoid circular dependency with Pattern
|
|
25
|
+
let _patternPathsWithCaptures:
|
|
26
|
+
| ((pattern: Pattern, haystack: Envelope) => [Path[], Map<string, Path[]>])
|
|
27
|
+
| undefined;
|
|
28
|
+
let _patternCompile:
|
|
29
|
+
| ((pattern: Pattern, code: Instr[], literals: Pattern[], captures: string[]) => void)
|
|
30
|
+
| undefined;
|
|
31
|
+
let _patternIsComplex: ((pattern: Pattern) => boolean) | undefined;
|
|
32
|
+
|
|
33
|
+
export function registerTraverseDispatchFunctions(
|
|
34
|
+
pathsWithCaptures: (pattern: Pattern, haystack: Envelope) => [Path[], Map<string, Path[]>],
|
|
35
|
+
compile: (pattern: Pattern, code: Instr[], literals: Pattern[], captures: string[]) => void,
|
|
36
|
+
isComplex: (pattern: Pattern) => boolean,
|
|
37
|
+
): void {
|
|
38
|
+
_patternPathsWithCaptures = pathsWithCaptures;
|
|
39
|
+
_patternCompile = compile;
|
|
40
|
+
_patternIsComplex = isComplex;
|
|
41
|
+
}
|
|
42
|
+
|
|
24
43
|
/**
|
|
25
44
|
* A pattern that matches a traversal order of patterns.
|
|
26
45
|
*
|
|
27
46
|
* Corresponds to the Rust `TraversePattern` struct in traverse_pattern.rs
|
|
28
47
|
*/
|
|
29
48
|
export class TraversePattern implements Matcher {
|
|
30
|
-
readonly
|
|
31
|
-
readonly
|
|
49
|
+
private readonly _first: Pattern;
|
|
50
|
+
private readonly _rest: TraversePattern | undefined;
|
|
32
51
|
|
|
33
52
|
private constructor(first: Pattern, rest: TraversePattern | undefined) {
|
|
34
|
-
this
|
|
35
|
-
this
|
|
53
|
+
this._first = first;
|
|
54
|
+
this._rest = rest;
|
|
36
55
|
}
|
|
37
56
|
|
|
38
57
|
/**
|
|
@@ -53,19 +72,21 @@ export class TraversePattern implements Matcher {
|
|
|
53
72
|
* Gets all patterns in this traversal.
|
|
54
73
|
*/
|
|
55
74
|
patterns(): Pattern[] {
|
|
56
|
-
const result: Pattern[] = [this
|
|
57
|
-
if (this
|
|
58
|
-
result.push(...this
|
|
75
|
+
const result: Pattern[] = [this._first];
|
|
76
|
+
if (this._rest !== undefined) {
|
|
77
|
+
result.push(...this._rest.patterns());
|
|
59
78
|
}
|
|
60
79
|
return result;
|
|
61
80
|
}
|
|
62
81
|
|
|
63
82
|
pathsWithCaptures(haystack: Envelope): [Path[], Map<string, Path[]>] {
|
|
64
|
-
|
|
65
|
-
|
|
83
|
+
if (_patternPathsWithCaptures === undefined) {
|
|
84
|
+
throw new Error("TraversePattern dispatch functions not registered");
|
|
85
|
+
}
|
|
86
|
+
const headPaths = _patternPathsWithCaptures(this._first, haystack)[0];
|
|
66
87
|
|
|
67
88
|
// If there's no further traversal, return head paths
|
|
68
|
-
if (this
|
|
89
|
+
if (this._rest === undefined) {
|
|
69
90
|
return [headPaths, new Map<string, Path[]>()];
|
|
70
91
|
}
|
|
71
92
|
|
|
@@ -74,7 +95,7 @@ export class TraversePattern implements Matcher {
|
|
|
74
95
|
const lastEnv = path[path.length - 1];
|
|
75
96
|
if (lastEnv !== undefined) {
|
|
76
97
|
// Recursively match the rest of the traversal
|
|
77
|
-
const tailPaths = this
|
|
98
|
+
const tailPaths = this._rest.paths(lastEnv);
|
|
78
99
|
for (const tailPath of tailPaths) {
|
|
79
100
|
const combined = [...path, ...tailPath];
|
|
80
101
|
result.push(combined);
|
|
@@ -94,24 +115,27 @@ export class TraversePattern implements Matcher {
|
|
|
94
115
|
}
|
|
95
116
|
|
|
96
117
|
compile(code: Instr[], literals: Pattern[], captures: string[]): void {
|
|
118
|
+
if (_patternCompile === undefined) {
|
|
119
|
+
throw new Error("TraversePattern dispatch functions not registered");
|
|
120
|
+
}
|
|
97
121
|
// Compile the first pattern
|
|
98
|
-
|
|
99
|
-
firstMatcher.compile(code, literals, captures);
|
|
122
|
+
_patternCompile(this._first, code, literals, captures);
|
|
100
123
|
|
|
101
|
-
if (this
|
|
124
|
+
if (this._rest !== undefined) {
|
|
102
125
|
// Save the current path and switch to last envelope
|
|
103
126
|
code.push({ type: "ExtendTraversal" });
|
|
104
127
|
// Compile the rest of the traversal
|
|
105
|
-
this
|
|
128
|
+
this._rest.compile(code, literals, captures);
|
|
106
129
|
// Combine the paths correctly
|
|
107
130
|
code.push({ type: "CombineTraversal" });
|
|
108
131
|
}
|
|
109
132
|
}
|
|
110
133
|
|
|
111
134
|
isComplex(): boolean {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
135
|
+
if (_patternIsComplex === undefined) {
|
|
136
|
+
throw new Error("TraversePattern dispatch functions not registered");
|
|
137
|
+
}
|
|
138
|
+
return _patternIsComplex(this._first) || this._rest !== undefined;
|
|
115
139
|
}
|
|
116
140
|
|
|
117
141
|
toString(): string {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import type { Envelope } from "@bcts/envelope";
|
|
10
10
|
import type { Path } from "../../format";
|
|
11
|
-
import type
|
|
11
|
+
import { matchPattern, type Matcher } from "../matcher";
|
|
12
12
|
import type { Instr } from "../vm";
|
|
13
13
|
import type { Pattern } from "../index";
|
|
14
14
|
|
|
@@ -42,10 +42,10 @@ export type AssertionsPatternType =
|
|
|
42
42
|
* Corresponds to the Rust `AssertionsPattern` enum in assertions_pattern.rs
|
|
43
43
|
*/
|
|
44
44
|
export class AssertionsPattern implements Matcher {
|
|
45
|
-
readonly
|
|
45
|
+
private readonly _pattern: AssertionsPatternType;
|
|
46
46
|
|
|
47
47
|
private constructor(pattern: AssertionsPatternType) {
|
|
48
|
-
this
|
|
48
|
+
this._pattern = pattern;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
/**
|
|
@@ -83,18 +83,18 @@ export class AssertionsPattern implements Matcher {
|
|
|
83
83
|
* Gets the pattern type.
|
|
84
84
|
*/
|
|
85
85
|
get patternType(): AssertionsPatternType {
|
|
86
|
-
return this
|
|
86
|
+
return this._pattern;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
/**
|
|
90
90
|
* Gets the predicate pattern if this has one, undefined otherwise.
|
|
91
91
|
*/
|
|
92
92
|
predicatePattern(): Pattern | undefined {
|
|
93
|
-
if (this
|
|
94
|
-
return this
|
|
93
|
+
if (this._pattern.type === "WithPredicate") {
|
|
94
|
+
return this._pattern.pattern;
|
|
95
95
|
}
|
|
96
|
-
if (this
|
|
97
|
-
return this
|
|
96
|
+
if (this._pattern.type === "WithBoth") {
|
|
97
|
+
return this._pattern.predicatePattern;
|
|
98
98
|
}
|
|
99
99
|
return undefined;
|
|
100
100
|
}
|
|
@@ -103,11 +103,11 @@ export class AssertionsPattern implements Matcher {
|
|
|
103
103
|
* Gets the object pattern if this has one, undefined otherwise.
|
|
104
104
|
*/
|
|
105
105
|
objectPattern(): Pattern | undefined {
|
|
106
|
-
if (this
|
|
107
|
-
return this
|
|
106
|
+
if (this._pattern.type === "WithObject") {
|
|
107
|
+
return this._pattern.pattern;
|
|
108
108
|
}
|
|
109
|
-
if (this
|
|
110
|
-
return this
|
|
109
|
+
if (this._pattern.type === "WithBoth") {
|
|
110
|
+
return this._pattern.objectPattern;
|
|
111
111
|
}
|
|
112
112
|
return undefined;
|
|
113
113
|
}
|
|
@@ -116,15 +116,14 @@ export class AssertionsPattern implements Matcher {
|
|
|
116
116
|
const paths: Path[] = [];
|
|
117
117
|
|
|
118
118
|
for (const assertion of haystack.assertions()) {
|
|
119
|
-
switch (this
|
|
119
|
+
switch (this._pattern.type) {
|
|
120
120
|
case "Any":
|
|
121
121
|
paths.push([assertion]);
|
|
122
122
|
break;
|
|
123
123
|
case "WithPredicate": {
|
|
124
124
|
const predicate = assertion.asPredicate?.();
|
|
125
125
|
if (predicate !== undefined) {
|
|
126
|
-
|
|
127
|
-
if (innerMatcher.matches(predicate)) {
|
|
126
|
+
if (matchPattern(this._pattern.pattern, predicate)) {
|
|
128
127
|
paths.push([assertion]);
|
|
129
128
|
}
|
|
130
129
|
}
|
|
@@ -133,8 +132,7 @@ export class AssertionsPattern implements Matcher {
|
|
|
133
132
|
case "WithObject": {
|
|
134
133
|
const object = assertion.asObject?.();
|
|
135
134
|
if (object !== undefined) {
|
|
136
|
-
|
|
137
|
-
if (innerMatcher.matches(object)) {
|
|
135
|
+
if (matchPattern(this._pattern.pattern, object)) {
|
|
138
136
|
paths.push([assertion]);
|
|
139
137
|
}
|
|
140
138
|
}
|
|
@@ -144,9 +142,10 @@ export class AssertionsPattern implements Matcher {
|
|
|
144
142
|
const predicate = assertion.asPredicate?.();
|
|
145
143
|
const object = assertion.asObject?.();
|
|
146
144
|
if (predicate !== undefined && object !== undefined) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
145
|
+
if (
|
|
146
|
+
matchPattern(this._pattern.predicatePattern, predicate) &&
|
|
147
|
+
matchPattern(this._pattern.objectPattern, object)
|
|
148
|
+
) {
|
|
150
149
|
paths.push([assertion]);
|
|
151
150
|
}
|
|
152
151
|
}
|
|
@@ -180,15 +179,15 @@ export class AssertionsPattern implements Matcher {
|
|
|
180
179
|
}
|
|
181
180
|
|
|
182
181
|
toString(): string {
|
|
183
|
-
switch (this
|
|
182
|
+
switch (this._pattern.type) {
|
|
184
183
|
case "Any":
|
|
185
184
|
return "assert";
|
|
186
185
|
case "WithPredicate":
|
|
187
|
-
return `assertpred(${(this
|
|
186
|
+
return `assertpred(${(this._pattern.pattern as unknown as { toString(): string }).toString()})`;
|
|
188
187
|
case "WithObject":
|
|
189
|
-
return `assertobj(${(this
|
|
188
|
+
return `assertobj(${(this._pattern.pattern as unknown as { toString(): string }).toString()})`;
|
|
190
189
|
case "WithBoth":
|
|
191
|
-
return `assert(${(this
|
|
190
|
+
return `assert(${(this._pattern.predicatePattern as unknown as { toString(): string }).toString()}, ${(this._pattern.objectPattern as unknown as { toString(): string }).toString()})`;
|
|
192
191
|
}
|
|
193
192
|
}
|
|
194
193
|
|
|
@@ -196,31 +195,31 @@ export class AssertionsPattern implements Matcher {
|
|
|
196
195
|
* Equality comparison.
|
|
197
196
|
*/
|
|
198
197
|
equals(other: AssertionsPattern): boolean {
|
|
199
|
-
if (this
|
|
198
|
+
if (this._pattern.type !== other._pattern.type) {
|
|
200
199
|
return false;
|
|
201
200
|
}
|
|
202
|
-
switch (this
|
|
201
|
+
switch (this._pattern.type) {
|
|
203
202
|
case "Any":
|
|
204
203
|
return true;
|
|
205
204
|
case "WithPredicate":
|
|
206
205
|
case "WithObject": {
|
|
207
206
|
const thisPattern = (
|
|
208
|
-
this
|
|
207
|
+
this._pattern as { type: "WithPredicate" | "WithObject"; pattern: Pattern }
|
|
209
208
|
).pattern;
|
|
210
209
|
const otherPattern = (
|
|
211
|
-
other
|
|
210
|
+
other._pattern as { type: "WithPredicate" | "WithObject"; pattern: Pattern }
|
|
212
211
|
).pattern;
|
|
213
212
|
return thisPattern === otherPattern;
|
|
214
213
|
}
|
|
215
214
|
case "WithBoth": {
|
|
216
|
-
const otherBoth = other
|
|
215
|
+
const otherBoth = other._pattern as {
|
|
217
216
|
type: "WithBoth";
|
|
218
217
|
predicatePattern: Pattern;
|
|
219
218
|
objectPattern: Pattern;
|
|
220
219
|
};
|
|
221
220
|
return (
|
|
222
|
-
this
|
|
223
|
-
this
|
|
221
|
+
this._pattern.predicatePattern === otherBoth.predicatePattern &&
|
|
222
|
+
this._pattern.objectPattern === otherBoth.objectPattern
|
|
224
223
|
);
|
|
225
224
|
}
|
|
226
225
|
}
|
|
@@ -230,7 +229,7 @@ export class AssertionsPattern implements Matcher {
|
|
|
230
229
|
* Hash code for use in Maps/Sets.
|
|
231
230
|
*/
|
|
232
231
|
hashCode(): number {
|
|
233
|
-
switch (this
|
|
232
|
+
switch (this._pattern.type) {
|
|
234
233
|
case "Any":
|
|
235
234
|
return 0;
|
|
236
235
|
case "WithPredicate":
|
|
@@ -49,10 +49,10 @@ export type DigestPatternType =
|
|
|
49
49
|
* Corresponds to the Rust `DigestPattern` enum in digest_pattern.rs
|
|
50
50
|
*/
|
|
51
51
|
export class DigestPattern implements Matcher {
|
|
52
|
-
readonly
|
|
52
|
+
private readonly _pattern: DigestPatternType;
|
|
53
53
|
|
|
54
54
|
private constructor(pattern: DigestPatternType) {
|
|
55
|
-
this
|
|
55
|
+
this._pattern = pattern;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
/**
|
|
@@ -87,7 +87,7 @@ export class DigestPattern implements Matcher {
|
|
|
87
87
|
* Gets the pattern type.
|
|
88
88
|
*/
|
|
89
89
|
get patternType(): DigestPatternType {
|
|
90
|
-
return this
|
|
90
|
+
return this._pattern;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
pathsWithCaptures(haystack: Envelope): [Path[], Map<string, Path[]>] {
|
|
@@ -95,16 +95,16 @@ export class DigestPattern implements Matcher {
|
|
|
95
95
|
const digestData = digest.data();
|
|
96
96
|
let isHit = false;
|
|
97
97
|
|
|
98
|
-
switch (this
|
|
98
|
+
switch (this._pattern.type) {
|
|
99
99
|
case "Any":
|
|
100
100
|
// Any digest matches - every envelope has a digest
|
|
101
101
|
isHit = true;
|
|
102
102
|
break;
|
|
103
103
|
case "Digest":
|
|
104
|
-
isHit = digest.equals(this
|
|
104
|
+
isHit = digest.equals(this._pattern.digest);
|
|
105
105
|
break;
|
|
106
106
|
case "Prefix": {
|
|
107
|
-
const prefix = this
|
|
107
|
+
const prefix = this._pattern.prefix;
|
|
108
108
|
if (digestData.length >= prefix.length) {
|
|
109
109
|
isHit = true;
|
|
110
110
|
for (let i = 0; i < prefix.length; i++) {
|
|
@@ -118,7 +118,7 @@ export class DigestPattern implements Matcher {
|
|
|
118
118
|
}
|
|
119
119
|
case "BinaryRegex": {
|
|
120
120
|
const latin1 = bytesToLatin1(digestData);
|
|
121
|
-
isHit = this
|
|
121
|
+
isHit = this._pattern.regex.test(latin1);
|
|
122
122
|
break;
|
|
123
123
|
}
|
|
124
124
|
}
|
|
@@ -147,15 +147,15 @@ export class DigestPattern implements Matcher {
|
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
toString(): string {
|
|
150
|
-
switch (this
|
|
150
|
+
switch (this._pattern.type) {
|
|
151
151
|
case "Any":
|
|
152
152
|
return "digest";
|
|
153
153
|
case "Digest":
|
|
154
|
-
return `digest(${this
|
|
154
|
+
return `digest(${this._pattern.digest.hex()})`;
|
|
155
155
|
case "Prefix":
|
|
156
|
-
return `digest(${bytesToHex(this
|
|
156
|
+
return `digest(${bytesToHex(this._pattern.prefix)})`;
|
|
157
157
|
case "BinaryRegex":
|
|
158
|
-
return `digest(/${this
|
|
158
|
+
return `digest(/${this._pattern.regex.source}/)`;
|
|
159
159
|
}
|
|
160
160
|
}
|
|
161
161
|
|
|
@@ -163,19 +163,19 @@ export class DigestPattern implements Matcher {
|
|
|
163
163
|
* Equality comparison.
|
|
164
164
|
*/
|
|
165
165
|
equals(other: DigestPattern): boolean {
|
|
166
|
-
if (this
|
|
166
|
+
if (this._pattern.type !== other._pattern.type) {
|
|
167
167
|
return false;
|
|
168
168
|
}
|
|
169
|
-
switch (this
|
|
169
|
+
switch (this._pattern.type) {
|
|
170
170
|
case "Any":
|
|
171
171
|
return true;
|
|
172
172
|
case "Digest":
|
|
173
|
-
return this
|
|
174
|
-
(other
|
|
173
|
+
return this._pattern.digest.equals(
|
|
174
|
+
(other._pattern as { type: "Digest"; digest: Digest }).digest,
|
|
175
175
|
);
|
|
176
176
|
case "Prefix": {
|
|
177
|
-
const thisPrefix = this
|
|
178
|
-
const otherPrefix = (other
|
|
177
|
+
const thisPrefix = this._pattern.prefix;
|
|
178
|
+
const otherPrefix = (other._pattern as { type: "Prefix"; prefix: Uint8Array }).prefix;
|
|
179
179
|
if (thisPrefix.length !== otherPrefix.length) return false;
|
|
180
180
|
for (let i = 0; i < thisPrefix.length; i++) {
|
|
181
181
|
if (thisPrefix[i] !== otherPrefix[i]) return false;
|
|
@@ -184,8 +184,8 @@ export class DigestPattern implements Matcher {
|
|
|
184
184
|
}
|
|
185
185
|
case "BinaryRegex":
|
|
186
186
|
return (
|
|
187
|
-
this
|
|
188
|
-
(other
|
|
187
|
+
this._pattern.regex.source ===
|
|
188
|
+
(other._pattern as { type: "BinaryRegex"; regex: RegExp }).regex.source
|
|
189
189
|
);
|
|
190
190
|
}
|
|
191
191
|
}
|
|
@@ -194,12 +194,12 @@ export class DigestPattern implements Matcher {
|
|
|
194
194
|
* Hash code for use in Maps/Sets.
|
|
195
195
|
*/
|
|
196
196
|
hashCode(): number {
|
|
197
|
-
switch (this
|
|
197
|
+
switch (this._pattern.type) {
|
|
198
198
|
case "Any":
|
|
199
199
|
return 0;
|
|
200
200
|
case "Digest": {
|
|
201
201
|
// Hash based on first few bytes of digest
|
|
202
|
-
const data = this
|
|
202
|
+
const data = this._pattern.digest.data().slice(0, 8);
|
|
203
203
|
let hash = 0;
|
|
204
204
|
for (const byte of data) {
|
|
205
205
|
hash = (hash * 31 + byte) | 0;
|
|
@@ -208,14 +208,14 @@ export class DigestPattern implements Matcher {
|
|
|
208
208
|
}
|
|
209
209
|
case "Prefix": {
|
|
210
210
|
let hash = 0;
|
|
211
|
-
for (const byte of this
|
|
211
|
+
for (const byte of this._pattern.prefix) {
|
|
212
212
|
hash = (hash * 31 + byte) | 0;
|
|
213
213
|
}
|
|
214
214
|
return hash;
|
|
215
215
|
}
|
|
216
216
|
case "BinaryRegex": {
|
|
217
217
|
let hash = 0;
|
|
218
|
-
for (const char of this
|
|
218
|
+
for (const char of this._pattern.regex.source) {
|
|
219
219
|
hash = (hash * 31 + char.charCodeAt(0)) | 0;
|
|
220
220
|
}
|
|
221
221
|
return hash;
|
|
@@ -37,10 +37,10 @@ export type NodePatternType =
|
|
|
37
37
|
* Corresponds to the Rust `NodePattern` enum in node_pattern.rs
|
|
38
38
|
*/
|
|
39
39
|
export class NodePattern implements Matcher {
|
|
40
|
-
readonly
|
|
40
|
+
private readonly _pattern: NodePatternType;
|
|
41
41
|
|
|
42
42
|
private constructor(pattern: NodePatternType) {
|
|
43
|
-
this
|
|
43
|
+
this._pattern = pattern;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
/**
|
|
@@ -76,14 +76,14 @@ export class NodePattern implements Matcher {
|
|
|
76
76
|
* Gets the pattern type.
|
|
77
77
|
*/
|
|
78
78
|
get patternType(): NodePatternType {
|
|
79
|
-
return this
|
|
79
|
+
return this._pattern;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
/**
|
|
83
83
|
* Gets the subject pattern if this is a WithSubject type, undefined otherwise.
|
|
84
84
|
*/
|
|
85
85
|
subjectPattern(): Pattern | undefined {
|
|
86
|
-
return this
|
|
86
|
+
return this._pattern.type === "WithSubject" ? this._pattern.subjectPattern : undefined;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
/**
|
|
@@ -101,12 +101,12 @@ export class NodePattern implements Matcher {
|
|
|
101
101
|
|
|
102
102
|
let isHit = false;
|
|
103
103
|
|
|
104
|
-
switch (this
|
|
104
|
+
switch (this._pattern.type) {
|
|
105
105
|
case "Any":
|
|
106
106
|
isHit = true;
|
|
107
107
|
break;
|
|
108
108
|
case "AssertionsInterval":
|
|
109
|
-
isHit = this
|
|
109
|
+
isHit = this._pattern.interval.contains(haystack.assertions().length);
|
|
110
110
|
break;
|
|
111
111
|
case "WithSubject":
|
|
112
112
|
// For WithSubject, we match if the node exists (subject pattern matching done at higher level)
|
|
@@ -138,13 +138,13 @@ export class NodePattern implements Matcher {
|
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
toString(): string {
|
|
141
|
-
switch (this
|
|
141
|
+
switch (this._pattern.type) {
|
|
142
142
|
case "Any":
|
|
143
143
|
return "node";
|
|
144
144
|
case "AssertionsInterval":
|
|
145
|
-
return `node(${this
|
|
145
|
+
return `node(${this._pattern.interval.toString()})`;
|
|
146
146
|
case "WithSubject":
|
|
147
|
-
return `node(${(this
|
|
147
|
+
return `node(${(this._pattern.subjectPattern as unknown as { toString(): string }).toString()})`;
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
150
|
|
|
@@ -152,21 +152,21 @@ export class NodePattern implements Matcher {
|
|
|
152
152
|
* Equality comparison.
|
|
153
153
|
*/
|
|
154
154
|
equals(other: NodePattern): boolean {
|
|
155
|
-
if (this
|
|
155
|
+
if (this._pattern.type !== other._pattern.type) {
|
|
156
156
|
return false;
|
|
157
157
|
}
|
|
158
|
-
switch (this
|
|
158
|
+
switch (this._pattern.type) {
|
|
159
159
|
case "Any":
|
|
160
160
|
return true;
|
|
161
161
|
case "AssertionsInterval":
|
|
162
|
-
return this
|
|
163
|
-
(other
|
|
162
|
+
return this._pattern.interval.equals(
|
|
163
|
+
(other._pattern as { type: "AssertionsInterval"; interval: Interval }).interval,
|
|
164
164
|
);
|
|
165
165
|
case "WithSubject":
|
|
166
166
|
// Simple reference equality for pattern (could be improved with deep equality)
|
|
167
167
|
return (
|
|
168
|
-
this
|
|
169
|
-
(other
|
|
168
|
+
this._pattern.subjectPattern ===
|
|
169
|
+
(other._pattern as { type: "WithSubject"; subjectPattern: Pattern }).subjectPattern
|
|
170
170
|
);
|
|
171
171
|
}
|
|
172
172
|
}
|
|
@@ -175,12 +175,12 @@ export class NodePattern implements Matcher {
|
|
|
175
175
|
* Hash code for use in Maps/Sets.
|
|
176
176
|
*/
|
|
177
177
|
hashCode(): number {
|
|
178
|
-
switch (this
|
|
178
|
+
switch (this._pattern.type) {
|
|
179
179
|
case "Any":
|
|
180
180
|
return 0;
|
|
181
181
|
case "AssertionsInterval":
|
|
182
182
|
// Simple hash based on interval min/max
|
|
183
|
-
return this
|
|
183
|
+
return this._pattern.interval.min() * 31 + (this._pattern.interval.max() ?? 0);
|
|
184
184
|
case "WithSubject":
|
|
185
185
|
return 1;
|
|
186
186
|
}
|