@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.
Files changed (53) hide show
  1. package/LICENSE +48 -0
  2. package/README.md +13 -0
  3. package/dist/index.cjs +6781 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.cts +2628 -0
  6. package/dist/index.d.cts.map +1 -0
  7. package/dist/index.d.mts +2628 -0
  8. package/dist/index.d.mts.map +1 -0
  9. package/dist/index.iife.js +6781 -0
  10. package/dist/index.iife.js.map +1 -0
  11. package/dist/index.mjs +6545 -0
  12. package/dist/index.mjs.map +1 -0
  13. package/package.json +77 -0
  14. package/src/error.ts +262 -0
  15. package/src/format.ts +375 -0
  16. package/src/index.ts +27 -0
  17. package/src/parse/index.ts +923 -0
  18. package/src/parse/token.ts +906 -0
  19. package/src/parse/utils.ts +339 -0
  20. package/src/pattern/index.ts +719 -0
  21. package/src/pattern/leaf/array-pattern.ts +273 -0
  22. package/src/pattern/leaf/bool-pattern.ts +140 -0
  23. package/src/pattern/leaf/byte-string-pattern.ts +172 -0
  24. package/src/pattern/leaf/cbor-pattern.ts +355 -0
  25. package/src/pattern/leaf/date-pattern.ts +178 -0
  26. package/src/pattern/leaf/index.ts +280 -0
  27. package/src/pattern/leaf/known-value-pattern.ts +192 -0
  28. package/src/pattern/leaf/map-pattern.ts +152 -0
  29. package/src/pattern/leaf/null-pattern.ts +110 -0
  30. package/src/pattern/leaf/number-pattern.ts +248 -0
  31. package/src/pattern/leaf/tagged-pattern.ts +228 -0
  32. package/src/pattern/leaf/text-pattern.ts +165 -0
  33. package/src/pattern/matcher.ts +88 -0
  34. package/src/pattern/meta/and-pattern.ts +109 -0
  35. package/src/pattern/meta/any-pattern.ts +81 -0
  36. package/src/pattern/meta/capture-pattern.ts +111 -0
  37. package/src/pattern/meta/group-pattern.ts +110 -0
  38. package/src/pattern/meta/index.ts +269 -0
  39. package/src/pattern/meta/not-pattern.ts +91 -0
  40. package/src/pattern/meta/or-pattern.ts +146 -0
  41. package/src/pattern/meta/search-pattern.ts +201 -0
  42. package/src/pattern/meta/traverse-pattern.ts +146 -0
  43. package/src/pattern/structure/assertions-pattern.ts +244 -0
  44. package/src/pattern/structure/digest-pattern.ts +225 -0
  45. package/src/pattern/structure/index.ts +272 -0
  46. package/src/pattern/structure/leaf-structure-pattern.ts +85 -0
  47. package/src/pattern/structure/node-pattern.ts +188 -0
  48. package/src/pattern/structure/object-pattern.ts +149 -0
  49. package/src/pattern/structure/obscured-pattern.ts +159 -0
  50. package/src/pattern/structure/predicate-pattern.ts +151 -0
  51. package/src/pattern/structure/subject-pattern.ts +152 -0
  52. package/src/pattern/structure/wrapped-pattern.ts +195 -0
  53. package/src/pattern/vm.ts +1021 -0
