@packet-schema/core 0.1.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.
Files changed (46) hide show
  1. package/dist/collect-refs.d.ts +4 -0
  2. package/dist/collect-refs.d.ts.map +1 -0
  3. package/dist/collect-refs.js +76 -0
  4. package/dist/collect-refs.js.map +1 -0
  5. package/dist/constraint.d.ts +66 -0
  6. package/dist/constraint.d.ts.map +1 -0
  7. package/dist/constraint.js +286 -0
  8. package/dist/constraint.js.map +1 -0
  9. package/dist/expr.d.ts +32 -0
  10. package/dist/expr.d.ts.map +1 -0
  11. package/dist/expr.js +190 -0
  12. package/dist/expr.js.map +1 -0
  13. package/dist/index.d.ts +17 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +19 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/layout.d.ts +7 -0
  18. package/dist/layout.d.ts.map +1 -0
  19. package/dist/layout.js +217 -0
  20. package/dist/layout.js.map +1 -0
  21. package/dist/normalize.d.ts +37 -0
  22. package/dist/normalize.d.ts.map +1 -0
  23. package/dist/normalize.js +857 -0
  24. package/dist/normalize.js.map +1 -0
  25. package/dist/types.d.ts +538 -0
  26. package/dist/types.d.ts.map +1 -0
  27. package/dist/types.js +27 -0
  28. package/dist/types.js.map +1 -0
  29. package/dist/utils.d.ts +3 -0
  30. package/dist/utils.d.ts.map +1 -0
  31. package/dist/utils.js +4 -0
  32. package/dist/utils.js.map +1 -0
  33. package/dist/validate.d.ts +8 -0
  34. package/dist/validate.d.ts.map +1 -0
  35. package/dist/validate.js +1222 -0
  36. package/dist/validate.js.map +1 -0
  37. package/dist/values.d.ts +31 -0
  38. package/dist/values.d.ts.map +1 -0
  39. package/dist/values.js +73 -0
  40. package/dist/values.js.map +1 -0
  41. package/dist/yaml.d.ts +11 -0
  42. package/dist/yaml.d.ts.map +1 -0
  43. package/dist/yaml.js +145 -0
  44. package/dist/yaml.js.map +1 -0
  45. package/package.json +35 -0
  46. package/schemas/psdl-0.5.yaml +1223 -0
