@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.
Files changed (46) hide show
  1. package/README.md +1 -1
  2. package/dist/index.cjs +1992 -1714
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +147 -31
  5. package/dist/index.d.cts.map +1 -1
  6. package/dist/index.d.mts +147 -31
  7. package/dist/index.d.mts.map +1 -1
  8. package/dist/index.iife.js +1984 -1707
  9. package/dist/index.iife.js.map +1 -1
  10. package/dist/index.mjs +1966 -1698
  11. package/dist/index.mjs.map +1 -1
  12. package/package.json +9 -9
  13. package/src/format.ts +32 -13
  14. package/src/parse/index.ts +138 -5
  15. package/src/parse/token.ts +59 -58
  16. package/src/pattern/index.ts +110 -2
  17. package/src/pattern/leaf/array-pattern.ts +26 -26
  18. package/src/pattern/leaf/bool-pattern.ts +12 -12
  19. package/src/pattern/leaf/byte-string-pattern.ts +15 -15
  20. package/src/pattern/leaf/cbor-pattern.ts +31 -31
  21. package/src/pattern/leaf/date-pattern.ts +9 -9
  22. package/src/pattern/leaf/index.ts +1 -2
  23. package/src/pattern/leaf/known-value-pattern.ts +21 -20
  24. package/src/pattern/leaf/map-pattern.ts +14 -14
  25. package/src/pattern/leaf/null-pattern.ts +8 -8
  26. package/src/pattern/leaf/number-pattern.ts +20 -20
  27. package/src/pattern/leaf/tagged-pattern.ts +20 -20
  28. package/src/pattern/leaf/text-pattern.ts +14 -14
  29. package/src/pattern/matcher.ts +88 -3
  30. package/src/pattern/meta/and-pattern.ts +19 -18
  31. package/src/pattern/meta/capture-pattern.ts +16 -17
  32. package/src/pattern/meta/group-pattern.ts +20 -17
  33. package/src/pattern/meta/not-pattern.ts +9 -8
  34. package/src/pattern/meta/or-pattern.ts +30 -25
  35. package/src/pattern/meta/search-pattern.ts +17 -17
  36. package/src/pattern/meta/traverse-pattern.ts +42 -18
  37. package/src/pattern/structure/assertions-pattern.ts +31 -32
  38. package/src/pattern/structure/digest-pattern.ts +23 -23
  39. package/src/pattern/structure/index.ts +1 -0
  40. package/src/pattern/structure/node-pattern.ts +17 -17
  41. package/src/pattern/structure/object-pattern.ts +14 -15
  42. package/src/pattern/structure/obscured-pattern.ts +7 -7
  43. package/src/pattern/structure/predicate-pattern.ts +14 -15
  44. package/src/pattern/structure/subject-pattern.ts +16 -17
  45. package/src/pattern/structure/wrapped-pattern.ts +40 -19
  46. package/src/pattern/vm.ts +12 -11
@@ -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
 
@@ -34,10 +34,10 @@ export type ObjectPatternType =
34
34
  * Corresponds to the Rust `ObjectPattern` enum in object_pattern.rs
35
35
  */
