@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.
Files changed (42) hide show
  1. package/README.md +1 -1
  2. package/dist/index.cjs +1679 -1401
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +54 -2
  5. package/dist/index.d.cts.map +1 -1
  6. package/dist/index.d.mts +54 -2
  7. package/dist/index.d.mts.map +1 -1
  8. package/dist/index.iife.js +1680 -1403
  9. package/dist/index.iife.js.map +1 -1
  10. package/dist/index.mjs +1670 -1402
  11. package/dist/index.mjs.map +1 -1
  12. package/package.json +6 -6
  13. package/src/format.ts +23 -4
  14. package/src/parse/index.ts +138 -5
  15. package/src/parse/token.ts +4 -3
  16. package/src/pattern/index.ts +110 -2
  17. package/src/pattern/leaf/array-pattern.ts +1 -1
  18. package/src/pattern/leaf/bool-pattern.ts +1 -1
  19. package/src/pattern/leaf/byte-string-pattern.ts +1 -1
  20. package/src/pattern/leaf/cbor-pattern.ts +2 -2
  21. package/src/pattern/leaf/date-pattern.ts +1 -1
  22. package/src/pattern/leaf/index.ts +1 -2
  23. package/src/pattern/leaf/known-value-pattern.ts +4 -3
  24. package/src/pattern/leaf/map-pattern.ts +1 -1
  25. package/src/pattern/leaf/null-pattern.ts +1 -1
  26. package/src/pattern/leaf/number-pattern.ts +1 -1
  27. package/src/pattern/leaf/text-pattern.ts +1 -1
  28. package/src/pattern/matcher.ts +88 -3
  29. package/src/pattern/meta/and-pattern.ts +10 -9
  30. package/src/pattern/meta/capture-pattern.ts +5 -6
  31. package/src/pattern/meta/group-pattern.ts +9 -6
  32. package/src/pattern/meta/not-pattern.ts +3 -2
  33. package/src/pattern/meta/or-pattern.ts +18 -13
  34. package/src/pattern/meta/search-pattern.ts +4 -4
  35. package/src/pattern/meta/traverse-pattern.ts +31 -7
  36. package/src/pattern/structure/assertions-pattern.ts +7 -8
  37. package/src/pattern/structure/index.ts +1 -0
  38. package/src/pattern/structure/object-pattern.ts +2 -3
  39. package/src/pattern/structure/predicate-pattern.ts +2 -3
  40. package/src/pattern/structure/subject-pattern.ts +2 -3
  41. package/src/pattern/structure/wrapped-pattern.ts +28 -7
  42. package/src/pattern/vm.ts +12 -11
@@ -88,14 +88,23 @@ export function compileAsAtomic(
88
88
  }
89
89
 
90
90
  // ============================================================================
91
- // Pattern Match Registry
91
+ // Pattern Dispatch Registry
92
92
  // ============================================================================
93
93
 
94
94
  /**
95
- * Registry for pattern matching function to break circular dependencies.
96
- * This allows meta patterns to match child patterns without importing from index.ts.
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 { type Matcher, matchPattern } from "../matcher";
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
- const matcher = pattern as unknown as Matcher;
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 type { Matcher } from "../matcher";
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 matcher = this._pattern as unknown as Matcher;
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
- const matcher = this._pattern as unknown as Matcher;
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 as unknown as { toString(): string }).toString()})`;
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 type { Matcher } from "../matcher";
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(_haystack: Envelope): [Path[], Map<string, Path[]>] {
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
- // GroupPattern needs VM execution
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 as unknown as { toString(): string }).toString()})${formattedRange}`;
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 { type Matcher, matchPattern } from "../matcher";
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 as unknown as { toString(): string }).toString()}`;
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 { type Matcher, matchPattern } from "../matcher";
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
- const anyMatch = this._patterns.some((pattern) => matchPattern(pattern, haystack));
50
-
51
- const paths = anyMatch ? [[haystack]] : [];
52
- return [paths, new Map<string, Path[]>()];
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
- const matcher = pattern as unknown as Matcher;
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 type { Matcher } from "../matcher";
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 = matcher.paths(currentEnvelope);
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 as unknown as { toString(): string }).toString()})`;
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
- const firstMatcher = this._first as unknown as Matcher;
65
- const headPaths = firstMatcher.paths(haystack);
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
- const firstMatcher = this._first as unknown as Matcher;
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
- // A traversal is complex if `first` is complex, or it has more than one pattern
113
- const firstMatcher = this._first as unknown as Matcher;
114
- return firstMatcher.isComplex() || this._rest !== undefined;
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 { Matcher } from "../matcher";
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
- const innerMatcher = this._pattern.pattern as unknown as Matcher;
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
- const innerMatcher = this._pattern.pattern as unknown as Matcher;
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
- const predMatcher = this._pattern.predicatePattern as unknown as Matcher;
148
- const objMatcher = this._pattern.objectPattern as unknown as Matcher;
149
- if (predMatcher.matches(predicate) && objMatcher.matches(object)) {
145
+ if (
146
+ matchPattern(this._pattern.predicatePattern, predicate) &&
147
+ matchPattern(this._pattern.objectPattern, object)
148
+ ) {
150
149
  paths.push([assertion]);
151
150
  }
152
151
  }
@@ -51,6 +51,7 @@ export {
51
51
  WrappedPattern,
52
52
  type WrappedPatternType,
53
53
  registerWrappedPatternFactory,
54
+ registerWrappedPatternDispatch,
54
55
  } from "./wrapped-pattern";
55
56
 
56
57
  // Import concrete types for use in StructurePattern
@@ -8,7 +8,7 @@
8
8
 
9
9
  import type { Envelope } from "@bcts/envelope";
10
10
  import type { Path } from "../../format";
11
- import type { Matcher } from "../matcher";
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
- const innerMatcher = this._pattern.pattern as unknown as Matcher;
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 { Matcher } from "../matcher";
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
- const innerMatcher = this._pattern.pattern as unknown as Matcher;
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 { Matcher } from "../matcher";
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
- const innerMatcher = this._pattern.pattern as unknown as Matcher;
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 innerMatcher = this._pattern.pattern as unknown as Matcher;
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
- const innerMatcher = this._pattern.pattern as unknown as Matcher;
149
- innerMatcher.compile(code, literals, captures);
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
- // Check if it's the "any" pattern by string comparison
165
- const patternStr = (this._pattern.pattern as unknown as { toString(): string }).toString();
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
- for (let i = 0; i < patterns.length; i++) {
992
- const pat = patterns[i];
993
- if (pat === undefined) continue;
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;