@bcts/envelope-pattern 1.0.0-alpha.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +48 -0
- package/README.md +13 -0
- package/dist/index.cjs +6781 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2628 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +2628 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.iife.js +6781 -0
- package/dist/index.iife.js.map +1 -0
- package/dist/index.mjs +6545 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +77 -0
- package/src/error.ts +262 -0
- package/src/format.ts +375 -0
- package/src/index.ts +27 -0
- package/src/parse/index.ts +923 -0
- package/src/parse/token.ts +906 -0
- package/src/parse/utils.ts +339 -0
- package/src/pattern/index.ts +719 -0
- package/src/pattern/leaf/array-pattern.ts +273 -0
- package/src/pattern/leaf/bool-pattern.ts +140 -0
- package/src/pattern/leaf/byte-string-pattern.ts +172 -0
- package/src/pattern/leaf/cbor-pattern.ts +355 -0
- package/src/pattern/leaf/date-pattern.ts +178 -0
- package/src/pattern/leaf/index.ts +280 -0
- package/src/pattern/leaf/known-value-pattern.ts +192 -0
- package/src/pattern/leaf/map-pattern.ts +152 -0
- package/src/pattern/leaf/null-pattern.ts +110 -0
- package/src/pattern/leaf/number-pattern.ts +248 -0
- package/src/pattern/leaf/tagged-pattern.ts +228 -0
- package/src/pattern/leaf/text-pattern.ts +165 -0
- package/src/pattern/matcher.ts +88 -0
- package/src/pattern/meta/and-pattern.ts +109 -0
- package/src/pattern/meta/any-pattern.ts +81 -0
- package/src/pattern/meta/capture-pattern.ts +111 -0
- package/src/pattern/meta/group-pattern.ts +110 -0
- package/src/pattern/meta/index.ts +269 -0
- package/src/pattern/meta/not-pattern.ts +91 -0
- package/src/pattern/meta/or-pattern.ts +146 -0
- package/src/pattern/meta/search-pattern.ts +201 -0
- package/src/pattern/meta/traverse-pattern.ts +146 -0
- package/src/pattern/structure/assertions-pattern.ts +244 -0
- package/src/pattern/structure/digest-pattern.ts +225 -0
- package/src/pattern/structure/index.ts +272 -0
- package/src/pattern/structure/leaf-structure-pattern.ts +85 -0
- package/src/pattern/structure/node-pattern.ts +188 -0
- package/src/pattern/structure/object-pattern.ts +149 -0
- package/src/pattern/structure/obscured-pattern.ts +159 -0
- package/src/pattern/structure/predicate-pattern.ts +151 -0
- package/src/pattern/structure/subject-pattern.ts +152 -0
- package/src/pattern/structure/wrapped-pattern.ts +195 -0
- package/src/pattern/vm.ts +1021 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @bcts/envelope-pattern - Obscured pattern matching
|
|
3
|
+
*
|
|
4
|
+
* This is a 1:1 TypeScript port of bc-envelope-pattern-rust obscured_pattern.rs
|
|
5
|
+
*
|
|
6
|
+
* @module envelope-pattern/pattern/structure/obscured-pattern
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Envelope } from "@bcts/envelope";
|
|
10
|
+
import type { Path } from "../../format";
|
|
11
|
+
import type { Matcher } from "../matcher";
|
|
12
|
+
import { compileAsAtomic } from "../matcher";
|
|
13
|
+
import type { Instr } from "../vm";
|
|
14
|
+
import type { Pattern } from "../index";
|
|
15
|
+
|
|
16
|
+
// Forward declaration for Pattern factory
|
|
17
|
+
let createStructureObscuredPattern: ((pattern: ObscuredPattern) => Pattern) | undefined;
|
|
18
|
+
|
|
19
|
+
export function registerObscuredPatternFactory(
|
|
20
|
+
factory: (pattern: ObscuredPattern) => Pattern,
|
|
21
|
+
): void {
|
|
22
|
+
createStructureObscuredPattern = factory;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Pattern type for obscured pattern matching.
|
|
27
|
+
*
|
|
28
|
+
* Corresponds to the Rust `ObscuredPattern` enum in obscured_pattern.rs
|
|
29
|
+
*/
|
|
30
|
+
export type ObscuredPatternType =
|
|
31
|
+
| { readonly type: "Any" }
|
|
32
|
+
| { readonly type: "Elided" }
|
|
33
|
+
| { readonly type: "Encrypted" }
|
|
34
|
+
| { readonly type: "Compressed" };
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Pattern for matching obscured elements.
|
|
38
|
+
*
|
|
39
|
+
* Corresponds to the Rust `ObscuredPattern` enum in obscured_pattern.rs
|
|
40
|
+
*/
|
|
41
|
+
export class ObscuredPattern implements Matcher {
|
|
42
|
+
readonly #pattern: ObscuredPatternType;
|
|
43
|
+
|
|
44
|
+
private constructor(pattern: ObscuredPatternType) {
|
|
45
|
+
this.#pattern = pattern;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Creates a new ObscuredPattern that matches any obscured element.
|
|
50
|
+
*/
|
|
51
|
+
static any(): ObscuredPattern {
|
|
52
|
+
return new ObscuredPattern({ type: "Any" });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Creates a new ObscuredPattern that matches any elided element.
|
|
57
|
+
*/
|
|
58
|
+
static elided(): ObscuredPattern {
|
|
59
|
+
return new ObscuredPattern({ type: "Elided" });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Creates a new ObscuredPattern that matches any encrypted element.
|
|
64
|
+
*/
|
|
65
|
+
static encrypted(): ObscuredPattern {
|
|
66
|
+
return new ObscuredPattern({ type: "Encrypted" });
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Creates a new ObscuredPattern that matches any compressed element.
|
|
71
|
+
*/
|
|
72
|
+
static compressed(): ObscuredPattern {
|
|
73
|
+
return new ObscuredPattern({ type: "Compressed" });
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Gets the pattern type.
|
|
78
|
+
*/
|
|
79
|
+
get patternType(): ObscuredPatternType {
|
|
80
|
+
return this.#pattern;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
pathsWithCaptures(haystack: Envelope): [Path[], Map<string, Path[]>] {
|
|
84
|
+
let isHit = false;
|
|
85
|
+
|
|
86
|
+
switch (this.#pattern.type) {
|
|
87
|
+
case "Any":
|
|
88
|
+
isHit = haystack.isObscured();
|
|
89
|
+
break;
|
|
90
|
+
case "Elided":
|
|
91
|
+
isHit = haystack.isElided();
|
|
92
|
+
break;
|
|
93
|
+
case "Encrypted":
|
|
94
|
+
isHit = haystack.isEncrypted();
|
|
95
|
+
break;
|
|
96
|
+
case "Compressed":
|
|
97
|
+
isHit = haystack.isCompressed();
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const paths = isHit ? [[haystack]] : [];
|
|
102
|
+
return [paths, new Map<string, Path[]>()];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
paths(haystack: Envelope): Path[] {
|
|
106
|
+
return this.pathsWithCaptures(haystack)[0];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
matches(haystack: Envelope): boolean {
|
|
110
|
+
return this.paths(haystack).length > 0;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
compile(code: Instr[], literals: Pattern[], captures: string[]): void {
|
|
114
|
+
if (createStructureObscuredPattern === undefined) {
|
|
115
|
+
throw new Error("ObscuredPattern factory not registered");
|
|
116
|
+
}
|
|
117
|
+
compileAsAtomic(createStructureObscuredPattern(this), code, literals, captures);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
isComplex(): boolean {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
toString(): string {
|
|
125
|
+
switch (this.#pattern.type) {
|
|
126
|
+
case "Any":
|
|
127
|
+
return "obscured";
|
|
128
|
+
case "Elided":
|
|
129
|
+
return "elided";
|
|
130
|
+
case "Encrypted":
|
|
131
|
+
return "encrypted";
|
|
132
|
+
case "Compressed":
|
|
133
|
+
return "compressed";
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Equality comparison.
|
|
139
|
+
*/
|
|
140
|
+
equals(other: ObscuredPattern): boolean {
|
|
141
|
+
return this.#pattern.type === other.#pattern.type;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Hash code for use in Maps/Sets.
|
|
146
|
+
*/
|
|
147
|
+
hashCode(): number {
|
|
148
|
+
switch (this.#pattern.type) {
|
|
149
|
+
case "Any":
|
|
150
|
+
return 0;
|
|
151
|
+
case "Elided":
|
|
152
|
+
return 1;
|
|
153
|
+
case "Encrypted":
|
|
154
|
+
return 2;
|
|
155
|
+
case "Compressed":
|
|
156
|
+
return 3;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @bcts/envelope-pattern - Predicate pattern matching
|
|
3
|
+
*
|
|
4
|
+
* This is a 1:1 TypeScript port of bc-envelope-pattern-rust predicate_pattern.rs
|
|
5
|
+
*
|
|
6
|
+
* @module envelope-pattern/pattern/structure/predicate-pattern
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Envelope } from "@bcts/envelope";
|
|
10
|
+
import type { Path } from "../../format";
|
|
11
|
+
import type { Matcher } from "../matcher";
|
|
12
|
+
import type { Instr } from "../vm";
|
|
13
|
+
import type { Pattern } from "../index";
|
|
14
|
+
|
|
15
|
+
// Forward declaration for Pattern factory
|
|
16
|
+
let createStructurePredicatePattern: ((pattern: PredicatePattern) => Pattern) | undefined;
|
|
17
|
+
|
|
18
|
+
export function registerPredicatePatternFactory(
|
|
19
|
+
factory: (pattern: PredicatePattern) => Pattern,
|
|
20
|
+
): void {
|
|
21
|
+
createStructurePredicatePattern = factory;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Pattern type for predicate pattern matching.
|
|
26
|
+
*
|
|
27
|
+
* Corresponds to the Rust `PredicatePattern` enum in predicate_pattern.rs
|
|
28
|
+
*/
|
|
29
|
+
export type PredicatePatternType =
|
|
30
|
+
| { readonly type: "Any" }
|
|
31
|
+
| { readonly type: "Pattern"; readonly pattern: Pattern };
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Pattern for matching predicates in envelopes.
|
|
35
|
+
*
|
|
36
|
+
* Corresponds to the Rust `PredicatePattern` enum in predicate_pattern.rs
|
|
37
|
+
*/
|
|
38
|
+
export class PredicatePattern implements Matcher {
|
|
39
|
+
readonly #pattern: PredicatePatternType;
|
|
40
|
+
|
|
41
|
+
private constructor(pattern: PredicatePatternType) {
|
|
42
|
+
this.#pattern = pattern;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Creates a new PredicatePattern that matches any predicate.
|
|
47
|
+
*/
|
|
48
|
+
static any(): PredicatePattern {
|
|
49
|
+
return new PredicatePattern({ type: "Any" });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Creates a new PredicatePattern that matches predicates matching the given pattern.
|
|
54
|
+
*/
|
|
55
|
+
static pattern(pattern: Pattern): PredicatePattern {
|
|
56
|
+
return new PredicatePattern({ type: "Pattern", pattern });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Gets the pattern type.
|
|
61
|
+
*/
|
|
62
|
+
get patternType(): PredicatePatternType {
|
|
63
|
+
return this.#pattern;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Gets the inner pattern if this is a Pattern type, undefined otherwise.
|
|
68
|
+
*/
|
|
69
|
+
innerPattern(): Pattern | undefined {
|
|
70
|
+
return this.#pattern.type === "Pattern" ? this.#pattern.pattern : undefined;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
pathsWithCaptures(haystack: Envelope): [Path[], Map<string, Path[]>] {
|
|
74
|
+
const predicate = haystack.asPredicate?.();
|
|
75
|
+
|
|
76
|
+
if (predicate === undefined) {
|
|
77
|
+
return [[], new Map<string, Path[]>()];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let paths: Path[];
|
|
81
|
+
|
|
82
|
+
switch (this.#pattern.type) {
|
|
83
|
+
case "Any":
|
|
84
|
+
paths = [[predicate]];
|
|
85
|
+
break;
|
|
86
|
+
case "Pattern": {
|
|
87
|
+
const innerMatcher = this.#pattern.pattern as unknown as Matcher;
|
|
88
|
+
if (innerMatcher.matches(predicate)) {
|
|
89
|
+
paths = [[predicate]];
|
|
90
|
+
} else {
|
|
91
|
+
paths = [];
|
|
92
|
+
}
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return [paths, new Map<string, Path[]>()];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
paths(haystack: Envelope): Path[] {
|
|
101
|
+
return this.pathsWithCaptures(haystack)[0];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
matches(haystack: Envelope): boolean {
|
|
105
|
+
return this.paths(haystack).length > 0;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
compile(code: Instr[], literals: Pattern[], _captures: string[]): void {
|
|
109
|
+
if (createStructurePredicatePattern === undefined) {
|
|
110
|
+
throw new Error("PredicatePattern factory not registered");
|
|
111
|
+
}
|
|
112
|
+
const idx = literals.length;
|
|
113
|
+
literals.push(createStructurePredicatePattern(this));
|
|
114
|
+
code.push({ type: "MatchStructure", literalIndex: idx });
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
isComplex(): boolean {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
toString(): string {
|
|
122
|
+
switch (this.#pattern.type) {
|
|
123
|
+
case "Any":
|
|
124
|
+
return "pred";
|
|
125
|
+
case "Pattern":
|
|
126
|
+
return `pred(${(this.#pattern.pattern as unknown as { toString(): string }).toString()})`;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Equality comparison.
|
|
132
|
+
*/
|
|
133
|
+
equals(other: PredicatePattern): boolean {
|
|
134
|
+
if (this.#pattern.type !== other.#pattern.type) {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
if (this.#pattern.type === "Any") {
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
const thisPattern = (this.#pattern as { type: "Pattern"; pattern: Pattern }).pattern;
|
|
141
|
+
const otherPattern = (other.#pattern as { type: "Pattern"; pattern: Pattern }).pattern;
|
|
142
|
+
return thisPattern === otherPattern;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Hash code for use in Maps/Sets.
|
|
147
|
+
*/
|
|
148
|
+
hashCode(): number {
|
|
149
|
+
return this.#pattern.type === "Any" ? 0 : 1;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @bcts/envelope-pattern - Subject pattern matching
|
|
3
|
+
*
|
|
4
|
+
* This is a 1:1 TypeScript port of bc-envelope-pattern-rust subject_pattern.rs
|
|
5
|
+
*
|
|
6
|
+
* @module envelope-pattern/pattern/structure/subject-pattern
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Envelope } from "@bcts/envelope";
|
|
10
|
+
import type { Path } from "../../format";
|
|
11
|
+
import type { Matcher } from "../matcher";
|
|
12
|
+
import type { Instr } from "../vm";
|
|
13
|
+
import type { Pattern } from "../index";
|
|
14
|
+
|
|
15
|
+
// Forward declaration for Pattern factory (used for late binding)
|
|
16
|
+
export let createStructureSubjectPattern: ((pattern: SubjectPattern) => Pattern) | undefined;
|
|
17
|
+
|
|
18
|
+
export function registerSubjectPatternFactory(factory: (pattern: SubjectPattern) => Pattern): void {
|
|
19
|
+
createStructureSubjectPattern = factory;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Pattern type for subject pattern matching.
|
|
24
|
+
*
|
|
25
|
+
* Corresponds to the Rust `SubjectPattern` enum in subject_pattern.rs
|
|
26
|
+
*/
|
|
27
|
+
export type SubjectPatternType =
|
|
28
|
+
| { readonly type: "Any" }
|
|
29
|
+
| { readonly type: "Pattern"; readonly pattern: Pattern };
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Pattern for matching subjects in envelopes.
|
|
33
|
+
*
|
|
34
|
+
* Corresponds to the Rust `SubjectPattern` enum in subject_pattern.rs
|
|
35
|
+
*/
|
|
36
|
+
export class SubjectPattern implements Matcher {
|
|
37
|
+
readonly #pattern: SubjectPatternType;
|
|
38
|
+
|
|
39
|
+
private constructor(pattern: SubjectPatternType) {
|
|
40
|
+
this.#pattern = pattern;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Creates a new SubjectPattern that matches any subject.
|
|
45
|
+
*/
|
|
46
|
+
static any(): SubjectPattern {
|
|
47
|
+
return new SubjectPattern({ type: "Any" });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Creates a new SubjectPattern that matches subjects matching the given pattern.
|
|
52
|
+
*/
|
|
53
|
+
static pattern(pattern: Pattern): SubjectPattern {
|
|
54
|
+
return new SubjectPattern({ type: "Pattern", pattern });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Gets the pattern type.
|
|
59
|
+
*/
|
|
60
|
+
get patternType(): SubjectPatternType {
|
|
61
|
+
return this.#pattern;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Gets the inner pattern if this is a Pattern type, undefined otherwise.
|
|
66
|
+
*/
|
|
67
|
+
innerPattern(): Pattern | undefined {
|
|
68
|
+
return this.#pattern.type === "Pattern" ? this.#pattern.pattern : undefined;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
pathsWithCaptures(haystack: Envelope): [Path[], Map<string, Path[]>] {
|
|
72
|
+
const subject = haystack.subject();
|
|
73
|
+
let paths: Path[];
|
|
74
|
+
|
|
75
|
+
switch (this.#pattern.type) {
|
|
76
|
+
case "Any":
|
|
77
|
+
paths = [[subject]];
|
|
78
|
+
break;
|
|
79
|
+
case "Pattern": {
|
|
80
|
+
const innerMatcher = this.#pattern.pattern as unknown as Matcher;
|
|
81
|
+
if (innerMatcher.matches(subject)) {
|
|
82
|
+
paths = [[subject]];
|
|
83
|
+
} else {
|
|
84
|
+
paths = [];
|
|
85
|
+
}
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return [paths, new Map<string, Path[]>()];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
paths(haystack: Envelope): Path[] {
|
|
94
|
+
return this.pathsWithCaptures(haystack)[0];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
matches(haystack: Envelope): boolean {
|
|
98
|
+
return this.paths(haystack).length > 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
compile(code: Instr[], literals: Pattern[], captures: string[]): void {
|
|
102
|
+
switch (this.#pattern.type) {
|
|
103
|
+
case "Any":
|
|
104
|
+
code.push({ type: "NavigateSubject" });
|
|
105
|
+
break;
|
|
106
|
+
case "Pattern":
|
|
107
|
+
// Navigate to the subject first
|
|
108
|
+
code.push({ type: "NavigateSubject" });
|
|
109
|
+
// Save the path and run the inner pattern relative to the subject
|
|
110
|
+
code.push({ type: "ExtendTraversal" });
|
|
111
|
+
(this.#pattern.pattern as unknown as Matcher).compile(code, literals, captures);
|
|
112
|
+
code.push({ type: "CombineTraversal" });
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
isComplex(): boolean {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
toString(): string {
|
|
122
|
+
switch (this.#pattern.type) {
|
|
123
|
+
case "Any":
|
|
124
|
+
return "subj";
|
|
125
|
+
case "Pattern":
|
|
126
|
+
return `subj(${(this.#pattern.pattern as unknown as { toString(): string }).toString()})`;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Equality comparison.
|
|
132
|
+
*/
|
|
133
|
+
equals(other: SubjectPattern): boolean {
|
|
134
|
+
if (this.#pattern.type !== other.#pattern.type) {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
if (this.#pattern.type === "Any") {
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
// 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;
|
|
143
|
+
return thisPattern === otherPattern; // Reference equality for now
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Hash code for use in Maps/Sets.
|
|
148
|
+
*/
|
|
149
|
+
hashCode(): number {
|
|
150
|
+
return this.#pattern.type === "Any" ? 0 : 1;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @bcts/envelope-pattern - Wrapped pattern matching
|
|
3
|
+
*
|
|
4
|
+
* This is a 1:1 TypeScript port of bc-envelope-pattern-rust wrapped_pattern.rs
|
|
5
|
+
*
|
|
6
|
+
* @module envelope-pattern/pattern/structure/wrapped-pattern
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Envelope } from "@bcts/envelope";
|
|
10
|
+
import type { Path } from "../../format";
|
|
11
|
+
import type { Matcher } from "../matcher";
|
|
12
|
+
import type { Instr, Axis } from "../vm";
|
|
13
|
+
import type { Pattern } from "../index";
|
|
14
|
+
|
|
15
|
+
// Forward declaration for Pattern factory
|
|
16
|
+
let createStructureWrappedPattern: ((pattern: WrappedPattern) => Pattern) | undefined;
|
|
17
|
+
|
|
18
|
+
export function registerWrappedPatternFactory(factory: (pattern: WrappedPattern) => Pattern): void {
|
|
19
|
+
createStructureWrappedPattern = factory;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Pattern type for wrapped pattern matching.
|
|
24
|
+
*
|
|
25
|
+
* Corresponds to the Rust `WrappedPattern` enum in wrapped_pattern.rs
|
|
26
|
+
*/
|
|
27
|
+
export type WrappedPatternType =
|
|
28
|
+
| { readonly type: "Any" }
|
|
29
|
+
| { readonly type: "Unwrap"; readonly pattern: Pattern };
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Represents patterns for matching wrapped envelopes.
|
|
33
|
+
*
|
|
34
|
+
* Corresponds to the Rust `WrappedPattern` enum in wrapped_pattern.rs
|
|
35
|
+
*/
|
|
36
|
+
export class WrappedPattern implements Matcher {
|
|
37
|
+
readonly #pattern: WrappedPatternType;
|
|
38
|
+
|
|
39
|
+
private constructor(pattern: WrappedPatternType) {
|
|
40
|
+
this.#pattern = pattern;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Creates a new WrappedPattern that matches any wrapped envelope without descending.
|
|
45
|
+
*/
|
|
46
|
+
static new(): WrappedPattern {
|
|
47
|
+
return new WrappedPattern({ type: "Any" });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Creates a new WrappedPattern that matches a wrapped envelope and also matches
|
|
52
|
+
* on its unwrapped content.
|
|
53
|
+
*/
|
|
54
|
+
static unwrapMatching(pattern: Pattern): WrappedPattern {
|
|
55
|
+
return new WrappedPattern({ type: "Unwrap", pattern });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Creates a new WrappedPattern that matches any wrapped envelope and descends into it.
|
|
60
|
+
* Note: This requires Pattern.any() to be available, so it's set up during registration.
|
|
61
|
+
*/
|
|
62
|
+
static unwrap(): WrappedPattern {
|
|
63
|
+
// This will be filled in when Pattern.any() is available
|
|
64
|
+
// For now, create a placeholder that will be replaced
|
|
65
|
+
return new WrappedPattern({ type: "Any" }); // Will be overwritten
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Gets the pattern type.
|
|
70
|
+
*/
|
|
71
|
+
get patternType(): WrappedPatternType {
|
|
72
|
+
return this.#pattern;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Gets the inner pattern if this is an Unwrap type, undefined otherwise.
|
|
77
|
+
*/
|
|
78
|
+
innerPattern(): Pattern | undefined {
|
|
79
|
+
return this.#pattern.type === "Unwrap" ? this.#pattern.pattern : undefined;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
pathsWithCaptures(haystack: Envelope): [Path[], Map<string, Path[]>] {
|
|
83
|
+
const subject = haystack.subject();
|
|
84
|
+
|
|
85
|
+
if (!subject.isWrapped()) {
|
|
86
|
+
return [[], new Map<string, Path[]>()];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
let paths: Path[];
|
|
90
|
+
|
|
91
|
+
switch (this.#pattern.type) {
|
|
92
|
+
case "Any":
|
|
93
|
+
// Just match the wrapped envelope itself, don't descend
|
|
94
|
+
paths = [[haystack]];
|
|
95
|
+
break;
|
|
96
|
+
case "Unwrap": {
|
|
97
|
+
// Match the content of the wrapped envelope
|
|
98
|
+
const unwrapped = subject.tryUnwrap?.();
|
|
99
|
+
if (unwrapped !== undefined) {
|
|
100
|
+
const innerMatcher = this.#pattern.pattern as unknown as Matcher;
|
|
101
|
+
const innerPaths = innerMatcher.paths(unwrapped);
|
|
102
|
+
paths = innerPaths.map((path) => {
|
|
103
|
+
// Add the current envelope to the path
|
|
104
|
+
return [haystack, ...path];
|
|
105
|
+
});
|
|
106
|
+
} else {
|
|
107
|
+
paths = [];
|
|
108
|
+
}
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return [paths, new Map<string, Path[]>()];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
paths(haystack: Envelope): Path[] {
|
|
117
|
+
return this.pathsWithCaptures(haystack)[0];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
matches(haystack: Envelope): boolean {
|
|
121
|
+
return this.paths(haystack).length > 0;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
compile(code: Instr[], literals: Pattern[], captures: string[]): void {
|
|
125
|
+
if (createStructureWrappedPattern === undefined) {
|
|
126
|
+
throw new Error("WrappedPattern factory not registered");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
switch (this.#pattern.type) {
|
|
130
|
+
case "Any": {
|
|
131
|
+
// Just match the wrapped envelope itself, don't descend
|
|
132
|
+
const idx = literals.length;
|
|
133
|
+
literals.push(createStructureWrappedPattern(this));
|
|
134
|
+
code.push({ type: "MatchStructure", literalIndex: idx });
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
case "Unwrap": {
|
|
138
|
+
// First match that it's wrapped
|
|
139
|
+
const idx = literals.length;
|
|
140
|
+
literals.push(createStructureWrappedPattern(WrappedPattern.new()));
|
|
141
|
+
code.push({ type: "MatchStructure", literalIndex: idx });
|
|
142
|
+
|
|
143
|
+
// Then move into inner envelope
|
|
144
|
+
const axis: Axis = "Wrapped";
|
|
145
|
+
code.push({ type: "PushAxis", axis });
|
|
146
|
+
|
|
147
|
+
// Then match the pattern
|
|
148
|
+
const innerMatcher = this.#pattern.pattern as unknown as Matcher;
|
|
149
|
+
innerMatcher.compile(code, literals, captures);
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
isComplex(): boolean {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
toString(): string {
|
|
160
|
+
switch (this.#pattern.type) {
|
|
161
|
+
case "Any":
|
|
162
|
+
return "wrapped";
|
|
163
|
+
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();
|
|
166
|
+
if (patternStr === "*") {
|
|
167
|
+
return "unwrap";
|
|
168
|
+
}
|
|
169
|
+
return `unwrap(${patternStr})`;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Equality comparison.
|
|
176
|
+
*/
|
|
177
|
+
equals(other: WrappedPattern): boolean {
|
|
178
|
+
if (this.#pattern.type !== other.#pattern.type) {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
if (this.#pattern.type === "Any") {
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
const thisPattern = (this.#pattern as { type: "Unwrap"; pattern: Pattern }).pattern;
|
|
185
|
+
const otherPattern = (other.#pattern as { type: "Unwrap"; pattern: Pattern }).pattern;
|
|
186
|
+
return thisPattern === otherPattern;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Hash code for use in Maps/Sets.
|
|
191
|
+
*/
|
|
192
|
+
hashCode(): number {
|
|
193
|
+
return this.#pattern.type === "Any" ? 0 : 1;
|
|
194
|
+
}
|
|
195
|
+
}
|