@bcts/envelope-pattern 1.0.0-alpha.17 → 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 +1679 -1401
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +54 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +54 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.iife.js +1680 -1403
- package/dist/index.iife.js.map +1 -1
- package/dist/index.mjs +1670 -1402
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -6
- package/src/format.ts +23 -4
- package/src/parse/index.ts +138 -5
- package/src/parse/token.ts +4 -3
- package/src/pattern/index.ts +110 -2
- package/src/pattern/leaf/array-pattern.ts +1 -1
- package/src/pattern/leaf/bool-pattern.ts +1 -1
- package/src/pattern/leaf/byte-string-pattern.ts +1 -1
- package/src/pattern/leaf/cbor-pattern.ts +2 -2
- package/src/pattern/leaf/date-pattern.ts +1 -1
- package/src/pattern/leaf/index.ts +1 -2
- package/src/pattern/leaf/known-value-pattern.ts +4 -3
- package/src/pattern/leaf/map-pattern.ts +1 -1
- package/src/pattern/leaf/null-pattern.ts +1 -1
- package/src/pattern/leaf/number-pattern.ts +1 -1
- package/src/pattern/leaf/text-pattern.ts +1 -1
- package/src/pattern/matcher.ts +88 -3
- package/src/pattern/meta/and-pattern.ts +10 -9
- package/src/pattern/meta/capture-pattern.ts +5 -6
- package/src/pattern/meta/group-pattern.ts +9 -6
- package/src/pattern/meta/not-pattern.ts +3 -2
- package/src/pattern/meta/or-pattern.ts +18 -13
- package/src/pattern/meta/search-pattern.ts +4 -4
- package/src/pattern/meta/traverse-pattern.ts +31 -7
- package/src/pattern/structure/assertions-pattern.ts +7 -8
- package/src/pattern/structure/index.ts +1 -0
- package/src/pattern/structure/object-pattern.ts +2 -3
- package/src/pattern/structure/predicate-pattern.ts +2 -3
- package/src/pattern/structure/subject-pattern.ts +2 -3
- package/src/pattern/structure/wrapped-pattern.ts +28 -7
- package/src/pattern/vm.ts +12 -11
package/src/pattern/matcher.ts
CHANGED
|
@@ -88,14 +88,23 @@ export function compileAsAtomic(
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
// ============================================================================
|
|
91
|
-
// Pattern
|
|
91
|
+
// Pattern Dispatch Registry
|
|
92
92
|
// ============================================================================
|
|
93
93
|
|
|
94
94
|
/**
|
|
95
|
-
* Registry for pattern
|
|
96
|
-
* This allows meta patterns to
|
|
95
|
+
* Registry for pattern dispatch functions to break circular dependencies.
|
|
96
|
+
* This allows meta patterns to dispatch to child patterns without importing from index.ts.
|
|
97
97
|
*/
|
|
98
98
|
let patternMatchFn: ((pattern: Pattern, haystack: Envelope) => boolean) | undefined;
|
|
99
|
+
let patternPathsWithCapturesFn:
|
|
100
|
+
| ((pattern: Pattern, haystack: Envelope) => [Path[], Map<string, Path[]>])
|
|
101
|
+
| undefined;
|
|
102
|
+
let patternPathsFn: ((pattern: Pattern, haystack: Envelope) => Path[]) | undefined;
|
|
103
|
+
let patternCompileFn:
|
|
104
|
+
| ((pattern: Pattern, code: Instr[], literals: Pattern[], captures: string[]) => void)
|
|
105
|
+
| undefined;
|
|
106
|
+
let patternIsComplexFn: ((pattern: Pattern) => boolean) | undefined;
|
|
107
|
+
let patternToStringFn: ((pattern: Pattern) => string) | undefined;
|
|
99
108
|
|
|
100
109
|
/**
|
|
101
110
|
* Registers the pattern match function.
|
|
@@ -107,6 +116,24 @@ export function registerPatternMatchFn(
|
|
|
107
116
|
patternMatchFn = fn;
|
|
108
117
|
}
|
|
109
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Registers all pattern dispatch functions.
|
|
121
|
+
* Called from index.ts after all patterns are defined.
|
|
122
|
+
*/
|
|
123
|
+
export function registerPatternDispatchFns(fns: {
|
|
124
|
+
pathsWithCaptures: (pattern: Pattern, haystack: Envelope) => [Path[], Map<string, Path[]>];
|
|
125
|
+
paths: (pattern: Pattern, haystack: Envelope) => Path[];
|
|
126
|
+
compile: (pattern: Pattern, code: Instr[], literals: Pattern[], captures: string[]) => void;
|
|
127
|
+
isComplex: (pattern: Pattern) => boolean;
|
|
128
|
+
toString: (pattern: Pattern) => string;
|
|
129
|
+
}): void {
|
|
130
|
+
patternPathsWithCapturesFn = fns.pathsWithCaptures;
|
|
131
|
+
patternPathsFn = fns.paths;
|
|
132
|
+
patternCompileFn = fns.compile;
|
|
133
|
+
patternIsComplexFn = fns.isComplex;
|
|
134
|
+
patternToStringFn = fns.toString;
|
|
135
|
+
}
|
|
136
|
+
|
|
110
137
|
/**
|
|
111
138
|
* Match a pattern against an envelope using the registered match function.
|
|
112
139
|
* Used by meta patterns to match child patterns.
|
|
@@ -117,3 +144,61 @@ export function matchPattern(pattern: Pattern, haystack: Envelope): boolean {
|
|
|
117
144
|
}
|
|
118
145
|
return patternMatchFn(pattern, haystack);
|
|
119
146
|
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Dispatch pathsWithCaptures on a Pattern.
|
|
150
|
+
*/
|
|
151
|
+
export function dispatchPathsWithCaptures(
|
|
152
|
+
pattern: Pattern,
|
|
153
|
+
haystack: Envelope,
|
|
154
|
+
): [Path[], Map<string, Path[]>] {
|
|
155
|
+
if (patternPathsWithCapturesFn === undefined) {
|
|
156
|
+
throw new Error("Pattern dispatch functions not registered");
|
|
157
|
+
}
|
|
158
|
+
return patternPathsWithCapturesFn(pattern, haystack);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Dispatch paths on a Pattern.
|
|
163
|
+
*/
|
|
164
|
+
export function dispatchPaths(pattern: Pattern, haystack: Envelope): Path[] {
|
|
165
|
+
if (patternPathsFn === undefined) {
|
|
166
|
+
throw new Error("Pattern dispatch functions not registered");
|
|
167
|
+
}
|
|
168
|
+
return patternPathsFn(pattern, haystack);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Dispatch compile on a Pattern.
|
|
173
|
+
*/
|
|
174
|
+
export function dispatchCompile(
|
|
175
|
+
pattern: Pattern,
|
|
176
|
+
code: Instr[],
|
|
177
|
+
literals: Pattern[],
|
|
178
|
+
captures: string[],
|
|
179
|
+
): void {
|
|
180
|
+
if (patternCompileFn === undefined) {
|
|
181
|
+
throw new Error("Pattern dispatch functions not registered");
|
|
182
|
+
}
|
|
183
|
+
patternCompileFn(pattern, code, literals, captures);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Dispatch isComplex on a Pattern.
|
|
188
|
+
*/
|
|
189
|
+
export function dispatchIsComplex(pattern: Pattern): boolean {
|
|
190
|
+
if (patternIsComplexFn === undefined) {
|
|
191
|
+
throw new Error("Pattern dispatch functions not registered");
|
|
192
|
+
}
|
|
193
|
+
return patternIsComplexFn(pattern);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Dispatch toString on a Pattern.
|
|
198
|
+
*/
|
|
199
|
+
export function dispatchPatternToString(pattern: Pattern): string {
|
|
200
|
+
if (patternToStringFn === undefined) {
|
|
201
|
+
throw new Error("Pattern dispatch functions not registered");
|
|
202
|
+
}
|
|
203
|
+
return patternToStringFn(pattern);
|
|
204
|
+
}
|
|
@@ -8,9 +8,15 @@
|
|
|
8
8
|
|
|
9
9
|
import type { Envelope } from "@bcts/envelope";
|
|
10
10
|
import type { Path } from "../../format";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
matchPattern,
|
|
13
|
+
dispatchCompile,
|
|
14
|
+
dispatchIsComplex,
|
|
15
|
+
dispatchPatternToString,
|
|
16
|
+
} from "../matcher";
|
|
12
17
|
import type { Instr } from "../vm";
|
|
13
18
|
import type { Pattern } from "../index";
|
|
19
|
+
import type { Matcher } from "../matcher";
|
|
14
20
|
|
|
15
21
|
// Forward declaration for Pattern factory (used for late binding)
|
|
16
22
|
export let createMetaAndPattern: ((pattern: AndPattern) => Pattern) | undefined;
|
|
@@ -63,23 +69,18 @@ export class AndPattern implements Matcher {
|
|
|
63
69
|
compile(code: Instr[], literals: Pattern[], captures: string[]): void {
|
|
64
70
|
// Each pattern must match at this position
|
|
65
71
|
for (const pattern of this._patterns) {
|
|
66
|
-
|
|
67
|
-
matcher.compile(code, literals, captures);
|
|
72
|
+
dispatchCompile(pattern, code, literals, captures);
|
|
68
73
|
}
|
|
69
74
|
}
|
|
70
75
|
|
|
71
76
|
isComplex(): boolean {
|
|
72
77
|
// The pattern is complex if it contains more than one pattern, or if
|
|
73
78
|
// the one pattern is complex itself.
|
|
74
|
-
return (
|
|
75
|
-
this._patterns.length > 1 || this._patterns.some((p) => (p as unknown as Matcher).isComplex())
|
|
76
|
-
);
|
|
79
|
+
return this._patterns.length > 1 || this._patterns.some((p) => dispatchIsComplex(p));
|
|
77
80
|
}
|
|
78
81
|
|
|
79
82
|
toString(): string {
|
|
80
|
-
return this._patterns
|
|
81
|
-
.map((p) => (p as unknown as { toString(): string }).toString())
|
|
82
|
-
.join(" & ");
|
|
83
|
+
return this._patterns.map((p) => dispatchPatternToString(p)).join(" & ");
|
|
83
84
|
}
|
|
84
85
|
|
|
85
86
|
/**
|
|
@@ -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 { dispatchPathsWithCaptures, dispatchCompile, 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 createMetaCapturePattern: ((pattern: CapturePattern) => Pattern) | undefined;
|
|
@@ -55,8 +56,7 @@ export class CapturePattern implements Matcher {
|
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
pathsWithCaptures(haystack: Envelope): [Path[], Map<string, Path[]>] {
|
|
58
|
-
const
|
|
59
|
-
const [paths, caps] = matcher.pathsWithCaptures(haystack);
|
|
59
|
+
const [paths, caps] = dispatchPathsWithCaptures(this._pattern, haystack);
|
|
60
60
|
|
|
61
61
|
if (paths.length > 0) {
|
|
62
62
|
const existing = caps.get(this._name) ?? [];
|
|
@@ -78,8 +78,7 @@ export class CapturePattern implements Matcher {
|
|
|
78
78
|
const id = captures.length;
|
|
79
79
|
captures.push(this._name);
|
|
80
80
|
code.push({ type: "CaptureStart", captureIndex: id });
|
|
81
|
-
|
|
82
|
-
matcher.compile(code, literals, captures);
|
|
81
|
+
dispatchCompile(this._pattern, code, literals, captures);
|
|
83
82
|
code.push({ type: "CaptureEnd", captureIndex: id });
|
|
84
83
|
}
|
|
85
84
|
|
|
@@ -88,7 +87,7 @@ export class CapturePattern implements Matcher {
|
|
|
88
87
|
}
|
|
89
88
|
|
|
90
89
|
toString(): string {
|
|
91
|
-
return `@${this._name}(${(this._pattern
|
|
90
|
+
return `@${this._name}(${dispatchPatternToString(this._pattern)})`;
|
|
92
91
|
}
|
|
93
92
|
|
|
94
93
|
/**
|
|
@@ -9,9 +9,10 @@
|
|
|
9
9
|
import type { Envelope } from "@bcts/envelope";
|
|
10
10
|
import { Quantifier } from "@bcts/dcbor-pattern";
|
|
11
11
|
import type { Path } from "../../format";
|
|
12
|
-
import
|
|
12
|
+
import { matchPattern, dispatchPathsWithCaptures, dispatchPatternToString } from "../matcher";
|
|
13
13
|
import type { Instr } from "../vm";
|
|
14
14
|
import type { Pattern } from "../index";
|
|
15
|
+
import type { Matcher } from "../matcher";
|
|
15
16
|
|
|
16
17
|
// Forward declaration for Pattern factory (used for late binding)
|
|
17
18
|
export let createMetaGroupPattern: ((pattern: GroupPattern) => Pattern) | undefined;
|
|
@@ -62,7 +63,11 @@ export class GroupPattern implements Matcher {
|
|
|
62
63
|
return this._quantifier;
|
|
63
64
|
}
|
|
64
65
|
|
|
65
|
-
pathsWithCaptures(
|
|
66
|
+
pathsWithCaptures(haystack: Envelope): [Path[], Map<string, Path[]>] {
|
|
67
|
+
// For simple grouping (exactly 1), delegate to the inner pattern
|
|
68
|
+
if (this._quantifier.min() === 1 && this._quantifier.max() === 1) {
|
|
69
|
+
return dispatchPathsWithCaptures(this._pattern, haystack);
|
|
70
|
+
}
|
|
66
71
|
throw new Error(
|
|
67
72
|
"GroupPattern does not support pathsWithCaptures directly; use compile instead",
|
|
68
73
|
);
|
|
@@ -73,9 +78,7 @@ export class GroupPattern implements Matcher {
|
|
|
73
78
|
}
|
|
74
79
|
|
|
75
80
|
matches(haystack: Envelope): boolean {
|
|
76
|
-
|
|
77
|
-
const matcher = this._pattern as unknown as Matcher;
|
|
78
|
-
return matcher.matches(haystack);
|
|
81
|
+
return matchPattern(this._pattern, haystack);
|
|
79
82
|
}
|
|
80
83
|
|
|
81
84
|
compile(code: Instr[], literals: Pattern[], _captures: string[]): void {
|
|
@@ -90,7 +93,7 @@ export class GroupPattern implements Matcher {
|
|
|
90
93
|
|
|
91
94
|
toString(): string {
|
|
92
95
|
const formattedRange = this._quantifier.toString();
|
|
93
|
-
return `(${(this._pattern
|
|
96
|
+
return `(${dispatchPatternToString(this._pattern)})${formattedRange}`;
|
|
94
97
|
}
|
|
95
98
|
|
|
96
99
|
/**
|
|
@@ -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 { matchPattern, 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 createMetaNotPattern: ((pattern: NotPattern) => Pattern) | undefined;
|
|
@@ -71,7 +72,7 @@ export class NotPattern implements Matcher {
|
|
|
71
72
|
}
|
|
72
73
|
|
|
73
74
|
toString(): string {
|
|
74
|
-
return `!${(this._pattern
|
|
75
|
+
return `!${dispatchPatternToString(this._pattern)}`;
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
/**
|
|
@@ -8,9 +8,15 @@
|
|
|
8
8
|
|
|
9
9
|
import type { Envelope } from "@bcts/envelope";
|
|
10
10
|
import type { Path } from "../../format";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
dispatchPathsWithCaptures,
|
|
13
|
+
dispatchCompile,
|
|
14
|
+
dispatchIsComplex,
|
|
15
|
+
dispatchPatternToString,
|
|
16
|
+
} from "../matcher";
|
|
12
17
|
import type { Instr } from "../vm";
|
|
13
18
|
import type { Pattern } from "../index";
|
|
19
|
+
import type { Matcher } from "../matcher";
|
|
14
20
|
|
|
15
21
|
// Forward declaration for Pattern factory (used for late binding)
|
|
16
22
|
export let createMetaOrPattern: ((pattern: OrPattern) => Pattern) | undefined;
|
|
@@ -46,10 +52,14 @@ export class OrPattern implements Matcher {
|
|
|
46
52
|
}
|
|
47
53
|
|
|
48
54
|
pathsWithCaptures(haystack: Envelope): [Path[], Map<string, Path[]>] {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
55
|
+
// Try each pattern and return paths+captures from the first match
|
|
56
|
+
for (const pattern of this._patterns) {
|
|
57
|
+
const [paths, captures] = dispatchPathsWithCaptures(pattern, haystack);
|
|
58
|
+
if (paths.length > 0) {
|
|
59
|
+
return [paths, captures];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return [[], new Map<string, Path[]>()];
|
|
53
63
|
}
|
|
54
64
|
|
|
55
65
|
paths(haystack: Envelope): Path[] {
|
|
@@ -83,8 +93,7 @@ export class OrPattern implements Matcher {
|
|
|
83
93
|
|
|
84
94
|
// Compile this pattern
|
|
85
95
|
const pattern = this._patterns[i];
|
|
86
|
-
|
|
87
|
-
matcher.compile(code, literals, captures);
|
|
96
|
+
dispatchCompile(pattern, code, literals, captures);
|
|
88
97
|
|
|
89
98
|
// This pattern will jump to the end if it matches
|
|
90
99
|
const jumpPastAll = code.length;
|
|
@@ -108,15 +117,11 @@ export class OrPattern implements Matcher {
|
|
|
108
117
|
isComplex(): boolean {
|
|
109
118
|
// The pattern is complex if it contains more than one pattern, or if
|
|
110
119
|
// the one pattern is complex itself.
|
|
111
|
-
return (
|
|
112
|
-
this._patterns.length > 1 || this._patterns.some((p) => (p as unknown as Matcher).isComplex())
|
|
113
|
-
);
|
|
120
|
+
return this._patterns.length > 1 || this._patterns.some((p) => dispatchIsComplex(p));
|
|
114
121
|
}
|
|
115
122
|
|
|
116
123
|
toString(): string {
|
|
117
|
-
return this._patterns
|
|
118
|
-
.map((p) => (p as unknown as { toString(): string }).toString())
|
|
119
|
-
.join(" | ");
|
|
124
|
+
return this._patterns.map((p) => dispatchPatternToString(p)).join(" | ");
|
|
120
125
|
}
|
|
121
126
|
|
|
122
127
|
/**
|
|
@@ -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;
|
|
@@ -47,7 +48,6 @@ export class SearchPattern implements Matcher {
|
|
|
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
53
|
this._walkEnvelope(haystack, [], (currentEnvelope, pathToCurrent) => {
|
|
@@ -55,7 +55,7 @@ export class SearchPattern implements Matcher {
|
|
|
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) {
|
|
@@ -170,7 +170,7 @@ export class SearchPattern implements Matcher {
|
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
toString(): string {
|
|
173
|
-
return `search(${(this._pattern
|
|
173
|
+
return `search(${dispatchPatternToString(this._pattern)})`;
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
/**
|
|
@@ -21,6 +21,25 @@ 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
|
*
|
|
@@ -61,8 +80,10 @@ export class TraversePattern implements Matcher {
|
|
|
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
89
|
if (this._rest === undefined) {
|
|
@@ -94,9 +115,11 @@ 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
124
|
if (this._rest !== undefined) {
|
|
102
125
|
// Save the current path and switch to last envelope
|
|
@@ -109,9 +132,10 @@ export class TraversePattern implements Matcher {
|
|
|
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
|
|
|
@@ -123,8 +123,7 @@ export class AssertionsPattern implements Matcher {
|
|
|
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
|
}
|
|
@@ -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
|
|
|
@@ -82,8 +82,7 @@ export class ObjectPattern implements Matcher {
|
|
|
82
82
|
paths = [[object]];
|
|
83
83
|
break;
|
|
84
84
|
case "Pattern": {
|
|
85
|
-
|
|
86
|
-
if (innerMatcher.matches(object)) {
|
|
85
|
+
if (matchPattern(this._pattern.pattern, object)) {
|
|
87
86
|
paths = [[object]];
|
|
88
87
|
} else {
|
|
89
88
|
paths = [];
|
|
@@ -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
|
|
|
@@ -84,8 +84,7 @@ export class PredicatePattern implements Matcher {
|
|
|
84
84
|
paths = [[predicate]];
|
|
85
85
|
break;
|
|
86
86
|
case "Pattern": {
|
|
87
|
-
|
|
88
|
-
if (innerMatcher.matches(predicate)) {
|
|
87
|
+
if (matchPattern(this._pattern.pattern, predicate)) {
|
|
89
88
|
paths = [[predicate]];
|
|
90
89
|
} else {
|
|
91
90
|
paths = [];
|
|
@@ -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
|
|
|
@@ -77,8 +77,7 @@ export class SubjectPattern implements Matcher {
|
|
|
77
77
|
paths = [[subject]];
|
|
78
78
|
break;
|
|
79
79
|
case "Pattern": {
|
|
80
|
-
|
|
81
|
-
if (innerMatcher.matches(subject)) {
|
|
80
|
+
if (matchPattern(this._pattern.pattern, subject)) {
|
|
82
81
|
paths = [[subject]];
|
|
83
82
|
} else {
|
|
84
83
|
paths = [];
|
|
@@ -15,10 +15,29 @@ import type { Pattern } from "../index";
|
|
|
15
15
|
// Forward declaration for Pattern factory
|
|
16
16
|
let createStructureWrappedPattern: ((pattern: WrappedPattern) => Pattern) | undefined;
|
|
17
17
|
|
|
18
|
+
// Forward declaration for pattern dispatch (avoids circular imports)
|
|
19
|
+
let dispatchPatternPathsWithCaptures:
|
|
20
|
+
| ((pattern: Pattern, haystack: Envelope) => [Path[], Map<string, Path[]>])
|
|
21
|
+
| undefined;
|
|
22
|
+
let dispatchPatternCompile:
|
|
23
|
+
| ((pattern: Pattern, code: Instr[], literals: Pattern[], captures: string[]) => void)
|
|
24
|
+
| undefined;
|
|
25
|
+
let dispatchPatternToString: ((pattern: Pattern) => string) | undefined;
|
|
26
|
+
|
|
18
27
|
export function registerWrappedPatternFactory(factory: (pattern: WrappedPattern) => Pattern): void {
|
|
19
28
|
createStructureWrappedPattern = factory;
|
|
20
29
|
}
|
|
21
30
|
|
|
31
|
+
export function registerWrappedPatternDispatch(dispatch: {
|
|
32
|
+
pathsWithCaptures: (pattern: Pattern, haystack: Envelope) => [Path[], Map<string, Path[]>];
|
|
33
|
+
compile: (pattern: Pattern, code: Instr[], literals: Pattern[], captures: string[]) => void;
|
|
34
|
+
toString: (pattern: Pattern) => string;
|
|
35
|
+
}): void {
|
|
36
|
+
dispatchPatternPathsWithCaptures = dispatch.pathsWithCaptures;
|
|
37
|
+
dispatchPatternCompile = dispatch.compile;
|
|
38
|
+
dispatchPatternToString = dispatch.toString;
|
|
39
|
+
}
|
|
40
|
+
|
|
22
41
|
/**
|
|
23
42
|
* Pattern type for wrapped pattern matching.
|
|
24
43
|
*
|
|
@@ -96,9 +115,8 @@ export class WrappedPattern implements Matcher {
|
|
|
96
115
|
case "Unwrap": {
|
|
97
116
|
// Match the content of the wrapped envelope
|
|
98
117
|
const unwrapped = subject.tryUnwrap?.();
|
|
99
|
-
if (unwrapped !== undefined) {
|
|
100
|
-
const
|
|
101
|
-
const innerPaths = innerMatcher.paths(unwrapped);
|
|
118
|
+
if (unwrapped !== undefined && dispatchPatternPathsWithCaptures !== undefined) {
|
|
119
|
+
const [innerPaths] = dispatchPatternPathsWithCaptures(this._pattern.pattern, unwrapped);
|
|
102
120
|
paths = innerPaths.map((path) => {
|
|
103
121
|
// Add the current envelope to the path
|
|
104
122
|
return [haystack, ...path];
|
|
@@ -145,8 +163,9 @@ export class WrappedPattern implements Matcher {
|
|
|
145
163
|
code.push({ type: "PushAxis", axis });
|
|
146
164
|
|
|
147
165
|
// Then match the pattern
|
|
148
|
-
|
|
149
|
-
|
|
166
|
+
if (dispatchPatternCompile !== undefined) {
|
|
167
|
+
dispatchPatternCompile(this._pattern.pattern, code, literals, captures);
|
|
168
|
+
}
|
|
150
169
|
break;
|
|
151
170
|
}
|
|
152
171
|
}
|
|
@@ -161,8 +180,10 @@ export class WrappedPattern implements Matcher {
|
|
|
161
180
|
case "Any":
|
|
162
181
|
return "wrapped";
|
|
163
182
|
case "Unwrap": {
|
|
164
|
-
|
|
165
|
-
|
|
183
|
+
const patternStr =
|
|
184
|
+
dispatchPatternToString !== undefined
|
|
185
|
+
? dispatchPatternToString(this._pattern.pattern)
|
|
186
|
+
: "*";
|
|
166
187
|
if (patternStr === "*") {
|
|
167
188
|
return "unwrap";
|
|
168
189
|
}
|
package/src/pattern/vm.ts
CHANGED
|
@@ -987,24 +987,25 @@ function compileMetaPattern(
|
|
|
987
987
|
break;
|
|
988
988
|
}
|
|
989
989
|
case "Traverse": {
|
|
990
|
+
// Matches Rust's recursive compilation: each ExtendTraversal gets a
|
|
991
|
+
// matching CombineTraversal so saved paths are properly restored.
|
|
990
992
|
const patterns = pattern.pattern.patterns();
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
compilePattern(pat, code, literals, captureNames);
|
|
995
|
-
if (i < patterns.length - 1) {
|
|
993
|
+
if (patterns.length > 0) {
|
|
994
|
+
compilePattern(patterns[0], code, literals, captureNames);
|
|
995
|
+
for (let i = 1; i < patterns.length; i++) {
|
|
996
996
|
code.push({ type: "ExtendTraversal" });
|
|
997
|
+
compilePattern(patterns[i], code, literals, captureNames);
|
|
998
|
+
}
|
|
999
|
+
for (let i = 1; i < patterns.length; i++) {
|
|
1000
|
+
code.push({ type: "CombineTraversal" });
|
|
997
1001
|
}
|
|
998
|
-
}
|
|
999
|
-
if (patterns.length > 1) {
|
|
1000
|
-
code.push({ type: "CombineTraversal" });
|
|
1001
1002
|
}
|
|
1002
1003
|
break;
|
|
1003
1004
|
}
|
|
1004
1005
|
case "Group": {
|
|
1005
1006
|
const quantifier = pattern.pattern.quantifier();
|
|
1006
|
-
if (quantifier !== undefined) {
|
|
1007
|
-
// Repeat pattern
|
|
1007
|
+
if (quantifier !== undefined && !(quantifier.min() === 1 && quantifier.max() === 1)) {
|
|
1008
|
+
// Repeat pattern (skip for exactly-1 which is simple pass-through)
|
|
1008
1009
|
literals.push(pattern.pattern.pattern());
|
|
1009
1010
|
code.push({
|
|
1010
1011
|
type: "Repeat",
|
|
@@ -1012,7 +1013,7 @@ function compileMetaPattern(
|
|
|
1012
1013
|
quantifier,
|
|
1013
1014
|
});
|
|
1014
1015
|
} else {
|
|
1015
|
-
// Simple grouping
|
|
1016
|
+
// Simple grouping (including exactly-1 quantifier)
|
|
1016
1017
|
compilePattern(pattern.pattern.pattern(), code, literals, captureNames);
|
|
1017
1018
|
}
|
|
1018
1019
|
break;
|