@@ -0,0 +1,339 @@
1
+ /**
2
+ * @bcts/envelope-pattern - Parser utility functions
3
+ *
4
+ * This is a 1:1 TypeScript port of bc-envelope-pattern-rust utils.rs
5
+ *
6
+ * @module envelope-pattern/parse/utils
7
+ */
8
+
9
+ import type { Cbor } from "@bcts/dcbor";
10
+ import { type Pattern as DCBORPattern, parse as parseDcborPattern } from "@bcts/dcbor-pattern";
11
+
12
+ // Stub for cborFromDiagnostic - not implemented in dcbor yet
13
+ function cborFromDiagnostic(_src: string): { ok: true; value: Cbor } | { ok: false } {
14
+ // TODO: Implement when dcbor adds diagnostic notation parsing
15
+ return { ok: false };
16
+ }
17
+ import {
18
+ type Result,
19
+ ok,
20
+ err,
21
+ unterminatedRegex,
22
+ invalidRegex,
23
+ invalidRange,
24
+ invalidNumberFormat,
25
+ unexpectedEndOfInput,
26
+ invalidPattern,
27
+ unknown,
28
+ } from "../error";
29
+ import type { Pattern } from "../pattern";
30
+
31
+ // Forward declaration - will be imported from pattern module
32
+ // Using a registry pattern to avoid circular dependencies
33
+ let createCborPattern: ((cbor: Cbor) => Pattern) | undefined;
34
+ let createCborPatternFromDcbor: ((pattern: DCBORPattern) => Pattern) | undefined;
35
+ let createAnyArray: (() => Pattern) | undefined;
36
+ let createArrayWithCount: ((count: number) => Pattern) | undefined;
37
+ let createArrayWithRange: ((min: number, max?: number) => Pattern) | undefined;
38
+ let createArrayFromDcborPattern: ((pattern: DCBORPattern) => Pattern) | undefined;
39
+
40
+ /**
41
+ * Register pattern factory functions.
42
+ * This is called by the pattern module to avoid circular dependencies.
43
+ */
44
+ export function registerPatternFactories(factories: {
45
+ cborPattern: (cbor: Cbor) => Pattern;
46
+ cborPatternFromDcbor: (pattern: DCBORPattern) => Pattern;
47
+ anyArray: () => Pattern;
48
+ arrayWithCount: (count: number) => Pattern;
49
+ arrayWithRange: (min: number, max?: number) => Pattern;
50
+ arrayFromDcborPattern: (pattern: DCBORPattern) => Pattern;
51
+ }): void {
52
+ createCborPattern = factories.cborPattern;
53
+ createCborPatternFromDcbor = factories.cborPatternFromDcbor;
54
+ createAnyArray = factories.anyArray;
55
+ createArrayWithCount = factories.arrayWithCount;
56
+ createArrayWithRange = factories.arrayWithRange;
57
+ createArrayFromDcborPattern = factories.arrayFromDcborPattern;
58
+ }
59
+
60
+ /**
61
+ * Skips whitespace in the source string.
62
+ *
63
+ * @param src - The source string
64
+ * @param pos - The current position (modified in place)
65
+ */
66
+ export function skipWs(src: string, pos: { value: number }): void {
67
+ while (pos.value < src.length) {
68
+ const ch = src[pos.value];
69
+ if (ch === " " || ch === "\t" || ch === "\n" || ch === "\r" || ch === "\f") {
70
+ pos.value++;
71
+ } else {
72
+ break;
73
+ }
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Parses a text regex from the source.
79
+ *
80
+ * @param src - The source string (starting after initial whitespace)
81
+ * @returns The parsed regex and consumed character count, or an error
82
+ */
83
+ export function parseTextRegex(src: string): Result<[RegExp, number]> {
84
+ const pos = { value: 0 };
85
+ skipWs(src, pos);
86
+
87
+ if (pos.value >= src.length || src[pos.value] !== "/") {
88
+ return err(unterminatedRegex({ start: pos.value, end: pos.value }));
89
+ }
90
+ pos.value++; // skip opening '/'
91
+
92
+ const start = pos.value;
93
+ let escape = false;
94
+
95
+ while (pos.value < src.length) {
96
+ const b = src[pos.value];
97
+ pos.value++;
98
+
99
+ if (escape) {
100
+ escape = false;
101
+ continue;
102
+ }
103
+
104
+ if (b === "\\") {
105
+ escape = true;
106
+ continue;
107
+ }
108
+
109
+ if (b === "/") {
110
+ const inner = src.slice(start, pos.value - 1);
111
+ try {
112
+ const regex = new RegExp(inner);
113
+ skipWs(src, pos);
114
+ return ok([regex, pos.value]);
115
+ } catch {
116
+ return err(invalidRegex({ start: pos.value, end: pos.value }));
117
+ }
118
+ }
119
+ }
120
+
121
+ return err(unterminatedRegex({ start: pos.value, end: pos.value }));
122
+ }
123
+
124
+ /**
125
+ * Parses a CBOR value or dcbor-pattern expression.
126
+ *
127
+ * @param src - The source string
128
+ * @returns The parsed pattern and consumed character count, or an error
129
+ */
130
+ export function parseCborInner(src: string): Result<[Pattern, number]> {
131
+ if (createCborPattern === undefined || createCborPatternFromDcbor === undefined) {
132
+ return err(unknown());
133
+ }
134
+
135
+ const pos = { value: 0 };
136
+ skipWs(src, pos);
137
+
138
+ // Check if this is a dcbor-pattern expression (/patex/)
139
+ if (src[pos.value] === "/") {
140
+ pos.value++; // skip opening '/'
141
+ const start = pos.value;
142
+ let escape = false;
143
+
144
+ // Find the closing '/'
145
+ while (pos.value < src.length) {
146
+ const b = src[pos.value];
147
+ pos.value++;
148
+
149
+ if (escape) {
150
+ escape = false;
151
+ continue;
152
+ }
153
+
154
+ if (b === "\\") {
155
+ escape = true;
156
+ continue;
157
+ }
158
+
159
+ if (b === "/") {
160
+ const patternStr = src.slice(start, pos.value - 1);
161
+
162
+ // Parse the dcbor-pattern expression
163
+ const parseResult = parseDcborPattern(patternStr);
164
+ if (!parseResult.ok) {
165
+ return err(invalidPattern({ start, end: pos.value - 1 }));
166
+ }
167
+
168
+ skipWs(src, pos);
169
+ return ok([createCborPatternFromDcbor(parseResult.value), pos.value]);
170
+ }
171
+ }
172
+
173
+ return err(unterminatedRegex({ start: start - 1, end: pos.value }));
174
+ }
175
+
176
+ // Check if this is a UR (ur:type/value)
177
+ if (src.slice(pos.value, pos.value + 3) === "ur:") {
178
+ // For now, parse as CBOR diagnostic notation
179
+ // TODO: Add proper UR parsing when available
180
+ }
181
+
182
+ // Default: parse as CBOR diagnostic notation
183
+ const remaining = src.slice(pos.value);
184
+ const cborResult = cborFromDiagnostic(remaining);
185
+ if (!cborResult.ok) {
186
+ return err(unknown());
187
+ }
188
+
189
+ // Count consumed characters (approximation - full string was consumed)
190
+ const consumed = remaining.length;
191
+ return ok([createCborPattern(cborResult.value), pos.value + consumed]);
192
+ }
193
+
194
+ /**
195
+ * Parses an array pattern inner content.
196
+ *
197
+ * @param src - The source string (content between [ and ])
198
+ * @returns The parsed pattern and consumed character count, or an error
199
+ */
200
+ export function parseArrayInner(src: string): Result<[Pattern, number]> {
201
+ if (
202
+ createAnyArray === undefined ||
203
+ createArrayWithCount === undefined ||
204
+ createArrayWithRange === undefined ||
205
+ createArrayFromDcborPattern === undefined
206
+ ) {
207
+ return err(unknown());
208
+ }
209
+
210
+ const pos = { value: 0 };
211
+ skipWs(src, pos);
212
+
213
+ // Check for the simple "*" pattern first - matches any array
214
+ if (src[pos.value] === "*") {
215
+ pos.value++;
216
+ skipWs(src, pos);
217
+ return ok([createAnyArray(), pos.value]);
218
+ }
219
+
220
+ // Check for length patterns like {n}, {n,m}, {n,}
221
+ if (src[pos.value] === "{") {
222
+ pos.value++;
223
+ skipWs(src, pos);
224
+
225
+ // Parse the first number
226
+ const startPos = pos.value;
227
+ while (pos.value < src.length && src[pos.value] !== undefined && /\d/.test(src[pos.value])) {
228
+ pos.value++;
229
+ }
230
+ if (startPos === pos.value) {
231
+ return err(invalidRange({ start: pos.value, end: pos.value }));
232
+ }
233
+
234
+ const firstNum = parseInt(src.slice(startPos, pos.value), 10);
235
+ if (Number.isNaN(firstNum)) {
236
+ return err(invalidNumberFormat({ start: startPos, end: pos.value }));
237
+ }
238
+
239
+ skipWs(src, pos);
240
+
241
+ if (pos.value >= src.length) {
242
+ return err(unexpectedEndOfInput());
243
+ }
244
+
245
+ const ch = src[pos.value];
246
+
247
+ if (ch === "}") {
248
+ // {n} - exact count
249
+ pos.value++;
250
+ skipWs(src, pos);
251
+ return ok([createArrayWithCount(firstNum), pos.value]);
252
+ }
253
+
254
+ if (ch === ",") {
255
+ pos.value++;
256
+ skipWs(src, pos);
257
+
258
+ if (pos.value >= src.length) {
259
+ return err(unexpectedEndOfInput());
260
+ }
261
+
262
+ const nextCh = src[pos.value];
263
+
264
+ if (nextCh === "}") {
265
+ // {n,} - at least n
266
+ pos.value++;
267
+ skipWs(src, pos);
268
+ return ok([createArrayWithRange(firstNum, undefined), pos.value]);
269
+ }
270
+
271
+ if (nextCh !== undefined && /\d/.test(nextCh)) {
272
+ // {n,m} - range
273
+ const secondStart = pos.value;
274
+ while (
275
+ pos.value < src.length &&
276
+ src[pos.value] !== undefined &&
277
+ /\d/.test(src[pos.value])
278
+ ) {
279
+ pos.value++;
280
+ }
281
+ const secondNum = parseInt(src.slice(secondStart, pos.value), 10);
282
+ if (Number.isNaN(secondNum)) {
283
+ return err(invalidNumberFormat({ start: secondStart, end: pos.value }));
284
+ }
285
+
286
+ skipWs(src, pos);
287
+ if (pos.value >= src.length || src[pos.value] !== "}") {
288
+ return err(unexpectedEndOfInput());
289
+ }
290
+ pos.value++;
291
+ skipWs(src, pos);
292
+ return ok([createArrayWithRange(firstNum, secondNum), pos.value]);
293
+ }
294
+
295
+ return err(invalidRange({ start: pos.value, end: pos.value }));
296
+ }
297
+
298
+ return err(invalidRange({ start: pos.value, end: pos.value }));
299
+ }
300
+
301
+ // For any other pattern content, delegate to dcbor-pattern
302
+ const patternStr = `[${src.slice(pos.value)}]`;
303
+ const parseResult = parseDcborPattern(patternStr);
304
+ if (!parseResult.ok) {
305
+ return err(invalidPattern({ start: pos.value, end: src.length }));
306
+ }
307
+
308
+ // Create an array pattern that wraps the dcbor-pattern
309
+ const consumed = src.length - pos.value;
310
+ return ok([createArrayFromDcborPattern(parseResult.value), consumed]);
311
+ }
312
+
313
+ /**
314
+ * Parses a bare word (identifier-like token).
315
+ *
316
+ * @param src - The source string
317
+ * @returns The parsed word and consumed character count, or an error
318
+ */
319
+ export function parseBareWord(src: string): Result<[string, number]> {
320
+ const pos = { value: 0 };
321
+ skipWs(src, pos);
322
+
323
+ const start = pos.value;
324
+ while (pos.value < src.length) {
325
+ const ch = src[pos.value];
326
+ if (ch === " " || ch === "\t" || ch === "\n" || ch === "\r" || ch === "\f" || ch === ")") {
327
+ break;
328
+ }
329
+ pos.value++;
330
+ }
331
+
332
+ if (start === pos.value) {
333
+ return err(unexpectedEndOfInput());
334
+ }
335
+
336
+ const word = src.slice(start, pos.value);
337
+ skipWs(src, pos);
338
+ return ok([word, pos.value]);
339
+ }