@@ -0,0 +1,4 @@
1
+ import type { Packet } from "./types.js";
2
+ /** All plain field-id references reachable from a packet's expressions. */
3
+ export declare function collectPsdlRefs(packet: Packet): Set<string>;
4
+ //# sourceMappingURL=collect-refs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collect-refs.d.ts","sourceRoot":"","sources":["../src/collect-refs.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAmB,MAAM,EAAE,MAAM,YAAY,CAAC;AAY1D,2EAA2E;AAC3E,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CA4C3D"}
@@ -0,0 +1,76 @@
1
+ import { walkExpr } from "./expr.js";
2
+ import { isField } from "./utils.js";
3
+ function isExpr(value) {
4
+ return typeof value === "object" && value !== null && "kind" in value &&
5
+ typeof value.kind === "string";
6
+ }
7
+ function isUntilCount(value) {
8
+ return typeof value === "object" && value !== null && "until" in value &&
9
+ isExpr(value.until);
10
+ }
11
+ /** All plain field-id references reachable from a packet's expressions. */
12
+ export function collectPsdlRefs(packet) {
13
+ const out = new Set();
14
+ const visit = (e) => {
15
+ walkExpr(e, (n) => {
16
+ if (n.kind === "ref")
17
+ out.add(n.field);
18
+ });
19
+ };
20
+ const walk = (containers) => {
21
+ for (const c of containers) {
22
+ if (isField(c)) {
23
+ // A delimiter-terminated bytes length (`delimiter` form, §3) carries no Expr.
24
+ if (c.type.kind === "bytes" && isExpr(c.type.n))
25
+ visit(c.type.n);
26
+ if (c.computedFrom)
27
+ visit(c.computedFrom);
28
+ continue;
29
+ }
30
+ switch (c.kind) {
31
+ case "virtual":
32
+ visit(c.expr);
33
+ break;
34
+ case "group":
35
+ walk(c.children);
36
+ break;
37
+ case "bounded":
38
+ visit(c.bytes);
39
+ walk(c.fields);
40
+ break;
41
+ case "switch":
42
+ visit(c.on);
43
+ for (const arm of Object.values(c.cases))
44
+ walk(arm.fields);
45
+ break;
46
+ case "repeat":
47
+ if (isExpr(c.count))
48
+ visit(c.count);
49
+ else if (isUntilCount(c.count))
50
+ visit(c.count.until);
51
+ walk(c.element.fields);
52
+ break;
53
+ case "encrypted":
54
+ if (c.wireBits)
55
+ visit(c.wireBits);
56
+ walk(c.plaintext.fields);
57
+ break;
58
+ case "optional":
59
+ visit(c.when);
60
+ walk([c.container]);
61
+ break;
62
+ // align, ref: no inline field-id expressions
63
+ }
64
+ }
65
+ };
66
+ walk(packet.body);
67
+ if (packet.defs)
68
+ for (const def of Object.values(packet.defs))
69
+ walk(def.fields);
70
+ for (const con of packet.constraints ?? []) {
71
+ visit(con.lhs);
72
+ visit(con.rhs);
73
+ }
74
+ return out;
75
+ }
76
+ //# sourceMappingURL=collect-refs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collect-refs.js","sourceRoot":"","sources":["../src/collect-refs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ import type { Constraint, NormativeLevel, PacketEnv } from "./types.js";
2
+ export type PropagateOk = {
3
+ ok: PacketEnv;
4
+ };
5
+ /**
6
+ * A hard solver conflict (§9.1). `index` identifies the conflicting constraint
7
+ * within the `constraints` array (so an LSP can map the conflict back to a
8
+ * source range) and `env` carries the partially-propagated environment at the
9
+ * moment the conflict was detected — re-running `validateConstraints` over it
10
+ * reproduces the conflict with full per-constraint diagnostics. Both fields
11
+ * are additive (older callers that only read `conflict` keep working).
12
+ */
13
+ export type PropagateConflict = {
14
+ conflict: string;
15
+ index?: number;
16
+ env?: PacketEnv;
17
+ };
18
+ export type PropagateResult = PropagateOk | PropagateConflict;
19
+ export declare function propagate(constraints: Constraint[], env: PacketEnv, changedKey: string): PropagateResult;
20
+ /**
21
+ * Fixpoint back-propagation (§9): re-run the constraint list until no new
22
+ * field is resolved in a pass. Seeds the pass set from every key in `env`
23
+ * UNION every ref mentioned by a `must` (or level-less) constraint, so a
24
+ * constraint sharing no ref with the env — e.g. the literal-only
25
+ * `version == 4` — still fires and can start a resolution chain (§9
26
+ * "re-runs the full constraint list until no new fields are resolved").
27
+ */
28
+ export declare function propagateFixpoint(constraints: Constraint[], env: PacketEnv): PropagateResult;
29
+ /**
30
+ * A constraint mismatch diagnostic (§9.1). EVERY failing constraint — `must`
31
+ * included — is reported as one of these: `should`/`may` mismatches are
32
+ * advisories (warning/info), while a failing `must` additionally drives the
33
+ * hard `conflict` (so a `must` violation appears both as the `conflict` string
34
+ * and as a level-tagged entry here). `index` identifies the failing constraint
35
+ * within the `constraints` array (so an LSP can map the diagnostic back to a
36
+ * source range) and `doc` copies the constraint's authored doc string when
37
+ * present.
38
+ */
39
+ export type ConstraintDiagnostic = {
40
+ index: number;
41
+ level: NormativeLevel;
42
+ message: string;
43
+ doc?: string;
44
+ };
45
+ /**
46
+ * Evaluate every constraint for diagnostics (§9.1). `must` (and level-less)
47
+ * mismatches become a hard `conflict`; `should`/`may` mismatches are collected
48
+ * into `diagnostics` (level-tagged) so the caller can map them to warning/info
49
+ * severities. `should`/`may` never produce a conflict, matching the solver's
50
+ * back-propagation exclusion in `propagate`. EVERY failing constraint — `must`
51
+ * included — also appears in `diagnostics` with its index, so an LSP can map
52
+ * each violation (not just the first) back to a source range; the `conflict`
53
+ * string is derived from the first failing `must` in declaration order.
54
+ * Evaluation errors (e.g. division by zero in an expression) likewise never
55
+ * escape as exceptions: they become a diagnostic at the constraint's level
56
+ * (plus the hard conflict for `must`), so a single broken advisory constraint
57
+ * cannot crash diagnostics generation.
58
+ */
59
+ export declare function validateConstraints(constraints: Constraint[], env: PacketEnv): {
60
+ ok: true;
61
+ diagnostics?: ConstraintDiagnostic[];
62
+ } | {
63
+ conflict: string;
64
+ diagnostics: ConstraintDiagnostic[];
65
+ };
66
+ //# sourceMappingURL=constraint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constraint.d.ts","sourceRoot":"","sources":["../src/constraint.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAQ,cAAc,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE9E,MAAM,MAAM,WAAW,GAAG;IAAE,EAAE,EAAE,SAAS,CAAA;CAAE,CAAC;AAC5C;;;;;;;GAOG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,SAAS,CAAA;CAAE,CAAC;AACtF,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG,iBAAiB,CAAC;AA6I9D,wBAAgB,SAAS,CACvB,WAAW,EAAE,UAAU,EAAE,EACzB,GAAG,EAAE,SAAS,EACd,UAAU,EAAE,MAAM,GACjB,eAAe,CAsCjB;AAUD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,UAAU,EAAE,EACzB,GAAG,EAAE,SAAS,GACb,eAAe,CA2BjB;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,cAAc,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAOF;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,UAAU,EAAE,EACzB,GAAG,EAAE,SAAS,GACb;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,oBAAoB,EAAE,CAAA;CAAE,GACjD;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,oBAAoB,EAAE,CAAA;CAAE,CA0B5D"}
@@ -0,0 +1,286 @@
1
+ import { evalExpr, exprContains, MissingRefError, walkExpr } from "./expr.js";
2
+ function solveFor(expr, targetRef, known, env) {
3
+ if (expr.kind === "ref")
4
+ return expr.field === targetRef ? known : null;
5
+ if (expr.kind !== "op")
6
+ return null; // only linear op-trees are invertible
7
+ const aHas = containsRef(expr.a, targetRef);
8
+ const bHas = containsRef(expr.b, targetRef);
9
+ if (aHas === bHas)
10
+ return null;
11
+ if (aHas) {
12
+ const bVal = evalConst(expr.b, env);
13
+ if (bVal === null)
14
+ return null;
15
+ const peeled = invertLeft(expr.op, known, bVal);
16
+ if (peeled === null)
17
+ return null;
18
+ return solveFor(expr.a, targetRef, peeled, env);
19
+ }
20
+ else {
21
+ const aVal = evalConst(expr.a, env);
22
+ if (aVal === null)
23
+ return null;
24
+ const peeled = invertRight(expr.op, known, aVal);
25
+ if (peeled === null)
26
+ return null;
27
+ return solveFor(expr.b, targetRef, peeled, env);
28
+ }
29
+ }
30
+ function containsRef(expr, target) {
31
+ return exprContains(expr, (e) => e.kind === "ref" && e.field === target);
32
+ }
33
+ function evalConst(expr, env) {
34
+ try {
35
+ return evalExpr(expr, env);
36
+ }
37
+ catch (e) {
38
+ if (e instanceof MissingRefError)
39
+ return null;
40
+ throw e;
41
+ }
42
+ }
43
+ // Invert `a OP known = result` for `a` (the unknown on the LEFT operand).
44
+ // Shifts use the semantics of expr.ts (`<<` → `(a<<b)>>>0` unsigned, `>>` →
45
+ // arithmetic `a>>b`, §4). They are lossy and not uniquely invertible, so the
46
+ // candidate produced here is verified by re-evaluation in applyKnownSide
47
+ // before adoption.
48
+ function invertLeft(o, result, known) {
49
+ switch (o) {
50
+ case "+": return result - known;
51
+ case "-": return result + known;
52
+ case "*": return known === 0 ? null : Math.trunc(result / known);
53
+ case "/": return result * known;
54
+ case "%": return null;
55
+ case "<<": return result >>> known; // inverse of (a << known)>>>0
56
+ case ">>": return (result << known) | 0; // inverse of arithmetic a >> known
57
+ }
58
+ return null;
59
+ }
60
+ // Invert `known OP b = result` for `b` (the unknown on the RIGHT operand).
61
+ function invertRight(o, result, known) {
62
+ switch (o) {
63
+ case "+": return result - known;
64
+ case "-": return known - result;
65
+ case "*": return known === 0 ? null : Math.trunc(result / known);
66
+ case "/": return result === 0 ? null : Math.trunc(known / result);
67
+ case "%": return null;
68
+ // Shift amount as the unknown is not soundly invertible.
69
+ case "<<": return null;
70
+ case ">>": return null;
71
+ }
72
+ return null;
73
+ }
74
+ /** Refs in `expr` whose value is not yet known in `env`. */
75
+ function unknownRefs(expr, env) {
76
+ return uniqueRefs(expr).filter((r) => !env.has(r));
77
+ }
78
+ /**
79
+ * Given that `known` is the value of one side of an equality, try to use it to
80
+ * resolve the other (`other`) side: solve its single unknown, or detect a
81
+ * conflict when it is fully determined and disagrees. `knownSide` names the
82
+ * side `known` came from so conflict messages keep the lhs/rhs orientation,
83
+ * and `c` supplies the authored doc string for the message (§9.1).
84
+ */
85
+ function applyKnownSide(known, other, next, knownSide, c) {
86
+ const unknowns = unknownRefs(other, next);
87
+ if (unknowns.length === 0) {
88
+ let otherVal;
89
+ try {
90
+ otherVal = evalConst(other, next);
91
+ }
92
+ catch (e) {
93
+ // Mirror validateConstraints: a `must` expression that cannot be
94
+ // evaluated is a hard conflict, never an uncaught exception.
95
+ return { conflict: withDoc(`Constraint evaluation error: ${e instanceof Error ? e.message : String(e)}`, c) };
96
+ }
97
+ if (otherVal !== null && otherVal !== known) {
98
+ const [l, r] = knownSide === "lhs" ? [known, otherVal] : [otherVal, known];
99
+ return { conflict: withDoc(`Constraint failed: lhs=${l} rhs=${r}`, c) };
100
+ }
101
+ return null;
102
+ }
103
+ if (unknowns.length === 1) {
104
+ // Like the probe below, an evaluation error while inverting (e.g. a
105
+ // constant zero-divisor subtree next to the unknown ref) just means no
106
+ // candidate can be derived — not a conflict, and never an uncaught
107
+ // exception out of the solver. This matches validateConstraints, which
108
+ // skips the constraint when a side cannot be evaluated.
109
+ let solved;
110
+ try {
111
+ solved = solveFor(other, unknowns[0], known, next);
112
+ }
113
+ catch {
114
+ solved = null;
115
+ }
116
+ if (solved !== null) {
117
+ // Inversion through lossy operators (shifts, truncating division) is not
118
+ // guaranteed to round-trip under the unsigned evaluator. Adopt the solved
119
+ // value only if re-evaluating `other` actually reproduces `known`. An
120
+ // evaluation error on the probe (e.g. the candidate lands as a zero
121
+ // divisor) just means the candidate is not adoptable — not a conflict.
122
+ const probe = new Map(next);
123
+ probe.set(unknowns[0], solved);
124
+ let check;
125
+ try {
126
+ check = evalConst(other, probe);
127
+ }
128
+ catch {
129
+ check = null;
130
+ }
131
+ if (check === known)
132
+ next.set(unknowns[0], solved);
133
+ }
134
+ }
135
+ return null;
136
+ }
137
+ export function propagate(constraints, env, changedKey) {
138
+ const next = new Map(env);
139
+ for (const [index, c] of constraints.entries()) {
140
+ // Only `must` (and level-less) constraints back-propagate (§9.1). `should`/
141
+ // `may` are diagnostic-only and excluded from both value derivation and the
142
+ // conflict detection below, so relaxing a level never changes any
143
+ // wire-parsed value (it can drop solver-derived values and hard-failure
144
+ // detection that the `must` was providing — see §9.1).
145
+ if (c.level !== undefined && c.level !== "must")
146
+ continue;
147
+ // A constraint with no ref node at all (literal-only, or built solely from
148
+ // env-backed nullaries like `remaining`) can never match the changed-key
149
+ // gate, yet may still be violated — always evaluate it as a pure check.
150
+ const refLess = uniqueRefs(c.lhs).length === 0 && uniqueRefs(c.rhs).length === 0;
151
+ if (!refLess && !containsRef(c.lhs, changedKey) && !containsRef(c.rhs, changedKey))
152
+ continue;
153
+ let lhsVal;
154
+ let rhsVal;
155
+ try {
156
+ lhsVal = evalConst(c.lhs, next);
157
+ rhsVal = evalConst(c.rhs, next);
158
+ }
159
+ catch (e) {
160
+ // Mirror validateConstraints: an evaluation error (e.g. division by zero
161
+ // on wire-derived values) in a `must` constraint is a hard conflict, not
162
+ // an uncaught exception out of the solver.
163
+ return { conflict: withDoc(`Constraint evaluation error: ${e instanceof Error ? e.message : String(e)}`, c), index, env: next };
164
+ }
165
+ if (lhsVal !== null && rhsVal !== null) {
166
+ if (lhsVal !== rhsVal)
167
+ return { conflict: withDoc(`Constraint failed: lhs=${lhsVal} rhs=${rhsVal}`, c), index, env: next };
168
+ continue;
169
+ }
170
+ if (lhsVal !== null) {
171
+ const conflict = applyKnownSide(lhsVal, c.rhs, next, "lhs", c);
172
+ if (conflict)
173
+ return { ...conflict, index, env: next };
174
+ }
175
+ else if (rhsVal !== null) {
176
+ const conflict = applyKnownSide(rhsVal, c.lhs, next, "rhs", c);
177
+ if (conflict)
178
+ return { ...conflict, index, env: next };
179
+ }
180
+ }
181
+ return { ok: next };
182
+ }
183
+ function uniqueRefs(expr) {
184
+ const set = new Set();
185
+ walkExpr(expr, (e) => {
186
+ if (e.kind === "ref")
187
+ set.add(e.field);
188
+ });
189
+ return [...set];
190
+ }
191
+ /**
192
+ * Fixpoint back-propagation (§9): re-run the constraint list until no new
193
+ * field is resolved in a pass. Seeds the pass set from every key in `env`
194
+ * UNION every ref mentioned by a `must` (or level-less) constraint, so a
195
+ * constraint sharing no ref with the env — e.g. the literal-only
196
+ * `version == 4` — still fires and can start a resolution chain (§9
197
+ * "re-runs the full constraint list until no new fields are resolved").
198
+ */
199
+ export function propagateFixpoint(constraints, env) {
200
+ let current = new Map(env);
201
+ const seedKeys = new Set();
202
+ for (const c of constraints) {
203
+ if (c.level !== undefined && c.level !== "must")
204
+ continue; // §9.1
205
+ for (const r of uniqueRefs(c.lhs))
206
+ seedKeys.add(r);
207
+ for (const r of uniqueRefs(c.rhs))
208
+ seedKeys.add(r);
209
+ }
210
+ let changed = true;
211
+ let guard = 0;
212
+ const MAX_PASSES = 1000;
213
+ while (changed && guard++ < MAX_PASSES) {
214
+ changed = false;
215
+ const keys = new Set([...current.keys(), ...seedKeys]);
216
+ // Even with no keys at all (empty env, ref-less constraints only), run one
217
+ // pass so pure checks like `remaining == 0` still get evaluated.
218
+ if (keys.size === 0)
219
+ keys.add("");
220
+ for (const key of keys) {
221
+ const res = propagate(constraints, current, key);
222
+ if ("conflict" in res)
223
+ return res;
224
+ // Adopt newly resolved keys.
225
+ for (const [k, v] of res.ok) {
226
+ if (current.get(k) !== v) {
227
+ current = res.ok;
228
+ changed = true;
229
+ break;
230
+ }
231
+ }
232
+ }
233
+ }
234
+ return { ok: current };
235
+ }
236
+ /** Append the constraint's authored doc to a message, when present. */
237
+ function withDoc(message, c) {
238
+ return c.doc !== undefined ? `${message} (${c.doc})` : message;
239
+ }
240
+ /**
241
+ * Evaluate every constraint for diagnostics (§9.1). `must` (and level-less)
242
+ * mismatches become a hard `conflict`; `should`/`may` mismatches are collected
243
+ * into `diagnostics` (level-tagged) so the caller can map them to warning/info
244
+ * severities. `should`/`may` never produce a conflict, matching the solver's
245
+ * back-propagation exclusion in `propagate`. EVERY failing constraint — `must`
246
+ * included — also appears in `diagnostics` with its index, so an LSP can map
247
+ * each violation (not just the first) back to a source range; the `conflict`
248
+ * string is derived from the first failing `must` in declaration order.
249
+ * Evaluation errors (e.g. division by zero in an expression) likewise never
250
+ * escape as exceptions: they become a diagnostic at the constraint's level
251
+ * (plus the hard conflict for `must`), so a single broken advisory constraint
252
+ * cannot crash diagnostics generation.
253
+ */
254
+ export function validateConstraints(constraints, env) {
255
+ const diagnostics = [];
256
+ let hardConflict = null;
257
+ for (const [index, c] of constraints.entries()) {
258
+ const level = c.level ?? "must";
259
+ const report = (message) => {
260
+ if (level === "must" && hardConflict === null)
261
+ hardConflict = withDoc(message, c);
262
+ diagnostics.push({ index, level, message, ...(c.doc !== undefined ? { doc: c.doc } : {}) });
263
+ };
264
+ let l;
265
+ let r;
266
+ try {
267
+ l = evalConst(c.lhs, env);
268
+ r = evalConst(c.rhs, env);
269
+ }
270
+ catch (e) {
271
+ report(`Constraint evaluation error: ${e instanceof Error ? e.message : String(e)}`);
272
+ continue;
273
+ }
274
+ if (l === null || r === null)
275
+ continue;
276
+ if (l !== r)
277
+ report(`Constraint failed: lhs=${l} rhs=${r}`);
278
+ }
279
+ // A hard conflict is only ever set by report(), which always pushes the
280
+ // matching `must` diagnostic first — so `diagnostics` is necessarily
281
+ // non-empty here and the conflict return carries it unconditionally.
282
+ if (hardConflict !== null)
283
+ return { conflict: hardConflict, diagnostics };
284
+ return diagnostics.length ? { ok: true, diagnostics } : { ok: true };
285
+ }
286
+ //# sourceMappingURL=constraint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constraint.js","sourceRoot":"","sources":["../src/constraint.ts"],"names":[],"mappings":"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"}
package/dist/expr.d.ts ADDED
@@ -0,0 +1,32 @@
1
+ import type { BinOp, Expr, ExprWireSize, PacketEnv } from "./types.js";
2
+ export declare const lit: (value: number) => Expr;
3
+ export declare const ref: (field: string) => Expr;
4
+ export declare const op: (o: BinOp, a: Expr, b: Expr) => Expr;
5
+ export declare const cond: (test: Expr, t: Expr, f: Expr) => Expr;
6
+ export declare const peek: (bits: number, offset?: Expr) => Expr;
7
+ export declare const lookup: (key: Expr, table: Record<number, number>) => Expr;
8
+ export declare const wireSize: (target: string) => ExprWireSize;
9
+ export declare const prevIter: (field: string) => Expr;
10
+ export declare const remaining: () => Expr;
11
+ export declare const enclosingBits: () => Expr;
12
+ export declare const enclosingField: (field: string) => Expr;
13
+ export declare const peekEnvKey: (offset: number, bits: number) => string;
14
+ export declare const remainingEnvKey: () => string;
15
+ export declare const enclosingBitsEnvKey: () => string;
16
+ export declare const wireSizeEnvKey: (target: string) => string;
17
+ export declare const prevIterEnvKey: (field: string) => string;
18
+ export declare const enclosingFieldEnvKey: (field: string) => string;
19
+ export declare class MissingRefError extends Error {
20
+ readonly field: string;
21
+ constructor(field: string);
22
+ }
23
+ export declare function evalExpr(expr: Expr, env: PacketEnv): number;
24
+ /** Evaluate, returning `fallback` when a referenced field is missing (§10.3). */
25
+ export declare function evalExprOr(expr: Expr, env: PacketEnv, fallback?: number): number;
26
+ /** Plain field-id references (`ref`) reachable from an expression. */
27
+ export declare function exprRefs(expr: Expr): string[];
28
+ /** Visit every sub-expression node depth-first. */
29
+ export declare function walkExpr(expr: Expr, visit: (e: Expr) => void): void;
30
+ /** True if any sub-expression matches the predicate. */
31
+ export declare function exprContains(expr: Expr, pred: (e: Expr) => boolean): boolean;
32
+ //# sourceMappingURL=expr.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expr.d.ts","sourceRoot":"","sources":["../src/expr.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAMvE,eAAO,MAAM,GAAG,GAAI,OAAO,MAAM,KAAG,IAAgC,CAAC;AACrE,eAAO,MAAM,GAAG,GAAI,OAAO,MAAM,KAAG,IAAgC,CAAC;AACrE,eAAO,MAAM,EAAE,GAAI,GAAG,KAAK,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,KAAG,IAAqC,CAAC;AACtF,eAAO,MAAM,IAAI,GAAI,MAAM,IAAI,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,KAAG,IAAsC,CAAC;AAC3F,eAAO,MAAM,IAAI,GAAI,MAAM,MAAM,EAAE,SAAS,IAAI,KAAG,IAC6B,CAAC;AACjF,eAAO,MAAM,MAAM,GAAI,KAAK,IAAI,EAAE,OAAO,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAG,IAAwC,CAAC;AAC3G,eAAO,MAAM,QAAQ,GAAI,QAAQ,MAAM,KAAG,YAA8C,CAAC;AACzF,eAAO,MAAM,QAAQ,GAAI,OAAO,MAAM,KAAG,IAAqC,CAAC;AAC/E,eAAO,MAAM,SAAS,QAAO,IAA+B,CAAC;AAC7D,eAAO,MAAM,aAAa,QAAO,IAAmC,CAAC;AACrE,eAAO,MAAM,cAAc,GAAI,OAAO,MAAM,KAAG,IAA2C,CAAC;AAO3F,eAAO,MAAM,UAAU,GAAI,QAAQ,MAAM,EAAE,MAAM,MAAM,KAAG,MAAsC,CAAC;AACjG,eAAO,MAAM,eAAe,QAAO,MAAyB,CAAC;AAC7D,eAAO,MAAM,mBAAmB,QAAO,MAA6B,CAAC;AACrE,eAAO,MAAM,cAAc,GAAI,QAAQ,MAAM,KAAG,MAAiC,CAAC;AAClF,eAAO,MAAM,cAAc,GAAI,OAAO,MAAM,KAAG,MAAgC,CAAC;AAChF,eAAO,MAAM,oBAAoB,GAAI,OAAO,MAAM,KAAG,MAAiC,CAAC;AAEvF,qBAAa,eAAgB,SAAQ,KAAK;aACZ,KAAK,EAAE,MAAM;gBAAb,KAAK,EAAE,MAAM;CAI1C;AAMD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,SAAS,GAAG,MAAM,CAsF3D;AAED,iFAAiF;AACjF,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,QAAQ,SAAI,GAAG,MAAM,CAO3E;AAMD,sEAAsE;AACtE,wBAAgB,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CAM7C;AAED,mDAAmD;AACnD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,IAAI,KAAK,IAAI,GAAG,IAAI,CAiCnE;AAED,wDAAwD;AACxD,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,KAAK,OAAO,GAAG,OAAO,CAM5E"}