@bcts/envelope-pattern 1.0.0-alpha.12 → 1.0.0-alpha.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bcts/envelope-pattern",
3
- "version": "1.0.0-alpha.12",
3
+ "version": "1.0.0-alpha.14",
4
4
  "type": "module",
5
5
  "description": "Pattern matching for Gordian Envelope structures",
6
6
  "license": "BSD-2-Clause-Patent",
@@ -58,8 +58,8 @@
58
58
  "node": ">=18.0.0"
59
59
  },
60
60
  "devDependencies": {
61
- "@bcts/eslint": "workspace:*",
62
- "@bcts/tsconfig": "workspace:*",
61
+ "@bcts/eslint": "^0.1.0",
62
+ "@bcts/tsconfig": "^0.1.0",
63
63
  "eslint": "^9.39.2",
64
64
  "tsdown": "^0.18.3",
65
65
  "typedoc": "^0.28.15",
@@ -67,11 +67,10 @@
67
67
  "vitest": "^4.0.16"
68
68
  },
69
69
  "dependencies": {
70
- "@bcts/dcbor": "workspace:*",
71
- "@bcts/dcbor-pattern": "workspace:*",
72
- "@bcts/envelope": "workspace:*",
73
- "@bcts/components": "workspace:*",
74
- "@bcts/tags": "workspace:*",
75
- "@bcts/known-values": "workspace:*"
70
+ "@bcts/dcbor": "^1.0.0-alpha.14",
71
+ "@bcts/dcbor-parse": "^1.0.0-alpha.14",
72
+ "@bcts/dcbor-pattern": "^1.0.0-alpha.14",
73
+ "@bcts/envelope": "^1.0.0-alpha.14",
74
+ "@bcts/known-values": "^1.0.0-alpha.14"
76
75
  }
77
76
  }
@@ -7,13 +7,8 @@
7
7
  */
8
8
 
9
9
  import type { Cbor } from "@bcts/dcbor";
10
+ import { parseDcborItemPartial } from "@bcts/dcbor-parse";
10
11
  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
