@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,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
3
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
4
|
+
*
|
|
5
|
+
* Not parser — port of `bc-envelope-pattern-rust`
|
|
6
|
+
* `parse/meta/not_parser.rs`.
|
|
7
|
+
*
|
|
8
|
+
* Mirrors Rust:
|
|
9
|
+
* - On `!`, recurse into `parse_not` so chained negation parses as
|
|
10
|
+
* `not(not(x))` rather than `not(group(x))`.
|
|
11
|
+
* - Otherwise descend into `parse_and`.
|
|
12
|
+
*
|
|
13
|
+
* @module envelope-pattern/parse/meta/not-parser
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { type Result, ok } from "../../error";
|
|
17
|
+
import { type Pattern, notMatching } from "../../pattern";
|
|
18
|
+
import type { Lexer } from "../token";
|
|
19
|
+
import { parseAnd } from "./and-parser";
|
|
20
|
+
|
|
21
|
+
export function parseNot(lexer: Lexer): Result<Pattern> {
|
|
22
|
+
const next = lexer.peekToken();
|
|
23
|
+
if (next?.token.type === "Not") {
|
|
24
|
+
lexer.next(); // consume !
|
|
25
|
+
const inner = parseNot(lexer);
|
|
26
|
+
if (!inner.ok) return inner;
|
|
27
|
+
return ok(notMatching(inner.value));
|
|
28
|
+
}
|
|
29
|
+
return parseAnd(lexer);
|
|
30
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
3
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
4
|
+
*
|
|
5
|
+
* Or parser — port of `bc-envelope-pattern-rust` `parse/meta/or_parser.rs`.
|
|
6
|
+
*
|
|
7
|
+
* @module envelope-pattern/parse/meta/or-parser
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { type Result, ok } from "../../error";
|
|
11
|
+
import { type Pattern, or } from "../../pattern";
|
|
12
|
+
import type { Lexer } from "../token";
|
|
13
|
+
import { parseTraverse } from "./traverse-parser";
|
|
14
|
+
|
|
15
|
+
export function parseOr(lexer: Lexer): Result<Pattern> {
|
|
16
|
+
const patterns: Pattern[] = [];
|
|
17
|
+
const first = parseTraverse(lexer);
|
|
18
|
+
if (!first.ok) return first;
|
|
19
|
+
patterns.push(first.value);
|
|
20
|
+
|
|
21
|
+
while (true) {
|
|
22
|
+
const next = lexer.peekToken();
|
|
23
|
+
if (next?.token.type !== "Or") {
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
lexer.next(); // consume |
|
|
27
|
+
const nextExpr = parseTraverse(lexer);
|
|
28
|
+
if (!nextExpr.ok) return nextExpr;
|
|
29
|
+
patterns.push(nextExpr.value);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (patterns.length === 1) {
|
|
33
|
+
return ok(patterns[0]);
|
|
34
|
+
}
|
|
35
|
+
return ok(or(patterns));
|
|
36
|
+
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
3
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
4
|
+
*
|
|
5
|
+
* Primary parser — port of `bc-envelope-pattern-rust`
|
|
6
|
+
* `parse/meta/primary_parser.rs`.
|
|
7
|
+
*
|
|
8
|
+
* Dispatches on the next token to the appropriate leaf/structure/meta
|
|
9
|
+
* parser. When a `(` is encountered the open paren is consumed here and
|
|
10
|
+
* `parse_group` handles the rest (paren'd expression + optional
|
|
11
|
+
* quantifier suffix).
|
|
12
|
+
*
|
|
13
|
+
* @module envelope-pattern/parse/meta/primary-parser
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
type Result,
|
|
18
|
+
err,
|
|
19
|
+
invalidRegex,
|
|
20
|
+
ok,
|
|
21
|
+
unexpectedEndOfInput,
|
|
22
|
+
unexpectedToken,
|
|
23
|
+
unrecognizedToken,
|
|
24
|
+
} from "../../error";
|
|
25
|
+
import {
|
|
26
|
+
type Pattern,
|
|
27
|
+
ByteStringPattern,
|
|
28
|
+
KnownValuePattern,
|
|
29
|
+
NumberPattern,
|
|
30
|
+
any,
|
|
31
|
+
anyBool,
|
|
32
|
+
anyByteString,
|
|
33
|
+
anyDate,
|
|
34
|
+
anyKnownValue,
|
|
35
|
+
anyNumber,
|
|
36
|
+
anyText,
|
|
37
|
+
bool,
|
|
38
|
+
byteString,
|
|
39
|
+
leaf,
|
|
40
|
+
leafByteString,
|
|
41
|
+
leafKnownValue,
|
|
42
|
+
leafNumber,
|
|
43
|
+
number,
|
|
44
|
+
nullPattern,
|
|
45
|
+
patternLeaf,
|
|
46
|
+
text,
|
|
47
|
+
textRegex,
|
|
48
|
+
} from "../../pattern";
|
|
49
|
+
import type { Lexer } from "../token";
|
|
50
|
+
import { parseArray } from "../leaf/array-parser";
|
|
51
|
+
import { parseCbor } from "../leaf/cbor-parser";
|
|
52
|
+
import { parseDateContent } from "../leaf/date-parser";
|
|
53
|
+
import { parseKnownValueContent } from "../leaf/known-value-parser";
|
|
54
|
+
import { parseComparisonNumber, parseNumberRangeOrComparison } from "../leaf/number-parser";
|
|
55
|
+
import { parseTag } from "../leaf/tag-parser";
|
|
56
|
+
import { parseAssertion } from "../structure/assertion-parser";
|
|
57
|
+
import { parseAssertionObj } from "../structure/assertion-obj-parser";
|
|
58
|
+
import { parseAssertionPred } from "../structure/assertion-pred-parser";
|
|
59
|
+
import { parseCompressed } from "../structure/compressed-parser";
|
|
60
|
+
import { parseDigest } from "../structure/digest-parser";
|
|
61
|
+
import { parseElided } from "../structure/elided-parser";
|
|
62
|
+
import { parseEncrypted } from "../structure/encrypted-parser";
|
|
63
|
+
import { parseNode } from "../structure/node-parser";
|
|
64
|
+
import { parseObject } from "../structure/object-parser";
|
|
65
|
+
import { parseObscured } from "../structure/obscured-parser";
|
|
66
|
+
import { parsePredicate } from "../structure/predicate-parser";
|
|
67
|
+
import { parseSubject } from "../structure/subject-parser";
|
|
68
|
+
import { parseUnwrap, parseWrapped } from "../structure/wrapped-parser";
|
|
69
|
+
import { parseCapture } from "./capture-parser";
|
|
70
|
+
import { parseGroup } from "./group-parser";
|
|
71
|
+
import { parseSearch } from "./search-parser";
|
|
72
|
+
|
|
73
|
+
export function parsePrimary(lexer: Lexer): Result<Pattern> {
|
|
74
|
+
const tokenResult = lexer.next();
|
|
75
|
+
if (tokenResult === undefined) {
|
|
76
|
+
return err(unexpectedEndOfInput());
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const { token, span } = tokenResult;
|
|
80
|
+
|
|
81
|
+
switch (token.type) {
|
|
82
|
+
case "Search":
|
|
83
|
+
return parseSearch(lexer);
|
|
84
|
+
case "Node":
|
|
85
|
+
return parseNode(lexer);
|
|
86
|
+
case "Assertion":
|
|
87
|
+
return parseAssertion(lexer);
|
|
88
|
+
case "AssertionPred":
|
|
89
|
+
return parseAssertionPred(lexer);
|
|
90
|
+
case "AssertionObj":
|
|
91
|
+
return parseAssertionObj(lexer);
|
|
92
|
+
case "Digest":
|
|
93
|
+
return parseDigest(lexer);
|
|
94
|
+
case "Obj":
|
|
95
|
+
return parseObject(lexer);
|
|
96
|
+
case "Obscured":
|
|
97
|
+
return parseObscured(lexer);
|
|
98
|
+
case "Elided":
|
|
99
|
+
return parseElided(lexer);
|
|
100
|
+
case "Encrypted":
|
|
101
|
+
return parseEncrypted(lexer);
|
|
102
|
+
case "Compressed":
|
|
103
|
+
return parseCompressed(lexer);
|
|
104
|
+
case "Pred":
|
|
105
|
+
return parsePredicate(lexer);
|
|
106
|
+
case "Subject":
|
|
107
|
+
return parseSubject(lexer);
|
|
108
|
+
case "Wrapped":
|
|
109
|
+
return parseWrapped(lexer);
|
|
110
|
+
case "Unwrap":
|
|
111
|
+
return parseUnwrap(lexer);
|
|
112
|
+
case "Leaf":
|
|
113
|
+
return ok(leaf());
|
|
114
|
+
|
|
115
|
+
case "GroupName":
|
|
116
|
+
return parseCapture(lexer, token.name);
|
|
117
|
+
|
|
118
|
+
case "ParenOpen":
|
|
119
|
+
return parseGroup(lexer);
|
|
120
|
+
|
|
121
|
+
case "Cbor":
|
|
122
|
+
return parseCbor(lexer);
|
|
123
|
+
|
|
124
|
+
case "RepeatZeroOrMore":
|
|
125
|
+
return ok(any());
|
|
126
|
+
case "BoolKeyword":
|
|
127
|
+
return ok(anyBool());
|
|
128
|
+
case "BoolTrue":
|
|
129
|
+
return ok(bool(true));
|
|
130
|
+
case "BoolFalse":
|
|
131
|
+
return ok(bool(false));
|
|
132
|
+
case "NumberKeyword":
|
|
133
|
+
return ok(anyNumber());
|
|
134
|
+
case "TextKeyword":
|
|
135
|
+
return ok(anyText());
|
|
136
|
+
case "StringLiteral":
|
|
137
|
+
if (!token.value.ok) return err(token.value.error);
|
|
138
|
+
return ok(text(token.value.value));
|
|
139
|
+
case "UnsignedInteger":
|
|
140
|
+
if (!token.value.ok) return err(token.value.error);
|
|
141
|
+
return parseNumberRangeOrComparison(lexer, token.value.value);
|
|
142
|
+
case "Integer":
|
|
143
|
+
if (!token.value.ok) return err(token.value.error);
|
|
144
|
+
return parseNumberRangeOrComparison(lexer, token.value.value);
|
|
145
|
+
case "Float":
|
|
146
|
+
if (!token.value.ok) return err(token.value.error);
|
|
147
|
+
return parseNumberRangeOrComparison(lexer, token.value.value);
|
|
148
|
+
case "GreaterThanOrEqual":
|
|
149
|
+
return parseComparisonNumber(lexer, ">=");
|
|
150
|
+
case "LessThanOrEqual":
|
|
151
|
+
return parseComparisonNumber(lexer, "<=");
|
|
152
|
+
case "GreaterThan":
|
|
153
|
+
return parseComparisonNumber(lexer, ">");
|
|
154
|
+
case "LessThan":
|
|
155
|
+
return parseComparisonNumber(lexer, "<");
|
|
156
|
+
case "NaN":
|
|
157
|
+
return ok(patternLeaf(leafNumber(NumberPattern.nan())));
|
|
158
|
+
case "Infinity":
|
|
159
|
+
return ok(number(Infinity));
|
|
160
|
+
case "NegativeInfinity":
|
|
161
|
+
return ok(number(-Infinity));
|
|
162
|
+
case "Regex":
|
|
163
|
+
if (!token.value.ok) return err(token.value.error);
|
|
164
|
+
try {
|
|
165
|
+
return ok(textRegex(new RegExp(token.value.value)));
|
|
166
|
+
} catch {
|
|
167
|
+
return err(invalidRegex(span));
|
|
168
|
+
}
|
|
169
|
+
case "BracketOpen":
|
|
170
|
+
return parseArray(lexer);
|
|
171
|
+
case "ByteString":
|
|
172
|
+
return ok(anyByteString());
|
|
173
|
+
case "HexPattern":
|
|
174
|
+
if (!token.value.ok) return err(token.value.error);
|
|
175
|
+
return ok(byteString(token.value.value));
|
|
176
|
+
case "HexBinaryRegex":
|
|
177
|
+
if (!token.value.ok) return err(token.value.error);
|
|
178
|
+
try {
|
|
179
|
+
return ok(
|
|
180
|
+
patternLeaf(leafByteString(ByteStringPattern.regex(new RegExp(token.value.value)))),
|
|
181
|
+
);
|
|
182
|
+
} catch {
|
|
183
|
+
return err(invalidRegex(span));
|
|
184
|
+
}
|
|
185
|
+
case "DateKeyword":
|
|
186
|
+
return ok(anyDate());
|
|
187
|
+
case "DatePattern":
|
|
188
|
+
if (!token.value.ok) return err(token.value.error);
|
|
189
|
+
return parseDateContent(token.value.value, span);
|
|
190
|
+
case "Tagged":
|
|
191
|
+
return parseTag(lexer);
|
|
192
|
+
case "Known":
|
|
193
|
+
return ok(anyKnownValue());
|
|
194
|
+
case "SingleQuotedPattern":
|
|
195
|
+
if (!token.value.ok) return err(token.value.error);
|
|
196
|
+
return parseKnownValueContent(token.value.value);
|
|
197
|
+
case "SingleQuotedRegex":
|
|
198
|
+
if (!token.value.ok) return err(token.value.error);
|
|
199
|
+
try {
|
|
200
|
+
return ok(
|
|
201
|
+
patternLeaf(leafKnownValue(KnownValuePattern.regex(new RegExp(token.value.value)))),
|
|
202
|
+
);
|
|
203
|
+
} catch {
|
|
204
|
+
return err(invalidRegex(span));
|
|
205
|
+
}
|
|
206
|
+
case "Null":
|
|
207
|
+
return ok(nullPattern());
|
|
208
|
+
|
|
209
|
+
case "Identifier":
|
|
210
|
+
// Rust's logos lexer never produces `Identifier` for unknown
|
|
211
|
+
// words; it errors with `UnrecognizedToken`. Mirror that behaviour
|
|
212
|
+
// here so error positions line up with Rust.
|
|
213
|
+
return err(unrecognizedToken(span));
|
|
214
|
+
|
|
215
|
+
case "And":
|
|
216
|
+
case "Or":
|
|
217
|
+
case "Not":
|
|
218
|
+
case "Traverse":
|
|
219
|
+
case "RepeatZeroOrMoreLazy":
|
|
220
|
+
case "RepeatZeroOrMorePossessive":
|
|
221
|
+
case "RepeatOneOrMore":
|
|
222
|
+
case "RepeatOneOrMoreLazy":
|
|
223
|
+
case "RepeatOneOrMorePossessive":
|
|
224
|
+
case "RepeatZeroOrOne":
|
|
225
|
+
case "RepeatZeroOrOneLazy":
|
|
226
|
+
case "RepeatZeroOrOnePossessive":
|
|
227
|
+
case "ParenClose":
|
|
228
|
+
case "BracketClose":
|
|
229
|
+
case "Comma":
|
|
230
|
+
case "Ellipsis":
|
|
231
|
+
case "Range":
|
|
232
|
+
return err(unexpectedToken(token, span));
|
|
233
|
+
}
|
|
234
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
3
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
4
|
+
*
|
|
5
|
+
* Search parser — port of `bc-envelope-pattern-rust`
|
|
6
|
+
* `parse/meta/search_parser.rs`.
|
|
7
|
+
*
|
|
8
|
+
* @module envelope-pattern/parse/meta/search-parser
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
type Result,
|
|
13
|
+
err,
|
|
14
|
+
expectedCloseParen,
|
|
15
|
+
ok,
|
|
16
|
+
unexpectedEndOfInput,
|
|
17
|
+
unexpectedToken,
|
|
18
|
+
} from "../../error";
|
|
19
|
+
import { type Pattern, search } from "../../pattern";
|
|
20
|
+
import type { Lexer } from "../token";
|
|
21
|
+
import { parseOr } from "./or-parser";
|
|
22
|
+
|
|
23
|
+
export function parseSearch(lexer: Lexer): Result<Pattern> {
|
|
24
|
+
const open = lexer.next();
|
|
25
|
+
if (open === undefined) {
|
|
26
|
+
return err(unexpectedEndOfInput());
|
|
27
|
+
}
|
|
28
|
+
if (open.token.type !== "ParenOpen") {
|
|
29
|
+
return err(unexpectedToken(open.token, open.span));
|
|
30
|
+
}
|
|
31
|
+
const inner = parseOr(lexer);
|
|
32
|
+
if (!inner.ok) return inner;
|
|
33
|
+
const close = lexer.next();
|
|
34
|
+
if (close === undefined) {
|
|
35
|
+
return err(expectedCloseParen(lexer.span()));
|
|
36
|
+
}
|
|
37
|
+
if (close.token.type !== "ParenClose") {
|
|
38
|
+
return err(unexpectedToken(close.token, close.span));
|
|
39
|
+
}
|
|
40
|
+
return ok(search(inner.value));
|
|
41
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
3
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
4
|
+
*
|
|
5
|
+
* Traverse parser — port of `bc-envelope-pattern-rust`
|
|
6
|
+
* `parse/meta/traverse_parser.rs`.
|
|
7
|
+
*
|
|
8
|
+
* Note the precedence chain: `parse_or → parse_traverse → parse_not →
|
|
9
|
+
* parse_and → parse_primary`. The earlier TS port had `parse_traverse`
|
|
10
|
+
* call `parse_and` directly, which pushed `!` below `&` and
|
|
11
|
+
* miscompiled `!a & b`.
|
|
12
|
+
*
|
|
13
|
+
* @module envelope-pattern/parse/meta/traverse-parser
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { type Result, ok } from "../../error";
|
|
17
|
+
import { type Pattern, traverse } from "../../pattern";
|
|
18
|
+
import type { Lexer } from "../token";
|
|
19
|
+
import { parseNot } from "./not-parser";
|
|
20
|
+
|
|
21
|
+
export function parseTraverse(lexer: Lexer): Result<Pattern> {
|
|
22
|
+
const patterns: Pattern[] = [];
|
|
23
|
+
const first = parseNot(lexer);
|
|
24
|
+
if (!first.ok) return first;
|
|
25
|
+
patterns.push(first.value);
|
|
26
|
+
|
|
27
|
+
while (true) {
|
|
28
|
+
const next = lexer.peekToken();
|
|
29
|
+
if (next?.token.type !== "Traverse") {
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
lexer.next(); // consume ->
|
|
33
|
+
const nextExpr = parseNot(lexer);
|
|
34
|
+
if (!nextExpr.ok) return nextExpr;
|
|
35
|
+
patterns.push(nextExpr.value);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (patterns.length === 1) {
|
|
39
|
+
return ok(patterns[0]);
|
|
40
|
+
}
|
|
41
|
+
return ok(traverse(patterns));
|
|
42
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
3
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
4
|
+
*
|
|
5
|
+
* Assertion-object parser — port of
|
|
6
|
+
* `bc-envelope-pattern-rust/src/parse/structure/assertion_obj_parser.rs`.
|
|
7
|
+
*
|
|
8
|
+
* Requires `assertobj(<pattern>)`. The bare `assertobj` keyword is a
|
|
9
|
+
* syntax error in Rust; we now mirror that behaviour.
|
|
10
|
+
*
|
|
11
|
+
* @module envelope-pattern/parse/structure/assertion-obj-parser
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
type Result,
|
|
16
|
+
err,
|
|
17
|
+
expectedCloseParen,
|
|
18
|
+
ok,
|
|
19
|
+
unexpectedEndOfInput,
|
|
20
|
+
unexpectedToken,
|
|
21
|
+
} from "../../error";
|
|
22
|
+
import { type Pattern, assertionWithObject } from "../../pattern";
|
|
23
|
+
import type { Lexer } from "../token";
|
|
24
|
+
import { parseOr } from "../meta/or-parser";
|
|
25
|
+
|
|
26
|
+
export function parseAssertionObj(lexer: Lexer): Result<Pattern> {
|
|
27
|
+
const open = lexer.next();
|
|
28
|
+
if (open === undefined) {
|
|
29
|
+
return err(unexpectedEndOfInput());
|
|
30
|
+
}
|
|
31
|
+
if (open.token.type !== "ParenOpen") {
|
|
32
|
+
return err(unexpectedToken(open.token, open.span));
|
|
33
|
+
}
|
|
34
|
+
const inner = parseOr(lexer);
|
|
35
|
+
if (!inner.ok) return inner;
|
|
36
|
+
const close = lexer.next();
|
|
37
|
+
if (close === undefined) {
|
|
38
|
+
return err(expectedCloseParen(lexer.span()));
|
|
39
|
+
}
|
|
40
|
+
if (close.token.type !== "ParenClose") {
|
|
41
|
+
return err(unexpectedToken(close.token, close.span));
|
|
42
|
+
}
|
|
43
|
+
return ok(assertionWithObject(inner.value));
|
|
44
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
3
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
4
|
+
*
|
|
5
|
+
* Assertion parser — port of `bc-envelope-pattern-rust`
|
|
6
|
+
* `parse/structure/assertion_parser.rs`.
|
|
7
|
+
*
|
|
8
|
+
* Note: Rust's `parse_assertion` ignores its lexer entirely and always
|
|
9
|
+
* returns `Pattern::any_assertion()`. There is intentionally **no**
|
|
10
|
+
* `assert(pred, obj)` syntax — predicate/object filters are written via
|
|
11
|
+
* the dedicated `assertpred(...)` / `assertobj(...)` keywords.
|
|
12
|
+
*
|
|
13
|
+
* @module envelope-pattern/parse/structure/assertion-parser
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { type Result, ok } from "../../error";
|
|
17
|
+
import { type Pattern, anyAssertion } from "../../pattern";
|
|
18
|
+
import type { Lexer } from "../token";
|
|
19
|
+
|
|
20
|
+
export function parseAssertion(_lexer: Lexer): Result<Pattern> {
|
|
21
|
+
return ok(anyAssertion());
|
|
22
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
3
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
4
|
+
*
|
|
5
|
+
* Assertion-predicate parser — port of
|
|
6
|
+
* `bc-envelope-pattern-rust/src/parse/structure/assertion_pred_parser.rs`.
|
|
7
|
+
*
|
|
8
|
+
* Requires `assertpred(<pattern>)`. The bare `assertpred` keyword is a
|
|
9
|
+
* syntax error in Rust (it errors on `UnexpectedEndOfInput` /
|
|
10
|
+
* `UnexpectedToken`); we now mirror that behaviour.
|
|
11
|
+
*
|
|
12
|
+
* @module envelope-pattern/parse/structure/assertion-pred-parser
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
type Result,
|
|
17
|
+
err,
|
|
18
|
+
expectedCloseParen,
|
|
19
|
+
ok,
|
|
20
|
+
unexpectedEndOfInput,
|
|
21
|
+
unexpectedToken,
|
|
22
|
+
} from "../../error";
|
|
23
|
+
import { type Pattern, assertionWithPredicate } from "../../pattern";
|
|
24
|
+
import type { Lexer } from "../token";
|
|
25
|
+
import { parseOr } from "../meta/or-parser";
|
|
26
|
+
|
|
27
|
+
export function parseAssertionPred(lexer: Lexer): Result<Pattern> {
|
|
28
|
+
const open = lexer.next();
|
|
29
|
+
if (open === undefined) {
|
|
30
|
+
return err(unexpectedEndOfInput());
|
|
31
|
+
}
|
|
32
|
+
if (open.token.type !== "ParenOpen") {
|
|
33
|
+
return err(unexpectedToken(open.token, open.span));
|
|
34
|
+
}
|
|
35
|
+
const inner = parseOr(lexer);
|
|
36
|
+
if (!inner.ok) return inner;
|
|
37
|
+
const close = lexer.next();
|
|
38
|
+
if (close === undefined) {
|
|
39
|
+
return err(expectedCloseParen(lexer.span()));
|
|
40
|
+
}
|
|
41
|
+
if (close.token.type !== "ParenClose") {
|
|
42
|
+
return err(unexpectedToken(close.token, close.span));
|
|
43
|
+
}
|
|
44
|
+
return ok(assertionWithPredicate(inner.value));
|
|
45
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
3
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
4
|
+
*
|
|
5
|
+
* Compressed parser — port of `bc-envelope-pattern-rust`
|
|
6
|
+
* `parse/structure/compressed_parser.rs`.
|
|
7
|
+
*
|
|
8
|
+
* @module envelope-pattern/parse/structure/compressed-parser
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { type Result, ok } from "../../error";
|
|
12
|
+
import { type Pattern, compressed } from "../../pattern";
|
|
13
|
+
import type { Lexer } from "../token";
|
|
14
|
+
|
|
15
|
+
export function parseCompressed(_lexer: Lexer): Result<Pattern> {
|
|
16
|
+
return ok(compressed());
|
|
17
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
3
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
4
|
+
*
|
|
5
|
+
* Digest parser — port of `bc-envelope-pattern-rust`
|
|
6
|
+
* `parse/structure/digest_parser.rs`.
|
|
7
|
+
*
|
|
8
|
+
* Mirrors Rust exactly:
|
|
9
|
+
*
|
|
10
|
+
* - Requires `digest(...)` — bare `digest` is an `UnexpectedEndOfInput`
|
|
11
|
+
* error (the previous TS port silently returned `digest(any)`).
|
|
12
|
+
* - Inside the parens, accepts either a UR string (`ur:digest/...`,
|
|
13
|
+
* parsed via `Digest.fromURString`) or a hex byte prefix.
|
|
14
|
+
* - Hex prefixes must have even length and not exceed `Digest.DIGEST_SIZE`
|
|
15
|
+
* bytes; otherwise the parser surfaces `InvalidHexString`.
|
|
16
|
+
*
|
|
17
|
+
* @module envelope-pattern/parse/structure/digest-parser
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { Digest } from "@bcts/envelope";
|
|
21
|
+
import {
|
|
22
|
+
type Result,
|
|
23
|
+
err,
|
|
24
|
+
expectedCloseParen,
|
|
25
|
+
invalidHexString,
|
|
26
|
+
invalidUr,
|
|
27
|
+
ok,
|
|
28
|
+
unexpectedEndOfInput,
|
|
29
|
+
unexpectedToken,
|
|
30
|
+
} from "../../error";
|
|
31
|
+
import { type Pattern, digest, digestPrefix } from "../../pattern";
|
|
32
|
+
import type { Lexer } from "../token";
|
|
33
|
+
|
|
34
|
+
const DIGEST_SIZE = Digest.DIGEST_SIZE;
|
|
35
|
+
|
|
36
|
+
export function parseDigest(lexer: Lexer): Result<Pattern> {
|
|
37
|
+
const open = lexer.next();
|
|
38
|
+
if (open === undefined) {
|
|
39
|
+
return err(unexpectedEndOfInput());
|
|
40
|
+
}
|
|
41
|
+
if (open.token.type !== "ParenOpen") {
|
|
42
|
+
return err(unexpectedToken(open.token, open.span));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const remainder = lexer.remainder();
|
|
46
|
+
const innerResult = parseDigestInner(remainder, lexer.position);
|
|
47
|
+
if (!innerResult.ok) return innerResult;
|
|
48
|
+
const [pattern, consumed] = innerResult.value;
|
|
49
|
+
lexer.bump(consumed);
|
|
50
|
+
|
|
51
|
+
const close = lexer.next();
|
|
52
|
+
if (close === undefined) {
|
|
53
|
+
return err(expectedCloseParen(lexer.span()));
|
|
54
|
+
}
|
|
55
|
+
if (close.token.type !== "ParenClose") {
|
|
56
|
+
return err(unexpectedToken(close.token, close.span));
|
|
57
|
+
}
|
|
58
|
+
return ok(pattern);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function parseDigestInner(src: string, basePos: number): Result<[Pattern, number]> {
|
|
62
|
+
let pos = 0;
|
|
63
|
+
pos = skipWs(src, pos);
|
|
64
|
+
|
|
65
|
+
if (src.startsWith("ur:", pos)) {
|
|
66
|
+
const start = pos;
|
|
67
|
+
while (pos < src.length && src[pos] !== ")") {
|
|
68
|
+
pos += 1;
|
|
69
|
+
}
|
|
70
|
+
const ur = src.slice(start, pos).trimEnd();
|
|
71
|
+
let parsed: Digest;
|
|
72
|
+
try {
|
|
73
|
+
parsed = Digest.fromURString(ur);
|
|
74
|
+
} catch {
|
|
75
|
+
return err(invalidUr(ur, { start: basePos + start, end: basePos + pos }));
|
|
76
|
+
}
|
|
77
|
+
pos = skipWs(src, pos);
|
|
78
|
+
return ok([digest(parsed), pos]);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const start = pos;
|
|
82
|
+
while (pos < src.length && isAsciiHexDigit(src.charCodeAt(pos))) {
|
|
83
|
+
pos += 1;
|
|
84
|
+
}
|
|
85
|
+
if (pos === start) {
|
|
86
|
+
return err(invalidHexString({ start: basePos + pos, end: basePos + pos }));
|
|
87
|
+
}
|
|
88
|
+
const hexStr = src.slice(start, pos);
|
|
89
|
+
if (hexStr.length % 2 !== 0) {
|
|
90
|
+
return err(invalidHexString({ start: basePos + pos, end: basePos + pos }));
|
|
91
|
+
}
|
|
92
|
+
const bytes = decodeHex(hexStr);
|
|
93
|
+
if (bytes === undefined) {
|
|
94
|
+
return err(invalidHexString({ start: basePos + pos, end: basePos + pos }));
|
|
95
|
+
}
|
|
96
|
+
if (bytes.length > DIGEST_SIZE) {
|
|
97
|
+
return err(invalidHexString({ start: basePos + pos, end: basePos + pos }));
|
|
98
|
+
}
|
|
99
|
+
pos = skipWs(src, pos);
|
|
100
|
+
return ok([digestPrefix(bytes), pos]);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function skipWs(src: string, pos: number): number {
|
|
104
|
+
while (pos < src.length) {
|
|
105
|
+
const ch = src[pos];
|
|
106
|
+
if (ch === " " || ch === "\t" || ch === "\n" || ch === "\r" || ch === "\f") {
|
|
107
|
+
pos += 1;
|
|
108
|
+
} else {
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return pos;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function isAsciiHexDigit(c: number): boolean {
|
|
116
|
+
return (
|
|
117
|
+
(c >= 0x30 && c <= 0x39) /* 0-9 */ ||
|
|
118
|
+
(c >= 0x41 && c <= 0x46) /* A-F */ ||
|
|
119
|
+
(c >= 0x61 && c <= 0x66) /* a-f */
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function decodeHex(hex: string): Uint8Array | undefined {
|
|
124
|
+
if (hex.length % 2 !== 0) return undefined;
|
|
125
|
+
const out = new Uint8Array(hex.length / 2);
|
|
126
|
+
for (let i = 0; i < hex.length; i += 2) {
|
|
127
|
+
const value = Number.parseInt(hex.slice(i, i + 2), 16);
|
|
128
|
+
if (Number.isNaN(value)) return undefined;
|
|
129
|
+
out[i / 2] = value;
|
|
130
|
+
}
|
|
131
|
+
return out;
|
|
132
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
3
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
4
|
+
*
|
|
5
|
+
* Elided parser — port of `bc-envelope-pattern-rust`
|
|
6
|
+
* `parse/structure/elided_parser.rs`.
|
|
7
|
+
*
|
|
8
|
+
* @module envelope-pattern/parse/structure/elided-parser
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { type Result, ok } from "../../error";
|
|
12
|
+
import { type Pattern, elided } from "../../pattern";
|
|
13
|
+
import type { Lexer } from "../token";
|
|
14
|
+
|
|
15
|
+
export function parseElided(_lexer: Lexer): Result<Pattern> {
|
|
16
|
+
return ok(elided());
|
|
17
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
3
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
4
|
+
*
|
|
5
|
+
* Encrypted parser — port of `bc-envelope-pattern-rust`
|
|
6
|
+
* `parse/structure/encrypted_parser.rs`.
|
|
7
|
+
*
|
|
8
|
+
* @module envelope-pattern/parse/structure/encrypted-parser
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { type Result, ok } from "../../error";
|
|
12
|
+
import { type Pattern, encrypted } from "../../pattern";
|
|
13
|
+
import type { Lexer } from "../token";
|
|
14
|
+
|
|
15
|
+
export function parseEncrypted(_lexer: Lexer): Result<Pattern> {
|
|
16
|
+
return ok(encrypted());
|
|
17
|
+
}
|