@bcts/envelope-pattern 1.0.0-alpha.22 → 1.0.0-beta.0
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/dist/index.cjs +1291 -826
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +101 -59
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +102 -60
- package/dist/index.d.mts.map +1 -1
- package/dist/index.iife.js +1643 -1179
- package/dist/index.iife.js.map +1 -1
- package/dist/index.mjs +1315 -853
- package/dist/index.mjs.map +1 -1
- package/package.json +13 -11
- package/src/error.ts +1 -1
- package/src/format.ts +19 -31
- package/src/parse/index.ts +17 -1010
- package/src/parse/leaf/array-parser.ts +36 -0
- package/src/parse/leaf/cbor-parser.ts +43 -0
- package/src/parse/leaf/date-parser.ts +81 -0
- package/src/parse/leaf/known-value-parser.ts +73 -0
- package/src/parse/leaf/null-parser.ts +16 -0
- package/src/parse/leaf/number-parser.ts +90 -0
- package/src/parse/leaf/tag-parser.ts +160 -0
- package/src/parse/meta/and-parser.ts +40 -0
- package/src/parse/meta/capture-parser.ts +50 -0
- package/src/parse/meta/group-parser.ts +77 -0
- package/src/parse/meta/not-parser.ts +30 -0
- package/src/parse/meta/or-parser.ts +36 -0
- package/src/parse/meta/primary-parser.ts +234 -0
- package/src/parse/meta/search-parser.ts +41 -0
- package/src/parse/meta/traverse-parser.ts +42 -0
- package/src/parse/structure/assertion-obj-parser.ts +44 -0
- package/src/parse/structure/assertion-parser.ts +22 -0
- package/src/parse/structure/assertion-pred-parser.ts +45 -0
- package/src/parse/structure/compressed-parser.ts +17 -0
- package/src/parse/structure/digest-parser.ts +132 -0
- package/src/parse/structure/elided-parser.ts +17 -0
- package/src/parse/structure/encrypted-parser.ts +17 -0
- package/src/parse/structure/node-parser.ts +54 -0
- package/src/parse/structure/object-parser.ts +32 -0
- package/src/parse/structure/obscured-parser.ts +17 -0
- package/src/parse/structure/predicate-parser.ts +32 -0
- package/src/parse/structure/subject-parser.ts +32 -0
- package/src/parse/structure/wrapped-parser.ts +36 -0
- package/src/pattern/dcbor-integration.ts +40 -8
- package/src/pattern/index.ts +29 -0
- package/src/pattern/leaf/array-pattern.ts +67 -169
- package/src/pattern/leaf/cbor-pattern.ts +37 -23
- package/src/pattern/leaf/index.ts +1 -1
- package/src/pattern/leaf/map-pattern.ts +21 -2
- package/src/pattern/leaf/tagged-pattern.ts +6 -1
- package/src/pattern/meta/search-pattern.ts +13 -38
- package/src/pattern/meta/traverse-pattern.ts +2 -2
- package/src/pattern/structure/assertions-pattern.ts +19 -53
- package/src/pattern/structure/digest-pattern.ts +18 -22
- package/src/pattern/structure/index.ts +3 -0
- package/src/pattern/structure/node-pattern.ts +10 -29
- package/src/pattern/structure/object-pattern.ts +2 -2
- package/src/pattern/structure/predicate-pattern.ts +2 -2
- package/src/pattern/structure/subject-pattern.ts +31 -4
- package/src/pattern/structure/wrapped-pattern.ts +28 -9
- package/src/pattern/vm.ts +4 -4
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
3
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
4
|
+
*
|
|
5
|
+
* Node parser — port of `bc-envelope-pattern-rust`
|
|
6
|
+
* `parse/structure/node_parser.rs`.
|
|
7
|
+
*
|
|
8
|
+
* Mirrors Rust:
|
|
9
|
+
* - Bare `node` → `any_node()`.
|
|
10
|
+
* - `node({n})` / `node({n,m})` / `node({n,})` → `node_with_assertions_range`.
|
|
11
|
+
* - Anything else inside the parens (e.g. `node(text)`) is a syntax error
|
|
12
|
+
* in Rust; the previous TS port silently produced an always-matching
|
|
13
|
+
* `WithSubject` node, which we have removed.
|
|
14
|
+
*
|
|
15
|
+
* @module envelope-pattern/parse/structure/node-parser
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
type Result,
|
|
20
|
+
err,
|
|
21
|
+
expectedCloseParen,
|
|
22
|
+
ok,
|
|
23
|
+
unexpectedEndOfInput,
|
|
24
|
+
unexpectedToken,
|
|
25
|
+
} from "../../error";
|
|
26
|
+
import { type Pattern, NodePattern, anyNode, patternStructure, structureNode } from "../../pattern";
|
|
27
|
+
import type { Lexer } from "../token";
|
|
28
|
+
|
|
29
|
+
export function parseNode(lexer: Lexer): Result<Pattern> {
|
|
30
|
+
const next = lexer.peekToken();
|
|
31
|
+
if (next?.token.type !== "ParenOpen") {
|
|
32
|
+
return ok(anyNode());
|
|
33
|
+
}
|
|
34
|
+
lexer.next(); // consume (
|
|
35
|
+
|
|
36
|
+
const inner = lexer.next();
|
|
37
|
+
if (inner === undefined) {
|
|
38
|
+
return err(unexpectedEndOfInput());
|
|
39
|
+
}
|
|
40
|
+
if (inner.token.type !== "Range") {
|
|
41
|
+
return err(unexpectedToken(inner.token, inner.span));
|
|
42
|
+
}
|
|
43
|
+
if (!inner.token.value.ok) return err(inner.token.value.error);
|
|
44
|
+
const interval = inner.token.value.value.interval();
|
|
45
|
+
|
|
46
|
+
const close = lexer.next();
|
|
47
|
+
if (close === undefined) {
|
|
48
|
+
return err(expectedCloseParen(lexer.span()));
|
|
49
|
+
}
|
|
50
|
+
if (close.token.type !== "ParenClose") {
|
|
51
|
+
return err(unexpectedToken(close.token, close.span));
|
|
52
|
+
}
|
|
53
|
+
return ok(patternStructure(structureNode(NodePattern.fromInterval(interval))));
|
|
54
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
3
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
4
|
+
*
|
|
5
|
+
* Object parser — port of `bc-envelope-pattern-rust`
|
|
6
|
+
* `parse/structure/object_parser.rs`.
|
|
7
|
+
*
|
|
8
|
+
* @module envelope-pattern/parse/structure/object-parser
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { type Result, err, expectedCloseParen, ok, unexpectedToken } from "../../error";
|
|
12
|
+
import { type Pattern, anyObject, object } from "../../pattern";
|
|
13
|
+
import type { Lexer } from "../token";
|
|
14
|
+
import { parseOr } from "../meta/or-parser";
|
|
15
|
+
|
|
16
|
+
export function parseObject(lexer: Lexer): Result<Pattern> {
|
|
17
|
+
const next = lexer.peekToken();
|
|
18
|
+
if (next?.token.type !== "ParenOpen") {
|
|
19
|
+
return ok(anyObject());
|
|
20
|
+
}
|
|
21
|
+
lexer.next(); // consume (
|
|
22
|
+
const inner = parseOr(lexer);
|
|
23
|
+
if (!inner.ok) return inner;
|
|
24
|
+
const close = lexer.next();
|
|
25
|
+
if (close === undefined) {
|
|
26
|
+
return err(expectedCloseParen(lexer.span()));
|
|
27
|
+
}
|
|
28
|
+
if (close.token.type !== "ParenClose") {
|
|
29
|
+
return err(unexpectedToken(close.token, close.span));
|
|
30
|
+
}
|
|
31
|
+
return ok(object(inner.value));
|
|
32
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
3
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
4
|
+
*
|
|
5
|
+
* Obscured parser — port of `bc-envelope-pattern-rust`
|
|
6
|
+
* `parse/structure/obscured_parser.rs`.
|
|
7
|
+
*
|
|
8
|
+
* @module envelope-pattern/parse/structure/obscured-parser
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { type Result, ok } from "../../error";
|
|
12
|
+
import { type Pattern, obscured } from "../../pattern";
|
|
13
|
+
import type { Lexer } from "../token";
|
|
14
|
+
|
|
15
|
+
export function parseObscured(_lexer: Lexer): Result<Pattern> {
|
|
16
|
+
return ok(obscured());
|
|
17
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
3
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
4
|
+
*
|
|
5
|
+
* Predicate parser — port of `bc-envelope-pattern-rust`
|
|
6
|
+
* `parse/structure/predicate_parser.rs`.
|
|
7
|
+
*
|
|
8
|
+
* @module envelope-pattern/parse/structure/predicate-parser
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { type Result, err, expectedCloseParen, ok, unexpectedToken } from "../../error";
|
|
12
|
+
import { type Pattern, anyPredicate, predicate } from "../../pattern";
|
|
13
|
+
import type { Lexer } from "../token";
|
|
14
|
+
import { parseOr } from "../meta/or-parser";
|
|
15
|
+
|
|
16
|
+
export function parsePredicate(lexer: Lexer): Result<Pattern> {
|
|
17
|
+
const next = lexer.peekToken();
|
|
18
|
+
if (next?.token.type !== "ParenOpen") {
|
|
19
|
+
return ok(anyPredicate());
|
|
20
|
+
}
|
|
21
|
+
lexer.next(); // consume (
|
|
22
|
+
const inner = parseOr(lexer);
|
|
23
|
+
if (!inner.ok) return inner;
|
|
24
|
+
const close = lexer.next();
|
|
25
|
+
if (close === undefined) {
|
|
26
|
+
return err(expectedCloseParen(lexer.span()));
|
|
27
|
+
}
|
|
28
|
+
if (close.token.type !== "ParenClose") {
|
|
29
|
+
return err(unexpectedToken(close.token, close.span));
|
|
30
|
+
}
|
|
31
|
+
return ok(predicate(inner.value));
|
|
32
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
3
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
4
|
+
*
|
|
5
|
+
* Subject parser — port of `bc-envelope-pattern-rust`
|
|
6
|
+
* `parse/structure/subject_parser.rs`.
|
|
7
|
+
*
|
|
8
|
+
* @module envelope-pattern/parse/structure/subject-parser
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { type Result, err, expectedCloseParen, ok, unexpectedToken } from "../../error";
|
|
12
|
+
import { type Pattern, anySubject, subject } from "../../pattern";
|
|
13
|
+
import type { Lexer } from "../token";
|
|
14
|
+
import { parseOr } from "../meta/or-parser";
|
|
15
|
+
|
|
16
|
+
export function parseSubject(lexer: Lexer): Result<Pattern> {
|
|
17
|
+
const next = lexer.peekToken();
|
|
18
|
+
if (next?.token.type !== "ParenOpen") {
|
|
19
|
+
return ok(anySubject());
|
|
20
|
+
}
|
|
21
|
+
lexer.next(); // consume (
|
|
22
|
+
const inner = parseOr(lexer);
|
|
23
|
+
if (!inner.ok) return inner;
|
|
24
|
+
const close = lexer.next();
|
|
25
|
+
if (close === undefined) {
|
|
26
|
+
return err(expectedCloseParen(lexer.span()));
|
|
27
|
+
}
|
|
28
|
+
if (close.token.type !== "ParenClose") {
|
|
29
|
+
return err(unexpectedToken(close.token, close.span));
|
|
30
|
+
}
|
|
31
|
+
return ok(subject(inner.value));
|
|
32
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
3
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
4
|
+
*
|
|
5
|
+
* Wrapped/unwrap parser — port of `bc-envelope-pattern-rust`
|
|
6
|
+
* `parse/structure/wrapped_parser.rs`.
|
|
7
|
+
*
|
|
8
|
+
* @module envelope-pattern/parse/structure/wrapped-parser
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { type Result, err, expectedCloseParen, ok, unexpectedToken } from "../../error";
|
|
12
|
+
import { type Pattern, unwrapEnvelope, unwrapMatching, wrapped } from "../../pattern";
|
|
13
|
+
import type { Lexer } from "../token";
|
|
14
|
+
import { parseOr } from "../meta/or-parser";
|
|
15
|
+
|
|
16
|
+
export function parseWrapped(_lexer: Lexer): Result<Pattern> {
|
|
17
|
+
return ok(wrapped());
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function parseUnwrap(lexer: Lexer): Result<Pattern> {
|
|
21
|
+
const next = lexer.peekToken();
|
|
22
|
+
if (next?.token.type !== "ParenOpen") {
|
|
23
|
+
return ok(unwrapEnvelope());
|
|
24
|
+
}
|
|
25
|
+
lexer.next(); // consume (
|
|
26
|
+
const inner = parseOr(lexer);
|
|
27
|
+
if (!inner.ok) return inner;
|
|
28
|
+
const close = lexer.next();
|
|
29
|
+
if (close === undefined) {
|
|
30
|
+
return err(expectedCloseParen(lexer.span()));
|
|
31
|
+
}
|
|
32
|
+
if (close.token.type !== "ParenClose") {
|
|
33
|
+
return err(unexpectedToken(close.token, close.span));
|
|
34
|
+
}
|
|
35
|
+
return ok(unwrapMatching(inner.value));
|
|
36
|
+
}
|
|
@@ -135,17 +135,49 @@ function convertStructurePatternToEnvelopePattern(
|
|
|
135
135
|
): Result<Pattern> {
|
|
136
136
|
switch (structurePattern.type) {
|
|
137
137
|
case "Array": {
|
|
138
|
-
//
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
138
|
+
// Mirror Rust `convert_structure_pattern_to_envelope_pattern`'s
|
|
139
|
+
// `Array` arm: dispatch on the dcbor `ArrayPattern` variant so
|
|
140
|
+
// that `array` (Any) round-trips to `array`, `[{n,m}]` (Length)
|
|
141
|
+
// round-trips to `[{n,m}]`, and only Elements variants stay
|
|
142
|
+
// wrapped. Using `fromDcborPattern` for the Any variant produces
|
|
143
|
+
// an `Elements`-wrapped pattern that prints as `[array]`,
|
|
144
|
+
// breaking parser parity.
|
|
145
|
+
const inner = structurePattern.pattern;
|
|
146
|
+
let arrayPattern: ArrayPattern;
|
|
147
|
+
switch (inner.variant) {
|
|
148
|
+
case "Any":
|
|
149
|
+
arrayPattern = ArrayPattern.any();
|
|
150
|
+
break;
|
|
151
|
+
case "Length":
|
|
152
|
+
arrayPattern = ArrayPattern.fromInterval(inner.length);
|
|
153
|
+
break;
|
|
154
|
+
case "Elements":
|
|
155
|
+
arrayPattern = ArrayPattern.fromDcborArrayPattern(inner);
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
143
158
|
return ok({ type: "Leaf", pattern: leafArray(arrayPattern) });
|
|
144
159
|
}
|
|
145
160
|
case "Map": {
|
|
146
|
-
//
|
|
147
|
-
//
|
|
148
|
-
|
|
161
|
+
// Mirror Rust's variant dispatch — preserve `{{n,m}}` length
|
|
162
|
+
// info instead of collapsing every map pattern to `Any` (which
|
|
163
|
+
// breaks parser parity for `{{3}}`, `{{2,4}}`, `{{2,}}`).
|
|
164
|
+
const inner = structurePattern.pattern;
|
|
165
|
+
let mapPattern: MapPattern;
|
|
166
|
+
switch (inner.variant) {
|
|
167
|
+
case "Any":
|
|
168
|
+
mapPattern = MapPattern.any();
|
|
169
|
+
break;
|
|
170
|
+
case "Length":
|
|
171
|
+
mapPattern = MapPattern.fromInterval(inner.length);
|
|
172
|
+
break;
|
|
173
|
+
case "Constraints":
|
|
174
|
+
// The envelope-pattern `MapPattern` doesn't yet model dcbor
|
|
175
|
+
// map constraints. Falling back to `Any` is conservative —
|
|
176
|
+
// round-trip parity here is broken until envelope-pattern
|
|
177
|
+
// grows a constraints variant.
|
|
178
|
+
mapPattern = MapPattern.any();
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
149
181
|
return ok({ type: "Leaf", pattern: leafMap(mapPattern) });
|
|
150
182
|
}
|
|
151
183
|
case "Tagged": {
|
package/src/pattern/index.ts
CHANGED
|
@@ -110,6 +110,9 @@ import {
|
|
|
110
110
|
registerObscuredPatternFactory,
|
|
111
111
|
registerWrappedPatternFactory,
|
|
112
112
|
registerWrappedPatternDispatch,
|
|
113
|
+
registerWrappedPatternAny,
|
|
114
|
+
registerAssertionsPatternToStringDispatch,
|
|
115
|
+
registerSubjectPatternDispatch,
|
|
113
116
|
} from "./structure";
|
|
114
117
|
|
|
115
118
|
// Import meta patterns
|
|
@@ -801,6 +804,12 @@ function registerAllFactories(): void {
|
|
|
801
804
|
compile: patternCompile,
|
|
802
805
|
toString: patternToString,
|
|
803
806
|
});
|
|
807
|
+
registerWrappedPatternAny(any);
|
|
808
|
+
registerAssertionsPatternToStringDispatch(patternToString);
|
|
809
|
+
registerSubjectPatternDispatch({
|
|
810
|
+
compile: patternCompile,
|
|
811
|
+
toString: patternToString,
|
|
812
|
+
});
|
|
804
813
|
|
|
805
814
|
// Meta pattern factories
|
|
806
815
|
registerAnyPatternFactory((p) => patternMeta(metaAny(p)));
|
|
@@ -820,6 +829,26 @@ registerAllFactories();
|
|
|
820
829
|
import { registerVMPatternFunctions } from "./vm";
|
|
821
830
|
registerVMPatternFunctions(patternPathsWithCaptures, patternMatches, patternPaths);
|
|
822
831
|
|
|
832
|
+
// Register pattern-construction factories used by `parse/utils.ts` while it
|
|
833
|
+
// runs `parse_cbor_inner` / `parse_array_inner`. These mirror Rust's direct
|
|
834
|
+
// calls to `Pattern::cbor_pattern`, `Pattern::any_array`, etc. — exposed
|
|
835
|
+
// here as factory hooks because the parser sits one module layer below the
|
|
836
|
+
// `Pattern` factories and would otherwise create a circular import.
|
|
837
|
+
import { registerPatternFactories as registerParseUtilsFactories } from "../parse/utils";
|
|
838
|
+
import { type Pattern as DCBORPatternRegister } from "@bcts/dcbor-pattern";
|
|
839
|
+
import type { Cbor } from "@bcts/dcbor";
|
|
840
|
+
|
|
841
|
+
registerParseUtilsFactories({
|
|
842
|
+
cborPattern: (value: Cbor) => cborValue(value),
|
|
843
|
+
cborPatternFromDcbor: (pattern: DCBORPatternRegister) => cborPattern(pattern),
|
|
844
|
+
anyArray,
|
|
845
|
+
arrayWithCount: (count: number) => patternLeaf(leafArray(ArrayPattern.count(count))),
|
|
846
|
+
arrayWithRange: (min: number, max?: number) =>
|
|
847
|
+
patternLeaf(leafArray(ArrayPattern.interval(min, max))),
|
|
848
|
+
arrayFromDcborPattern: (pattern: DCBORPatternRegister) =>
|
|
849
|
+
patternLeaf(leafArray(ArrayPattern.fromDcborPattern(pattern))),
|
|
850
|
+
});
|
|
851
|
+
|
|
823
852
|
// Register pattern dispatch functions for meta patterns
|
|
824
853
|
import { registerPatternMatchFn, registerPatternDispatchFns } from "./matcher";
|
|
825
854
|
registerPatternMatchFn(patternMatches);
|
|
@@ -5,17 +5,29 @@
|
|
|
5
5
|
*
|
|
6
6
|
* @bcts/envelope-pattern - Array pattern matching
|
|
7
7
|
*
|
|
8
|
-
* This is a 1:1 TypeScript port of bc-envelope-pattern-rust array_pattern.rs
|
|
8
|
+
* This is a 1:1 TypeScript port of bc-envelope-pattern-rust array_pattern.rs.
|
|
9
|
+
*
|
|
10
|
+
* Like the Rust port, this is a thin wrapper around `dcbor_pattern::ArrayPattern`
|
|
11
|
+
* that delegates all matching to dcbor-pattern. The envelope-pattern's
|
|
12
|
+
* `ArrayPattern` adds nothing beyond extracting the leaf CBOR from the
|
|
13
|
+
* envelope subject before delegating.
|
|
9
14
|
*
|
|
10
15
|
* @module envelope-pattern/pattern/leaf/array-pattern
|
|
11
16
|
*/
|
|
12
17
|
|
|
13
|
-
import { Envelope } from "@bcts/envelope";
|
|
14
|
-
import { asCborArray, type Cbor } from "@bcts/dcbor";
|
|
18
|
+
import type { Envelope } from "@bcts/envelope";
|
|
15
19
|
import {
|
|
20
|
+
type ArrayPattern as DCBORArrayPattern,
|
|
16
21
|
type Pattern as DCBORPattern,
|
|
17
|
-
Interval,
|
|
18
|
-
|
|
22
|
+
type Interval,
|
|
23
|
+
arrayPatternAny,
|
|
24
|
+
arrayPatternMatches,
|
|
25
|
+
arrayPatternWithElements,
|
|
26
|
+
arrayPatternWithLength,
|
|
27
|
+
arrayPatternWithLengthInterval,
|
|
28
|
+
arrayPatternWithLengthRange,
|
|
29
|
+
arrayPatternDisplay,
|
|
30
|
+
arrayPatternEquals,
|
|
19
31
|
patternDisplay as dcborPatternDisplay,
|
|
20
32
|
} from "@bcts/dcbor-pattern";
|
|
21
33
|
import type { Path } from "../../format";
|
|
@@ -32,25 +44,16 @@ export function registerArrayPatternFactory(factory: (pattern: ArrayPattern) =>
|
|
|
32
44
|
}
|
|
33
45
|
|
|
34
46
|
/**
|
|
35
|
-
* Pattern for matching
|
|
47
|
+
* Pattern for matching arrays.
|
|
36
48
|
*
|
|
37
|
-
*
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
| { readonly type: "Any" }
|
|
41
|
-
| { readonly type: "Interval"; readonly interval: Interval }
|
|
42
|
-
| { readonly type: "DCBORPattern"; readonly pattern: DCBORPattern }
|
|
43
|
-
| { readonly type: "WithPatterns"; readonly patterns: Pattern[] };
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Pattern for matching array values in envelope leaf nodes.
|
|
47
|
-
*
|
|
48
|
-
* Corresponds to the Rust `ArrayPattern` struct in array_pattern.rs
|
|
49
|
+
* Mirrors Rust `ArrayPattern(dcbor_pattern::ArrayPattern)` from
|
|
50
|
+
* `bc-envelope-pattern-rust/src/pattern/leaf/array_pattern.rs`. All
|
|
51
|
+
* matching, display, and equality is delegated to dcbor-pattern.
|
|
49
52
|
*/
|
|
50
53
|
export class ArrayPattern implements Matcher {
|
|
51
|
-
private readonly _pattern:
|
|
54
|
+
private readonly _pattern: DCBORArrayPattern;
|
|
52
55
|
|
|
53
|
-
private constructor(pattern:
|
|
56
|
+
private constructor(pattern: DCBORArrayPattern) {
|
|
54
57
|
this._pattern = pattern;
|
|
55
58
|
}
|
|
56
59
|
|
|
@@ -58,124 +61,65 @@ export class ArrayPattern implements Matcher {
|
|
|
58
61
|
* Creates a new ArrayPattern that matches any array.
|
|
59
62
|
*/
|
|
60
63
|
static any(): ArrayPattern {
|
|
61
|
-
return new ArrayPattern(
|
|
64
|
+
return new ArrayPattern(arrayPatternAny());
|
|
62
65
|
}
|
|
63
66
|
|
|
64
67
|
/**
|
|
65
68
|
* Creates a new ArrayPattern that matches arrays with a specific length.
|
|
66
69
|
*/
|
|
67
70
|
static count(count: number): ArrayPattern {
|
|
68
|
-
return new ArrayPattern(
|
|
69
|
-
type: "Interval",
|
|
70
|
-
interval: Interval.exactly(count),
|
|
71
|
-
});
|
|
71
|
+
return new ArrayPattern(arrayPatternWithLength(count));
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
/**
|
|
75
75
|
* Creates a new ArrayPattern that matches arrays within a length range.
|
|
76
76
|
*/
|
|
77
77
|
static interval(min: number, max?: number): ArrayPattern {
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
return new ArrayPattern(arrayPatternWithLengthRange(min, max));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Creates a new ArrayPattern from a length Interval.
|
|
83
|
+
*/
|
|
84
|
+
static fromInterval(interval: Interval): ArrayPattern {
|
|
85
|
+
return new ArrayPattern(arrayPatternWithLengthInterval(interval));
|
|
80
86
|
}
|
|
81
87
|
|
|
82
88
|
/**
|
|
83
|
-
* Creates a new ArrayPattern from a dcbor-pattern.
|
|
89
|
+
* Creates a new ArrayPattern from a top-level dcbor-pattern.
|
|
90
|
+
*
|
|
91
|
+
* Mirrors Rust `ArrayPattern::from_dcbor_pattern`, which constructs an
|
|
92
|
+
* `ArrayPattern::Elements`-style dcbor array pattern.
|
|
84
93
|
*/
|
|
85
|
-
static fromDcborPattern(
|
|
86
|
-
return new ArrayPattern(
|
|
94
|
+
static fromDcborPattern(pattern: DCBORPattern): ArrayPattern {
|
|
95
|
+
return new ArrayPattern(arrayPatternWithElements(pattern));
|
|
87
96
|
}
|
|
88
97
|
|
|
89
98
|
/**
|
|
90
|
-
* Creates a new ArrayPattern
|
|
99
|
+
* Creates a new ArrayPattern from an existing dcbor-pattern ArrayPattern.
|
|
100
|
+
*
|
|
101
|
+
* Mirrors Rust `ArrayPattern::from_dcbor_array_pattern`.
|
|
91
102
|
*/
|
|
92
|
-
static
|
|
93
|
-
return new ArrayPattern(
|
|
103
|
+
static fromDcborArrayPattern(arrayPattern: DCBORArrayPattern): ArrayPattern {
|
|
104
|
+
return new ArrayPattern(arrayPattern);
|
|
94
105
|
}
|
|
95
106
|
|
|
96
107
|
/**
|
|
97
|
-
*
|
|
108
|
+
* Returns the underlying dcbor-pattern ArrayPattern.
|
|
98
109
|
*/
|
|
99
|
-
|
|
110
|
+
inner(): DCBORArrayPattern {
|
|
100
111
|
return this._pattern;
|
|
101
112
|
}
|
|
102
113
|
|
|
103
114
|
pathsWithCaptures(haystack: Envelope): [Path[], Map<string, Path[]>] {
|
|
104
|
-
// Try to extract CBOR from the envelope
|
|
105
115
|
const cbor = haystack.subject().asLeaf();
|
|
106
116
|
if (cbor === undefined) {
|
|
107
117
|
return [[], new Map<string, Path[]>()];
|
|
108
118
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
const array = asCborArray(cbor);
|
|
112
|
-
if (array === undefined) {
|
|
113
|
-
return [[], new Map<string, Path[]>()];
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
switch (this._pattern.type) {
|
|
117
|
-
case "Any":
|
|
118
|
-
return [[[haystack]], new Map<string, Path[]>()];
|
|
119
|
-
|
|
120
|
-
case "Interval": {
|
|
121
|
-
const length = array.length;
|
|
122
|
-
if (this._pattern.interval.contains(length)) {
|
|
123
|
-
return [[[haystack]], new Map<string, Path[]>()];
|
|
124
|
-
}
|
|
125
|
-
return [[], new Map<string, Path[]>()];
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
case "DCBORPattern": {
|
|
129
|
-
// Delegate to dcbor-pattern for matching
|
|
130
|
-
const { paths: dcborPaths, captures: dcborCaptures } = dcborPatternPathsWithCaptures(
|
|
131
|
-
this._pattern.pattern,
|
|
132
|
-
cbor,
|
|
133
|
-
);
|
|
134
|
-
|
|
135
|
-
if (dcborPaths.length > 0) {
|
|
136
|
-
// Convert dcbor paths to envelope paths
|
|
137
|
-
const envelopePaths: Path[] = dcborPaths.map((dcborPath: Cbor[]) => {
|
|
138
|
-
const envPath: Path = [haystack];
|
|
139
|
-
// Skip the first element (root) and convert rest to envelopes
|
|
140
|
-
for (let i = 1; i < dcborPath.length; i++) {
|
|
141
|
-
const elem = dcborPath[i];
|
|
142
|
-
if (elem !== undefined) {
|
|
143
|
-
envPath.push(Envelope.newLeaf(elem));
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
return envPath;
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
// Convert dcbor captures to envelope captures
|
|
150
|
-
const envelopeCaptures = new Map<string, Path[]>();
|
|
151
|
-
for (const [name, capturePaths] of dcborCaptures) {
|
|
152
|
-
const envCapturePaths: Path[] = capturePaths.map((dcborPath: Cbor[]) => {
|
|
153
|
-
const envPath: Path = [haystack];
|
|
154
|
-
for (let i = 1; i < dcborPath.length; i++) {
|
|
155
|
-
const elem = dcborPath[i];
|
|
156
|
-
if (elem !== undefined) {
|
|
157
|
-
envPath.push(Envelope.newLeaf(elem));
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
return envPath;
|
|
161
|
-
});
|
|
162
|
-
envelopeCaptures.set(name, envCapturePaths);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
return [envelopePaths, envelopeCaptures];
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
return [[], new Map<string, Path[]>()];
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
case "WithPatterns":
|
|
172
|
-
// For envelope patterns, match if array length equals patterns count
|
|
173
|
-
// Full element-by-element matching would require additional implementation
|
|
174
|
-
if (array.length === this._pattern.patterns.length) {
|
|
175
|
-
return [[[haystack]], new Map<string, Path[]>()];
|
|
176
|
-
}
|
|
177
|
-
return [[], new Map<string, Path[]>()];
|
|
119
|
+
if (arrayPatternMatches(this._pattern, cbor)) {
|
|
120
|
+
return [[[haystack]], new Map<string, Path[]>()];
|
|
178
121
|
}
|
|
122
|
+
return [[], new Map<string, Path[]>()];
|
|
179
123
|
}
|
|
180
124
|
|
|
181
125
|
paths(haystack: Envelope): Path[] {
|
|
@@ -198,80 +142,34 @@ export class ArrayPattern implements Matcher {
|
|
|
198
142
|
}
|
|
199
143
|
|
|
200
144
|
toString(): string {
|
|
201
|
-
|
|
202
|
-
case "Any":
|
|
203
|
-
return "[*]";
|
|
204
|
-
case "Interval":
|
|
205
|
-
return `[{${this._pattern.interval.toString()}}]`;
|
|
206
|
-
case "DCBORPattern":
|
|
207
|
-
return dcborPatternDisplay(this._pattern.pattern);
|
|
208
|
-
case "WithPatterns":
|
|
209
|
-
return `[${this._pattern.patterns.map(String).join(", ")}]`;
|
|
210
|
-
}
|
|
145
|
+
return arrayPatternDisplay(this._pattern, dcborPatternDisplay);
|
|
211
146
|
}
|
|
212
147
|
|
|
213
148
|
/**
|
|
214
|
-
* Equality comparison.
|
|
149
|
+
* Equality comparison. Delegates to dcbor-pattern's structural equality
|
|
150
|
+
* with a display-string fallback for pattern-equality (mirrors Rust's
|
|
151
|
+
* `Hash` impl that hashes the display, since dcbor `ArrayPattern`
|
|
152
|
+
* itself does not derive `Hash`).
|
|
215
153
|
*/
|
|
216
154
|
equals(other: ArrayPattern): boolean {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
return true;
|
|
223
|
-
case "Interval":
|
|
224
|
-
return this._pattern.interval.equals(
|
|
225
|
-
(other._pattern as { type: "Interval"; interval: Interval }).interval,
|
|
226
|
-
);
|
|
227
|
-
case "DCBORPattern":
|
|
228
|
-
// Compare using display representation
|
|
229
|
-
return (
|
|
230
|
-
dcborPatternDisplay(this._pattern.pattern) ===
|
|
231
|
-
dcborPatternDisplay(
|
|
232
|
-
(other._pattern as { type: "DCBORPattern"; pattern: DCBORPattern }).pattern,
|
|
233
|
-
)
|
|
234
|
-
);
|
|
235
|
-
case "WithPatterns": {
|
|
236
|
-
const otherPatterns = (other._pattern as { type: "WithPatterns"; patterns: Pattern[] })
|
|
237
|
-
.patterns;
|
|
238
|
-
if (this._pattern.patterns.length !== otherPatterns.length) return false;
|
|
239
|
-
for (let i = 0; i < this._pattern.patterns.length; i++) {
|
|
240
|
-
if (this._pattern.patterns[i] !== otherPatterns[i]) return false;
|
|
241
|
-
}
|
|
242
|
-
return true;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
155
|
+
return arrayPatternEquals(
|
|
156
|
+
this._pattern,
|
|
157
|
+
other._pattern,
|
|
158
|
+
(a, b) => dcborPatternDisplay(a) === dcborPatternDisplay(b),
|
|
159
|
+
);
|
|
245
160
|
}
|
|
246
161
|
|
|
247
162
|
/**
|
|
248
|
-
* Hash code for use in Maps/Sets.
|
|
163
|
+
* Hash code for use in Maps/Sets. Mirrors Rust's
|
|
164
|
+
* "hash the string representation" approach.
|
|
249
165
|
*/
|
|
250
166
|
hashCode(): number {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
return this._pattern.interval.min() * 31 + (this._pattern.interval.max() ?? 0);
|
|
257
|
-
case "DCBORPattern":
|
|
258
|
-
// Simple hash based on display string
|
|
259
|
-
return simpleStringHash(dcborPatternDisplay(this._pattern.pattern));
|
|
260
|
-
case "WithPatterns":
|
|
261
|
-
return this._pattern.patterns.length;
|
|
167
|
+
let hash = 0;
|
|
168
|
+
const str = this.toString();
|
|
169
|
+
for (let i = 0; i < str.length; i++) {
|
|
170
|
+
hash = (hash << 5) - hash + str.charCodeAt(i);
|
|
171
|
+
hash = hash & hash;
|
|
262
172
|
}
|
|
173
|
+
return hash;
|
|
263
174
|
}
|
|
264
175
|
}
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* Simple string hash function for hashCode implementations.
|
|
268
|
-
*/
|
|
269
|
-
function simpleStringHash(str: string): number {
|
|
270
|
-
let hash = 0;
|
|
271
|
-
for (let i = 0; i < str.length; i++) {
|
|
272
|
-
const char = str.charCodeAt(i);
|
|
273
|
-
hash = (hash << 5) - hash + char;
|
|
274
|
-
hash = hash & hash; // Convert to 32-bit integer
|
|
275
|
-
}
|
|
276
|
-
return hash;
|
|
277
|
-
}
|