12
  import {
18
13
  type Result,
19
14
  ok,
@@ -175,20 +170,24 @@ export function parseCborInner(src: string): Result<[Pattern, number]> {
175
170
 
176
171
  // Check if this is a UR (ur:type/value)
177
172
  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
173
+ // Parse as UR and convert to CBOR
174
+ const remaining = src.slice(pos.value);
175
+ const parseResult = parseDcborItemPartial(remaining);
176
+ if (!parseResult.ok) {
177
+ return err(unknown());
178
+ }
179
+ const [cborValue, consumed] = parseResult.value;
180
+ return ok([createCborPattern(cborValue), pos.value + consumed]);
180
181
  }
181
182
 
182
183
  // Default: parse as CBOR diagnostic notation
183
184
  const remaining = src.slice(pos.value);
184
- const cborResult = cborFromDiagnostic(remaining);
185
- if (!cborResult.ok) {
185
+ const parseResult = parseDcborItemPartial(remaining);
186
+ if (!parseResult.ok) {
186
187
  return err(unknown());
187
188
  }
188
-
189
- // Count consumed characters (approximation - full string was consumed)
190
- const consumed = remaining.length;
191
- return ok([createCborPattern(cborResult.value), pos.value + consumed]);
189
+ const [cborValue, consumed] = parseResult.value;
190
+ return ok([createCborPattern(cborValue), pos.value + consumed]);
192
191
  }
193
192
 
194
193
  /**
@@ -0,0 +1,208 @@
1
+ /**
2
+ * @bcts/envelope-pattern - DCBor Pattern Integration
3
+ *
4
+ * This module provides integration between dcbor-pattern and
5
+ * bc-envelope-pattern, allowing dcbor patterns to be used as envelope patterns
6
+ * through conversion.
7
+ *
8
+ * This is a 1:1 TypeScript port of bc-envelope-pattern-rust dcbor_integration.rs
9
+ *
10
+ * @module envelope-pattern/pattern/dcbor-integration
11
+ */
12
+
13
+ import {
14
+ type Pattern as DCBORPattern,
15
+ type ValuePattern as DCBORValuePattern,
16
+ type StructurePattern as DCBORStructurePattern,
17
+ type MetaPattern as DCBORMetaPattern,
18
+ } from "@bcts/dcbor-pattern";
19
+
20
+ import type { Pattern } from "./index";
21
+ import type { Result } from "../error";
22
+ import { ok } from "../error";
23
+
24
+ // Import leaf pattern constructors
25
+ import {
26
+ BoolPattern,
27
+ NumberPattern,
28
+ TextPattern,
29
+ ByteStringPattern,
30
+ DatePattern,
31
+ KnownValuePattern,
32
+ NullPattern,
33
+ ArrayPattern,
34
+ MapPattern,
35
+ TaggedPattern,
36
+ CBORPattern,
37
+ leafBool,
38
+ leafNull,
39
+ leafNumber,
40
+ leafText,
41
+ leafByteString,
42
+ leafDate,
43
+ leafKnownValue,
44
+ leafArray,
45
+ leafMap,
46
+ leafTag,
47
+ leafCbor,
48
+ } from "./leaf";
49
+
50
+ // Import meta pattern constructors
51
+ import {
52
+ AnyPattern,
53
+ AndPattern,
54
+ OrPattern,
55
+ NotPattern,
56
+ metaAny,
57
+ metaAnd,
58
+ metaOr,
59
+ metaNot,
60
+ } from "./meta";
61
+
62
+ /**
63
+ * Convert a dcbor-pattern Pattern to a bc-envelope-pattern Pattern.
64
+ *
65
+ * This function serves as the bridge between the two pattern systems,
66
+ * allowing dcbor-pattern expressions to be used in envelope pattern contexts.
67
+ *
68
+ * @param dcborPattern - The dcbor-pattern Pattern to convert
69
+ * @returns The converted envelope pattern, or an error if conversion fails
70
+ */
71
+ export function convertDcborPatternToEnvelopePattern(dcborPattern: DCBORPattern): Result<Pattern> {
72
+ switch (dcborPattern.kind) {
73
+ case "Value":
74
+ return convertValuePatternToEnvelopePattern(dcborPattern.pattern);
75
+ case "Structure":
76
+ return convertStructurePatternToEnvelopePattern(dcborPattern.pattern);
77
+ case "Meta":
78
+ return convertMetaPatternToEnvelopePattern(dcborPattern.pattern, dcborPattern);
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Convert a dcbor-pattern ValuePattern to an envelope leaf pattern.
84
+ */
85
+ function convertValuePatternToEnvelopePattern(valuePattern: DCBORValuePattern): Result<Pattern> {
86
+ switch (valuePattern.type) {
87
+ case "Bool": {
88
+ const boolPattern = BoolPattern.fromDcborPattern(valuePattern.pattern);
89
+ return ok({ type: "Leaf", pattern: leafBool(boolPattern) });
90
+ }
91
+ case "Number": {
92
+ const numberPattern = NumberPattern.fromDcborPattern(valuePattern.pattern);
93
+ return ok({ type: "Leaf", pattern: leafNumber(numberPattern) });
94
+ }
95
+ case "Text": {
96
+ const textPattern = TextPattern.fromDcborPattern(valuePattern.pattern);
97
+ return ok({ type: "Leaf", pattern: leafText(textPattern) });
98
+ }
99
+ case "ByteString": {
100
+ const byteStringPattern = ByteStringPattern.fromDcborPattern(valuePattern.pattern);
101
+ return ok({ type: "Leaf", pattern: leafByteString(byteStringPattern) });
102
+ }
103
+ case "Date": {
104
+ const datePattern = DatePattern.fromDcborPattern(valuePattern.pattern);
105
+ return ok({ type: "Leaf", pattern: leafDate(datePattern) });
106
+ }
107
+ case "KnownValue": {
108
+ const knownValuePattern = KnownValuePattern.fromDcborPattern(valuePattern.pattern);
109
+ return ok({ type: "Leaf", pattern: leafKnownValue(knownValuePattern) });
110
+ }
111
+ case "Null": {
112
+ return ok({ type: "Leaf", pattern: leafNull(NullPattern.new()) });
113
+ }
114
+ case "Digest": {
115
+ // Digest patterns don't have a direct envelope equivalent yet
116
+ // For now, wrap as a generic CBOR pattern
117
+ const cborPattern = CBORPattern.fromDcborPattern({
118
+ kind: "Value",
119
+ pattern: valuePattern,
120
+ });
121
+ return ok({ type: "Leaf", pattern: leafCbor(cborPattern) });
122
+ }
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Convert a dcbor-pattern StructurePattern to an envelope pattern.
128
+ */
129
+ function convertStructurePatternToEnvelopePattern(
130
+ structurePattern: DCBORStructurePattern,
131
+ ): Result<Pattern> {
132
+ switch (structurePattern.type) {
133
+ case "Array": {
134
+ // Wrap the full dcbor pattern for array matching
135
+ const arrayPattern = ArrayPattern.fromDcborPattern({
136
+ kind: "Structure",
137
+ pattern: structurePattern,
138
+ });
139
+ return ok({ type: "Leaf", pattern: leafArray(arrayPattern) });
140
+ }
141
+ case "Map": {
142
+ // MapPattern only supports interval-based matching currently
143
+ // Use any() for general map patterns from dcbor
144
+ const mapPattern = MapPattern.any();
145
+ return ok({ type: "Leaf", pattern: leafMap(mapPattern) });
146
+ }
147
+ case "Tagged": {
148
+ const taggedPattern = TaggedPattern.fromDcborPattern(structurePattern.pattern);
149
+ return ok({ type: "Leaf", pattern: leafTag(taggedPattern) });
150
+ }
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Convert a dcbor-pattern MetaPattern to an envelope meta pattern.
156
+ */
157
+ function convertMetaPatternToEnvelopePattern(
158
+ metaPattern: DCBORMetaPattern,
159
+ originalPattern: DCBORPattern,
160
+ ): Result<Pattern> {
161
+ switch (metaPattern.type) {
162
+ case "Any": {
163
+ // The dcbor "any" pattern corresponds to our "any" meta pattern
164
+ return ok({ type: "Meta", pattern: metaAny(AnyPattern.new()) });
165
+ }
166
+ case "And": {
167
+ // Convert AND pattern by recursively converting each sub-pattern
168
+ // dcbor-pattern structure: { type: "And", pattern: AndPattern }
169
+ // where AndPattern is { variant: "And", patterns: Pattern[] }
170
+ const convertedPatterns: Pattern[] = [];
171
+ for (const pattern of metaPattern.pattern.patterns) {
172
+ const result = convertDcborPatternToEnvelopePattern(pattern);
173
+ if (!result.ok) return result;
174
+ convertedPatterns.push(result.value);
175
+ }
176
+ return ok({ type: "Meta", pattern: metaAnd(AndPattern.new(convertedPatterns)) });
177
+ }
178
+ case "Or": {
179
+ // Convert OR pattern by recursively converting each sub-pattern
180
+ // dcbor-pattern structure: { type: "Or", pattern: OrPattern }
181
+ // where OrPattern is { variant: "Or", patterns: Pattern[] }
182
+ const convertedPatterns: Pattern[] = [];
183
+ for (const pattern of metaPattern.pattern.patterns) {
184
+ const result = convertDcborPatternToEnvelopePattern(pattern);
185
+ if (!result.ok) return result;
186
+ convertedPatterns.push(result.value);
187
+ }
188
+ return ok({ type: "Meta", pattern: metaOr(OrPattern.new(convertedPatterns)) });
189
+ }
190
+ case "Not": {
191
+ // Convert NOT pattern by recursively converting the inner pattern
192
+ // dcbor-pattern structure: { type: "Not", pattern: NotPattern }
193
+ // where NotPattern is { variant: "Not", pattern: Pattern }
194
+ const innerResult = convertDcborPatternToEnvelopePattern(metaPattern.pattern.pattern);
195
+ if (!innerResult.ok) return innerResult;
196
+ return ok({ type: "Meta", pattern: metaNot(NotPattern.new(innerResult.value)) });
197
+ }
198
+ case "Capture":
199
+ case "Repeat":
200
+ case "Search":
201
+ case "Sequence": {
202
+ // These patterns don't have direct envelope equivalents yet
203
+ // For now, wrap as a generic CBOR pattern
204
+ const cborPattern = CBORPattern.fromDcborPattern(originalPattern);
205
+ return ok({ type: "Leaf", pattern: leafCbor(cborPattern) });
206
+ }
207
+ }
208
+ }
@@ -26,6 +26,7 @@ export * from "./structure";
26
26
  export * from "./meta";
27
27
  export * from "./matcher";
28
28
  export * from "./vm";
29
+ export * from "./dcbor-integration";
29
30
 
30
31
  // Import leaf patterns
31
32
  import {
@@ -717,3 +718,7 @@ registerAllFactories();
717
718
  // Register VM pattern functions to resolve circular dependencies
718
719
  import { registerVMPatternFunctions } from "./vm";
719
720
  registerVMPatternFunctions(patternPathsWithCaptures, patternMatches, patternPaths);
721
+
722
+ // Register pattern match function for meta patterns
723
+ import { registerPatternMatchFn } from "./matcher";
724
+ registerPatternMatchFn(patternMatches);
@@ -86,3 +86,34 @@ export function compileAsAtomic(
86
86
  literals.push(pat);
87
87
  code.push({ type: "MatchPredicate", literalIndex: idx });
88
88
  }
89
+
90
+ // ============================================================================
91
+ // Pattern Match Registry
92
+ // ============================================================================
93
+
94
+ /**
95
+ * Registry for pattern matching function to break circular dependencies.
96
+ * This allows meta patterns to match child patterns without importing from index.ts.
97
+ */
98
+ let patternMatchFn: ((pattern: Pattern, haystack: Envelope) => boolean) | undefined;
99
+
100
+ /**
101
+ * Registers the pattern match function.
102
+ * Called from index.ts after all patterns are defined.
103
+ */
104
+ export function registerPatternMatchFn(
105
+ fn: (pattern: Pattern, haystack: Envelope) => boolean,
106
+ ): void {
107
+ patternMatchFn = fn;
108
+ }
109
+
110
+ /**
111
+ * Match a pattern against an envelope using the registered match function.
112
+ * Used by meta patterns to match child patterns.
113
+ */
114
+ export function matchPattern(pattern: Pattern, haystack: Envelope): boolean {
115
+ if (patternMatchFn === undefined) {
116
+ throw new Error("Pattern match function not registered");
117
+ }
118
+ return patternMatchFn(pattern, haystack);
119
+ }
@@ -8,7 +8,7 @@
8
8
 
9
9
  import type { Envelope } from "@bcts/envelope";
10
10
  import type { Path } from "../../format";
11
- import type { Matcher } from "../matcher";
11
+ import { type Matcher, matchPattern } from "../matcher";
12
12
  import type { Instr } from "../vm";
13
13
  import type { Pattern } from "../index";
14
14
 
@@ -46,10 +46,7 @@ export class AndPattern implements Matcher {
46
46
  }
47
47
 
48
48
  pathsWithCaptures(haystack: Envelope): [Path[], Map<string, Path[]>] {
49
- const allMatch = this.#patterns.every((pattern) => {
50
- const matcher = pattern as unknown as Matcher;
51
- return matcher.matches(haystack);
52
- });
49
+ const allMatch = this.#patterns.every((pattern) => matchPattern(pattern, haystack));
53
50
 
54
51
  const paths = allMatch ? [[haystack]] : [];
55
52
  return [paths, new Map<string, Path[]>()];
@@ -8,7 +8,7 @@
8
8
 
9
9
  import type { Envelope } from "@bcts/envelope";
10
10
  import type { Path } from "../../format";
11
- import type { Matcher } from "../matcher";
11
+ import { type Matcher, matchPattern } from "../matcher";
12
12
  import type { Instr } from "../vm";
13
13
  import type { Pattern } from "../index";
14
14
 
@@ -47,8 +47,7 @@ export class NotPattern implements Matcher {
47
47
 
48
48
  pathsWithCaptures(haystack: Envelope): [Path[], Map<string, Path[]>] {
49
49
  // If the inner pattern doesn't match, then we return the current envelope as a match
50
- const matcher = this.#pattern as unknown as Matcher;
51
- const paths = !matcher.matches(haystack) ? [[haystack]] : [];
50
+ const paths = !matchPattern(this.#pattern, haystack) ? [[haystack]] : [];
52
51
  return [paths, new Map<string, Path[]>()];
53
52
  }
54
53
 
@@ -8,7 +8,7 @@
8
8
 
9
9
  import type { Envelope } from "@bcts/envelope";
10
10
  import type { Path } from "../../format";
11
- import type { Matcher } from "../matcher";
11
+ import { type Matcher, matchPattern } from "../matcher";
12
12
  import type { Instr } from "../vm";
13
13
  import type { Pattern } from "../index";
14
14
 
@@ -46,10 +46,7 @@ export class OrPattern implements Matcher {
46
46
  }
47
47
 
48
48
  pathsWithCaptures(haystack: Envelope): [Path[], Map<string, Path[]>] {
49
- const anyMatch = this.#patterns.some((pattern) => {
50
- const matcher = pattern as unknown as Matcher;
51
- return matcher.matches(haystack);
52
- });
49
+ const anyMatch = this.#patterns.some((pattern) => matchPattern(pattern, haystack));
53
50
 
54
51
  const paths = anyMatch ? [[haystack]] : [];
55
52
  return [paths, new Map<string, Path[]>()];