36
36
  export class ObjectPattern implements Matcher {
37
- readonly #pattern: ObjectPatternType;
37
+ private readonly _pattern: ObjectPatternType;
38
38
 
39
39
  private constructor(pattern: ObjectPatternType) {
40
- this.#pattern = pattern;
40
+ this._pattern = pattern;
41
41
  }
42
42
 
43
43
  /**
@@ -58,14 +58,14 @@ export class ObjectPattern implements Matcher {
58
58
  * Gets the pattern type.
59
59
  */
60
60
  get patternType(): ObjectPatternType {
61
- return this.#pattern;
61
+ return this._pattern;
62
62
  }
63
63
 
64
64
  /**
65
65
  * Gets the inner pattern if this is a Pattern type, undefined otherwise.
66
66
  */
67
67
  innerPattern(): Pattern | undefined {
68
- return this.#pattern.type === "Pattern" ? this.#pattern.pattern : undefined;
68
+ return this._pattern.type === "Pattern" ? this._pattern.pattern : undefined;
69
69
  }
70
70
 
71
71
  pathsWithCaptures(haystack: Envelope): [Path[], Map<string, Path[]>] {
@@ -77,13 +77,12 @@ export class ObjectPattern implements Matcher {
77
77
 
78
78
  let paths: Path[];
79
79
 
80
- switch (this.#pattern.type) {
80
+ switch (this._pattern.type) {
81
81
  case "Any":
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 = [];
@@ -117,11 +116,11 @@ export class ObjectPattern implements Matcher {
117
116
  }
118
117
 
119
118
  toString(): string {
120
- switch (this.#pattern.type) {
119
+ switch (this._pattern.type) {
121
120
  case "Any":
122
121
  return "obj";
123
122
  case "Pattern":
124
- return `obj(${(this.#pattern.pattern as unknown as { toString(): string }).toString()})`;
123
+ return `obj(${(this._pattern.pattern as unknown as { toString(): string }).toString()})`;
125
124
  }
126
125
  }
127
126
 
@@ -129,14 +128,14 @@ export class ObjectPattern implements Matcher {
129
128
  * Equality comparison.
130
129
  */
131
130
  equals(other: ObjectPattern): boolean {
132
- if (this.#pattern.type !== other.#pattern.type) {
131
+ if (this._pattern.type !== other._pattern.type) {
133
132
  return false;
134
133
  }
135
- if (this.#pattern.type === "Any") {
134
+ if (this._pattern.type === "Any") {
136
135
  return true;
137
136
  }
138
- const thisPattern = (this.#pattern as { type: "Pattern"; pattern: Pattern }).pattern;
139
- const otherPattern = (other.#pattern as { type: "Pattern"; pattern: Pattern }).pattern;
137
+ const thisPattern = (this._pattern as { type: "Pattern"; pattern: Pattern }).pattern;
138
+ const otherPattern = (other._pattern as { type: "Pattern"; pattern: Pattern }).pattern;
140
139
  return thisPattern === otherPattern;
141
140
  }
142
141
 
@@ -144,6 +143,6 @@ export class ObjectPattern implements Matcher {
144
143
  * Hash code for use in Maps/Sets.
145
144
  */
146
145
  hashCode(): number {
147
- return this.#pattern.type === "Any" ? 0 : 1;
146
+ return this._pattern.type === "Any" ? 0 : 1;
148
147
  }
149
148
  }
@@ -39,10 +39,10 @@ export type ObscuredPatternType =
39
39
  * Corresponds to the Rust `ObscuredPattern` enum in obscured_pattern.rs
40
40
  */
41
41
  export class ObscuredPattern implements Matcher {
42
- readonly #pattern: ObscuredPatternType;
42
+ private readonly _pattern: ObscuredPatternType;
43
43
 
44
44
  private constructor(pattern: ObscuredPatternType) {
45
- this.#pattern = pattern;
45
+ this._pattern = pattern;
46
46
  }
47
47
 
48
48
  /**
@@ -77,13 +77,13 @@ export class ObscuredPattern implements Matcher {
77
77
  * Gets the pattern type.
78
78
  */
79
79
  get patternType(): ObscuredPatternType {
80
- return this.#pattern;
80
+ return this._pattern;
81
81
  }
82
82
 
83
83
  pathsWithCaptures(haystack: Envelope): [Path[], Map<string, Path[]>] {
84
84
  let isHit = false;
85
85
 
86
- switch (this.#pattern.type) {
86
+ switch (this._pattern.type) {
87
87
  case "Any":
88
88
  isHit = haystack.isObscured();
89
89
  break;
@@ -122,7 +122,7 @@ export class ObscuredPattern implements Matcher {
122
122
  }
123
123
 
124
124
  toString(): string {
125
- switch (this.#pattern.type) {
125
+ switch (this._pattern.type) {
126
126
  case "Any":
127
127
  return "obscured";
128
128
  case "Elided":
@@ -138,14 +138,14 @@ export class ObscuredPattern implements Matcher {
138
138
  * Equality comparison.
139
139
  */
140
140
  equals(other: ObscuredPattern): boolean {
141
- return this.#pattern.type === other.#pattern.type;
141
+ return this._pattern.type === other._pattern.type;
142
142
  }
143
143
 
144
144
  /**
145
145
  * Hash code for use in Maps/Sets.
146
146
  */
147
147
  hashCode(): number {
148
- switch (this.#pattern.type) {
148
+ switch (this._pattern.type) {
149
149
  case "Any":
150
150
  return 0;
151
151
  case "Elided":
@@ -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
 
@@ -36,10 +36,10 @@ export type PredicatePatternType =
36
36
  * Corresponds to the Rust `PredicatePattern` enum in predicate_pattern.rs
37
37
  */
38
38
  export class PredicatePattern implements Matcher {
39
- readonly #pattern: PredicatePatternType;
39
+ private readonly _pattern: PredicatePatternType;
40
40
 
41
41
  private constructor(pattern: PredicatePatternType) {
42
- this.#pattern = pattern;
42
+ this._pattern = pattern;
43
43
  }
44
44
 
45
45
  /**
@@ -60,14 +60,14 @@ export class PredicatePattern implements Matcher {
60
60
  * Gets the pattern type.
61
61
  */
62
62
  get patternType(): PredicatePatternType {
63
- return this.#pattern;
63
+ return this._pattern;
64
64
  }
65
65
 
66
66
  /**
67
67
  * Gets the inner pattern if this is a Pattern type, undefined otherwise.
68
68
  */
69
69
  innerPattern(): Pattern | undefined {
70
- return this.#pattern.type === "Pattern" ? this.#pattern.pattern : undefined;
70
+ return this._pattern.type === "Pattern" ? this._pattern.pattern : undefined;
71
71
  }
72
72
 
73
73
  pathsWithCaptures(haystack: Envelope): [Path[], Map<string, Path[]>] {
@@ -79,13 +79,12 @@ export class PredicatePattern implements Matcher {
79
79
 
80
80
  let paths: Path[];
81
81
 
82
- switch (this.#pattern.type) {
82
+ switch (this._pattern.type) {
83
83
  case "Any":
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 = [];
@@ -119,11 +118,11 @@ export class PredicatePattern implements Matcher {
119
118
  }
120
119
 
121
120
  toString(): string {
122
- switch (this.#pattern.type) {
121
+ switch (this._pattern.type) {
123
122
  case "Any":
124
123
  return "pred";
125
124
  case "Pattern":
126
- return `pred(${(this.#pattern.pattern as unknown as { toString(): string }).toString()})`;
125
+ return `pred(${(this._pattern.pattern as unknown as { toString(): string }).toString()})`;
127
126
  }
128
127
  }
129
128
 
@@ -131,14 +130,14 @@ export class PredicatePattern implements Matcher {
131
130
  * Equality comparison.
132
131
  */
133
132
  equals(other: PredicatePattern): boolean {
134
- if (this.#pattern.type !== other.#pattern.type) {
133
+ if (this._pattern.type !== other._pattern.type) {
135
134
  return false;
136
135
  }
137
- if (this.#pattern.type === "Any") {
136
+ if (this._pattern.type === "Any") {
138
137
  return true;
139
138
  }
140
- const thisPattern = (this.#pattern as { type: "Pattern"; pattern: Pattern }).pattern;
141
- const otherPattern = (other.#pattern as { type: "Pattern"; pattern: Pattern }).pattern;
139
+ const thisPattern = (this._pattern as { type: "Pattern"; pattern: Pattern }).pattern;
140
+ const otherPattern = (other._pattern as { type: "Pattern"; pattern: Pattern }).pattern;
142
141
  return thisPattern === otherPattern;
143
142
  }
144
143
 
@@ -146,6 +145,6 @@ export class PredicatePattern implements Matcher {
146
145
  * Hash code for use in Maps/Sets.
147
146
  */
148
147
  hashCode(): number {
149
- return this.#pattern.type === "Any" ? 0 : 1;
148
+ return this._pattern.type === "Any" ? 0 : 1;
150
149
  }
151
150
  }
@@ -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
 
@@ -34,10 +34,10 @@ export type SubjectPatternType =
34
34
  * Corresponds to the Rust `SubjectPattern` enum in subject_pattern.rs
35
35
  */
36
36
  export class SubjectPattern implements Matcher {
37
- readonly #pattern: SubjectPatternType;
37
+ private readonly _pattern: SubjectPatternType;
38
38
 
39
39
  private constructor(pattern: SubjectPatternType) {
40
- this.#pattern = pattern;
40
+ this._pattern = pattern;
41
41
  }
42
42
 
43
43
  /**
@@ -58,27 +58,26 @@ export class SubjectPattern implements Matcher {
58
58
  * Gets the pattern type.
59
59
  */
60
60
  get patternType(): SubjectPatternType {
61
- return this.#pattern;
61
+ return this._pattern;
62
62
  }
63
63
 
64
64
  /**
65
65
  * Gets the inner pattern if this is a Pattern type, undefined otherwise.
66
66
  */
67
67
  innerPattern(): Pattern | undefined {
68
- return this.#pattern.type === "Pattern" ? this.#pattern.pattern : undefined;
68
+ return this._pattern.type === "Pattern" ? this._pattern.pattern : undefined;
69
69
  }
70
70
 
71
71
  pathsWithCaptures(haystack: Envelope): [Path[], Map<string, Path[]>] {
72
72
  const subject = haystack.subject();
73
73
  let paths: Path[];
74
74
 
75
- switch (this.#pattern.type) {
75
+ switch (this._pattern.type) {
76
76
  case "Any":
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 = [];
@@ -99,7 +98,7 @@ export class SubjectPattern implements Matcher {
99
98
  }
100
99
 
101
100
  compile(code: Instr[], literals: Pattern[], captures: string[]): void {
102
- switch (this.#pattern.type) {
101
+ switch (this._pattern.type) {
103
102
  case "Any":
104
103
  code.push({ type: "NavigateSubject" });
105
104
  break;
@@ -108,7 +107,7 @@ export class SubjectPattern implements Matcher {
108
107
  code.push({ type: "NavigateSubject" });
109
108
  // Save the path and run the inner pattern relative to the subject
110
109
  code.push({ type: "ExtendTraversal" });
111
- (this.#pattern.pattern as unknown as Matcher).compile(code, literals, captures);
110
+ (this._pattern.pattern as unknown as Matcher).compile(code, literals, captures);
112
111
  code.push({ type: "CombineTraversal" });
113
112
  break;
114
113
  }
@@ -119,11 +118,11 @@ export class SubjectPattern implements Matcher {
119
118
  }
120
119
 
121
120
  toString(): string {
122
- switch (this.#pattern.type) {
121
+ switch (this._pattern.type) {
123
122
  case "Any":
124
123
  return "subj";
125
124
  case "Pattern":
126
- return `subj(${(this.#pattern.pattern as unknown as { toString(): string }).toString()})`;
125
+ return `subj(${(this._pattern.pattern as unknown as { toString(): string }).toString()})`;
127
126
  }
128
127
  }
129
128
 
@@ -131,15 +130,15 @@ export class SubjectPattern implements Matcher {
131
130
  * Equality comparison.
132
131
  */
133
132
  equals(other: SubjectPattern): boolean {
134
- if (this.#pattern.type !== other.#pattern.type) {
133
+ if (this._pattern.type !== other._pattern.type) {
135
134
  return false;
136
135
  }
137
- if (this.#pattern.type === "Any") {
136
+ if (this._pattern.type === "Any") {
138
137
  return true;
139
138
  }
140
139
  // For Pattern type, compare the inner patterns
141
- const thisPattern = (this.#pattern as { type: "Pattern"; pattern: Pattern }).pattern;
142
- const otherPattern = (other.#pattern as { type: "Pattern"; pattern: Pattern }).pattern;
140
+ const thisPattern = (this._pattern as { type: "Pattern"; pattern: Pattern }).pattern;
141
+ const otherPattern = (other._pattern as { type: "Pattern"; pattern: Pattern }).pattern;
143
142
  return thisPattern === otherPattern; // Reference equality for now
144
143
  }
145
144
 
@@ -147,6 +146,6 @@ export class SubjectPattern implements Matcher {
147
146
  * Hash code for use in Maps/Sets.
148
147
  */
149
148
  hashCode(): number {
150
- return this.#pattern.type === "Any" ? 0 : 1;
149
+ return this._pattern.type === "Any" ? 0 : 1;
151
150
  }
152
151
  }
@@ -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
  *
@@ -34,10 +53,10 @@ export type WrappedPatternType =
34
53
  * Corresponds to the Rust `WrappedPattern` enum in wrapped_pattern.rs
35
54
  */
36
55
  export class WrappedPattern implements Matcher {
37
- readonly #pattern: WrappedPatternType;
56
+ private readonly _pattern: WrappedPatternType;
38
57
 
39
58
  private constructor(pattern: WrappedPatternType) {
40
- this.#pattern = pattern;
59
+ this._pattern = pattern;
41
60
  }
42
61
 
43
62
  /**
@@ -69,14 +88,14 @@ export class WrappedPattern implements Matcher {
69
88
  * Gets the pattern type.
70
89
  */
71
90
  get patternType(): WrappedPatternType {
72
- return this.#pattern;
91
+ return this._pattern;
73
92
  }
74
93
 
75
94
  /**
76
95
  * Gets the inner pattern if this is an Unwrap type, undefined otherwise.
77
96
  */
78
97
  innerPattern(): Pattern | undefined {
79
- return this.#pattern.type === "Unwrap" ? this.#pattern.pattern : undefined;
98
+ return this._pattern.type === "Unwrap" ? this._pattern.pattern : undefined;
80
99
  }
81
100
 
82
101
  pathsWithCaptures(haystack: Envelope): [Path[], Map<string, Path[]>] {
@@ -88,7 +107,7 @@ export class WrappedPattern implements Matcher {
88
107
 
89
108
  let paths: Path[];
90
109
 
91
- switch (this.#pattern.type) {
110
+ switch (this._pattern.type) {
92
111
  case "Any":
93
112
  // Just match the wrapped envelope itself, don't descend
94
113
  paths = [[haystack]];
@@ -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];
@@ -126,7 +144,7 @@ export class WrappedPattern implements Matcher {
126
144
  throw new Error("WrappedPattern factory not registered");
127
145
  }
128
146
 
129
- switch (this.#pattern.type) {
147
+ switch (this._pattern.type) {
130
148
  case "Any": {
131
149
  // Just match the wrapped envelope itself, don't descend
132
150
  const idx = literals.length;
@@ -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
  }
@@ -157,12 +176,14 @@ export class WrappedPattern implements Matcher {
157
176
  }
158
177
 
159
178
  toString(): string {
160
- switch (this.#pattern.type) {
179
+ switch (this._pattern.type) {
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
  }
@@ -175,14 +196,14 @@ export class WrappedPattern implements Matcher {
175
196
  * Equality comparison.
176
197
  */
177
198
  equals(other: WrappedPattern): boolean {
178
- if (this.#pattern.type !== other.#pattern.type) {
199
+ if (this._pattern.type !== other._pattern.type) {
179
200
  return false;
180
201
  }
181
- if (this.#pattern.type === "Any") {
202
+ if (this._pattern.type === "Any") {
182
203
  return true;
183
204
  }
184
- const thisPattern = (this.#pattern as { type: "Unwrap"; pattern: Pattern }).pattern;
185
- const otherPattern = (other.#pattern as { type: "Unwrap"; pattern: Pattern }).pattern;
205
+ const thisPattern = (this._pattern as { type: "Unwrap"; pattern: Pattern }).pattern;
206
+ const otherPattern = (other._pattern as { type: "Unwrap"; pattern: Pattern }).pattern;
186
207
  return thisPattern === otherPattern;
187
208
  }
188
209
 
@@ -190,6 +211,6 @@ export class WrappedPattern implements Matcher {
190
211
  * Hash code for use in Maps/Sets.
191
212
  */
192
213
  hashCode(): number {
193
- return this.#pattern.type === "Any" ? 0 : 1;
214
+ return this._pattern.type === "Any" ? 0 : 1;
194
215
  }
195
216
  }
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;