gdp-wired 0.4.0 → 0.5.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/README.md +4 -1
- package/dist/core/dsl.d.ts +31 -14
- package/dist/core/dsl.js +143 -103
- package/dist/kernel/kernel.d.ts +6 -0
- package/dist/kernel/kernel.js +120 -10
- package/dist/kernel/prove.js +174 -50
- package/dist/lean-kernel/emit.js +34 -10
- package/dist/lean-kernel/library.js +4 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -148,6 +148,9 @@ const failsClosed = trial.decide('a missing fact means no trial', ({spent}) => s
|
|
|
148
148
|
- **Kinds**: `nat`, `upTo(k)`, `bool`, `oneOf(...)`, `maybe(kind)`, `int`, `decimal(places)`, `str({like, maxLength})`;
|
|
149
149
|
their assumptions (a bit is 0 or 1, a choice is in range) go to the kernel by themselves, and
|
|
150
150
|
`encodeInput`/`encodeParams` turn runtime values into the numbers the machine reads.
|
|
151
|
+
- **State of any kind**: `.state({open: bool.from(true), status: oneOf('new', 'done').from('new'), owner: str().from(''), note: maybe(nat).from(null)})`;
|
|
152
|
+
a step writes each kind's own values, `when(c, a, b)` chooses between any two values of a kind, and `rule.decode(state)`
|
|
153
|
+
reads them back as runtime values.
|
|
151
154
|
- **Integers and decimals**: `int` and `decimal(places)` inputs, `.ints({...})` and `.decimals(places, {...})` state;
|
|
152
155
|
`plus`, `minus`, `negate`, `times(k)` (k may be negative), `min`, `max`, `abs`, `isNegative`, comparisons with numbers
|
|
153
156
|
(a decimal's literals are scaled: `price.atMost(19.99)`). Each is two naturals underneath, so the kernel and the Lean
|
|
@@ -226,7 +229,7 @@ Lean, compiled natively, wins where the state stays small. Details in [`docs/com
|
|
|
226
229
|
| `core/` | Machines, the interpreter and JS compiler, the builders, composition (`compose.ts`), the linear solver and its checker, Houdini invariants, models, the first Lean export |
|
|
227
230
|
| `kernel/` | The trusted kernel and the untrusted prover, with soundness fuzz (`kernel.test.ts`, `history.test.ts`) |
|
|
228
231
|
| `lean-kernel/` | The kernel's Lean twin, its soundness proofs, and the exported trees |
|
|
229
|
-
| `examples/` | The builders at work: `domains/` (92 rules across nine domains, each with laws and broken variants), `harness.ts` (the triple check), `readme.ts`, `budget.ts`, `composition.ts`, `sets.ts`, `quantifiers.ts`, `functions.ts`, `lists.ts`, `sizes.ts`, `liveness.ts`, and `jev-fabric/` (a Bend project's 59 proven laws, ported) |
|
|
232
|
+
| `examples/` | The builders at work: `domains/` (92 rules across nine domains, each with laws and broken variants), `harness.ts` (the triple check), `readme.ts`, `budget.ts`, `composition.ts`, `sets.ts`, `quantifiers.ts`, `functions.ts`, `lists.ts`, `sizes.ts`, `liveness.ts`, `steps.ts` (laws over up to 16 steps), and `jev-fabric/` (a Bend project's 59 proven laws, ported) |
|
|
230
233
|
| `docs/` | The comparison with Bend and Lean, and the full scope |
|
|
231
234
|
| `compare/` | The same three rules in gdp-wired, Bend and plain Lean, checked and measured |
|
|
232
235
|
| `research/` | Everything around the library ([`research/README.md`](research/README.md)): the GPU searches (`search/`, `hw/`, `modal-gpu/`), invariant and rank synthesis (`synth/`), relational laws and guard synthesis (`next/`), bit-vector laws (`sat/`), the enumeration and benchmarks (`enum/`, `gpu/`, `perf/`), the first Lean checker (`lean/`) and the first prototype, the audit path (`audit/`) |
|
package/dist/core/dsl.d.ts
CHANGED
|
@@ -39,19 +39,32 @@ export declare function when(c: Cond, then: TermLike, otherwise: TermLike): Term
|
|
|
39
39
|
export declare function when(c: Cond, then: Decimal, otherwise: Decimal | number): Decimal;
|
|
40
40
|
export declare function when(c: Cond, then: Decimal | number, otherwise: Decimal): Decimal;
|
|
41
41
|
export declare function when(c: Cond, then: IntLike, otherwise: IntLike): Int;
|
|
42
|
+
export declare function when<K extends string>(c: Cond, then: Choice<K>, otherwise: Choice<K>): Choice<K>;
|
|
43
|
+
export declare function when(c: Cond, then: Str, otherwise: Str): Str;
|
|
44
|
+
export declare function when<V>(c: Cond, then: Maybe<V>, otherwise: Maybe<V>): Maybe<V>;
|
|
45
|
+
export declare function when(c: Cond, then: Cond, otherwise: Cond): Cond;
|
|
42
46
|
export declare const choose: (c: Cond, then: Cond, otherwise: Cond) => Cond;
|
|
43
47
|
export declare const count: (c: Cond) => Term;
|
|
44
48
|
export declare const sum: (first: TermLike, ...rest: TermLike[]) => Term;
|
|
45
49
|
type Rand = (k: number) => number;
|
|
46
50
|
type Raw = number | boolean | string | null | undefined;
|
|
51
|
+
export type At = string | ((name: string) => string);
|
|
52
|
+
export type Init<V> = {
|
|
53
|
+
readonly kind: Kind<V>;
|
|
54
|
+
readonly init: Raw | ((p: never) => unknown);
|
|
55
|
+
};
|
|
47
56
|
export declare abstract class Kind<V> {
|
|
48
57
|
readonly _value: V;
|
|
49
58
|
abstract vars(name: string): string[];
|
|
50
59
|
abstract assume(name: string): Cond[];
|
|
51
|
-
abstract value(name: string,
|
|
60
|
+
abstract value(name: string, at?: At): V;
|
|
52
61
|
abstract encode(name: string, raw: Raw): Record<string, number>;
|
|
53
62
|
abstract random(name: string, rand: Rand, bound: number): Record<string, number>;
|
|
63
|
+
abstract store(name: string, value: unknown): Record<string, TermLike>;
|
|
64
|
+
abstract decode(name: string, vars: Record<string, number>): Raw;
|
|
65
|
+
abstract empty(): Raw;
|
|
54
66
|
sample(_rand: Rand, _bound: number): Raw;
|
|
67
|
+
from(init: Raw | ((p: never) => unknown)): Init<V>;
|
|
55
68
|
}
|
|
56
69
|
export type IntLike = Int | Term | number;
|
|
57
70
|
export declare class Int {
|
|
@@ -117,8 +130,8 @@ export declare class Choice<K extends string> {
|
|
|
117
130
|
}
|
|
118
131
|
export declare const oneOf: <const K extends string>(...values: K[]) => Kind<Choice<K>>;
|
|
119
132
|
export declare class Maybe<V> {
|
|
120
|
-
|
|
121
|
-
|
|
133
|
+
readonly present: Term;
|
|
134
|
+
readonly inner: V;
|
|
122
135
|
constructor(present: Term, inner: V);
|
|
123
136
|
get isSome(): Cond;
|
|
124
137
|
get isNone(): Cond;
|
|
@@ -142,8 +155,11 @@ export declare class Str {
|
|
|
142
155
|
readonly length: Term;
|
|
143
156
|
private readonly bits;
|
|
144
157
|
constructor(id: Term, length: Term, bits: ReadonlyMap<string, Term>);
|
|
158
|
+
get patterns(): string[];
|
|
159
|
+
choose(c: Cond, other: Str): Str;
|
|
145
160
|
like(pattern: string): Cond;
|
|
146
161
|
get isEmpty(): Cond;
|
|
162
|
+
bit(pattern: string): Term;
|
|
147
163
|
eq(b: Str | string): Cond;
|
|
148
164
|
ne(b: Str | string): Cond;
|
|
149
165
|
}
|
|
@@ -236,8 +252,9 @@ type ValuesOf<K extends Kinds> = {
|
|
|
236
252
|
[n in keyof K]: K[n] extends Kind<infer V> ? V : never;
|
|
237
253
|
};
|
|
238
254
|
type Obj = object;
|
|
255
|
+
type Writes<V> = V extends Decimal ? DecimalLike | string : V extends Int ? IntLike : V extends Cond ? Cond | boolean : V extends Choice<infer K> ? Choice<K> | K : V extends Maybe<infer W> ? Maybe<W> | Writes<W> | null : V extends Str ? Str | string : TermLike;
|
|
239
256
|
type Update<S extends Obj, M extends Obj> = Partial<{
|
|
240
|
-
[k in keyof S]: S[k]
|
|
257
|
+
[k in keyof S]: Writes<S[k]>;
|
|
241
258
|
} & {
|
|
242
259
|
[k in keyof M]: MapWrite;
|
|
243
260
|
}>;
|
|
@@ -248,7 +265,7 @@ type Spec = {
|
|
|
248
265
|
state: [string, number | ((p: never) => TermLike)][];
|
|
249
266
|
maps: [string, number][];
|
|
250
267
|
lists?: string[];
|
|
251
|
-
|
|
268
|
+
kinded?: [string, Kind<unknown>][];
|
|
252
269
|
carried?: string[];
|
|
253
270
|
fns?: [string, FnDef][];
|
|
254
271
|
facts?: ((env: never) => Record<string, Cond | Forall>)[];
|
|
@@ -273,8 +290,8 @@ export declare class Rule<P extends Obj = {}, I extends Obj = {}, S extends Obj
|
|
|
273
290
|
get name(): string;
|
|
274
291
|
params<K extends Kinds>(kinds: K): Rule<P & ValuesOf<K>, I, S, M, F, R, Su, Q>;
|
|
275
292
|
inputs<K extends Kinds>(kinds: K): Rule<P, I & ValuesOf<K>, S, M, F, R, Su, Q>;
|
|
276
|
-
state<T extends Record<string, number | ((p: P) => TermLike)
|
|
277
|
-
[k in keyof T]: Term;
|
|
293
|
+
state<T extends Record<string, number | ((p: P) => TermLike) | Init<unknown>>>(init: T): Rule<P, I, S & {
|
|
294
|
+
[k in keyof T]: T[k] extends Init<infer V> ? V : Term;
|
|
278
295
|
}, M, F, R, Su, Q>;
|
|
279
296
|
maps<T extends Record<string, number>>(init: T): Rule<P, I, S, M & {
|
|
280
297
|
[k in keyof T]: MapRef;
|
|
@@ -285,7 +302,6 @@ export declare class Rule<P extends Obj = {}, I extends Obj = {}, S extends Obj
|
|
|
285
302
|
decimals<T extends Record<string, number | string | ((p: P) => DecimalLike)>>(places: number, init: T): Rule<P, I, S & {
|
|
286
303
|
[k in keyof T]: Decimal;
|
|
287
304
|
}, M, F, R, Su, Q>;
|
|
288
|
-
private signedState;
|
|
289
305
|
lists<const N extends string>(...names: N[]): Rule<P, I, S & {
|
|
290
306
|
[k in N as `${k}_len`]: Term;
|
|
291
307
|
}, M & {
|
|
@@ -330,7 +346,7 @@ export declare class Rule<P extends Obj = {}, I extends Obj = {}, S extends Obj
|
|
|
330
346
|
decide(name: string, given: (env: P & I) => Cond | Cond[], then: (env: P & I) => Cond): Decision;
|
|
331
347
|
relate(name: string, given: (a: P & I, b: P & I) => Cond | Cond[], then: (a: P & I, b: P & I) => Cond): Decision;
|
|
332
348
|
private kindAssumptions;
|
|
333
|
-
private
|
|
349
|
+
private afterEnv;
|
|
334
350
|
private env;
|
|
335
351
|
private names;
|
|
336
352
|
private whatIs;
|
|
@@ -372,7 +388,7 @@ export declare class Rule<P extends Obj = {}, I extends Obj = {}, S extends Obj
|
|
|
372
388
|
}): Request;
|
|
373
389
|
randomParams(rand: Rand, bound: number): Record<string, number>;
|
|
374
390
|
randomInput(rand: Rand, bound: number): Request;
|
|
375
|
-
decode(st: State): Record<string,
|
|
391
|
+
decode(st: State): Record<string, Raw>;
|
|
376
392
|
get orderedBy(): string | undefined;
|
|
377
393
|
interpreter(): {
|
|
378
394
|
init: (params: {
|
|
@@ -410,15 +426,16 @@ export type Decision = {
|
|
|
410
426
|
goal: Goal;
|
|
411
427
|
};
|
|
412
428
|
type Result<T> = {
|
|
413
|
-
[k in keyof T]: T[k] extends Cond ? Cond : T[k] extends Decimal ? Decimal : T[k] extends Int ? Int : Term;
|
|
429
|
+
[k in keyof T]: T[k] extends Cond ? Cond : T[k] extends Decimal ? Decimal : T[k] extends Int ? Int : T[k] extends Choice<infer K> ? Choice<K> : T[k] extends Str ? Str : Term;
|
|
414
430
|
};
|
|
415
431
|
type Value<T> = {
|
|
416
|
-
[k in keyof T]: T[k] extends Cond ? boolean : number;
|
|
432
|
+
[k in keyof T]: T[k] extends Cond ? boolean : T[k] extends Choice<infer K> ? K : T[k] extends Str ? string : number;
|
|
417
433
|
};
|
|
434
|
+
type Field = Term | Cond | Int | Decimal | Choice<string> | Str;
|
|
418
435
|
export declare class Fun<P extends Obj = {}, I extends Obj = {}, O extends Obj = {}> {
|
|
419
436
|
private readonly r;
|
|
420
437
|
private readonly out?;
|
|
421
|
-
constructor(r: Rule<P, I>, out?: ((env: never) => Record<string, TermLike |
|
|
438
|
+
constructor(r: Rule<P, I>, out?: ((env: never) => Record<string, TermLike | Field>) | undefined);
|
|
422
439
|
get name(): string;
|
|
423
440
|
params<K extends Kinds>(kinds: K): Fun<P & ValuesOf<K>, I, O>;
|
|
424
441
|
inputs<K extends Kinds>(kinds: K): Fun<P, I & ValuesOf<K>, O>;
|
|
@@ -428,7 +445,7 @@ export declare class Fun<P extends Obj = {}, I extends Obj = {}, O extends Obj =
|
|
|
428
445
|
facts(f: (p: P) => Record<string, Cond | Forall>): Fun<P, I, O>;
|
|
429
446
|
pre(f: (p: P) => Cond): Fun<P, I, O>;
|
|
430
447
|
assume(f: (env: P & I) => Cond): Fun<P, I, O>;
|
|
431
|
-
returns<T extends Record<string, TermLike |
|
|
448
|
+
returns<T extends Record<string, TermLike | Field>>(f: (env: P & I) => T): Fun<P, I, Result<T>>;
|
|
432
449
|
private result;
|
|
433
450
|
law(name: string, given: (env: P & I, out: O) => Cond | Cond[], then: (out: O, env: P & I) => Cond): Decision;
|
|
434
451
|
relate(name: string, given: (a: P & I, b: P & I) => Cond | Cond[], then: (outA: O, outB: O, a: P & I, b: P & I) => Cond): Decision;
|
package/dist/core/dsl.js
CHANGED
|
@@ -82,6 +82,17 @@ export const not = (c) => c.not();
|
|
|
82
82
|
export const min = (a, b) => new Term({ op: 'min', a: termOf(a), b: termOf(b) }, both(valOf(a), valOf(b), Math.min));
|
|
83
83
|
export const max = (a, b) => new Term({ op: 'max', a: termOf(a), b: termOf(b) }, both(valOf(a), valOf(b), Math.max));
|
|
84
84
|
export function when(c, then, otherwise) {
|
|
85
|
+
if (then instanceof Cond && otherwise instanceof Cond)
|
|
86
|
+
return choose(c, then, otherwise);
|
|
87
|
+
if (then instanceof Choice && otherwise instanceof Choice)
|
|
88
|
+
return new Choice(when(c, then.n, otherwise.n), then.values);
|
|
89
|
+
if (then instanceof Str && otherwise instanceof Str)
|
|
90
|
+
return then.choose(c, otherwise);
|
|
91
|
+
if (then instanceof Maybe && otherwise instanceof Maybe)
|
|
92
|
+
return new Maybe(when(c, then.present, otherwise.present), when(c, then.inner, otherwise.inner));
|
|
93
|
+
return pickNumbers(c, then, otherwise);
|
|
94
|
+
}
|
|
95
|
+
function pickNumbers(c, then, otherwise) {
|
|
85
96
|
if (then instanceof Decimal || otherwise instanceof Decimal) {
|
|
86
97
|
const places = (then instanceof Decimal ? then : otherwise).places, d = new Decimal(Int.of(0), places);
|
|
87
98
|
return new Decimal(when(c, d.lift(then), d.lift(otherwise)), places);
|
|
@@ -97,11 +108,13 @@ export const choose = (c, then, otherwise) => any(all(c, then), all(c.not(), oth
|
|
|
97
108
|
// 1 where c holds, else 0: what a sum over a history adds per record.
|
|
98
109
|
export const count = (c) => when(c, 1, 0);
|
|
99
110
|
export const sum = (first, ...rest) => rest.reduce((acc, t) => acc.plus(t), typeof first === 'number' ? lit(first) : first);
|
|
100
|
-
|
|
101
|
-
|
|
111
|
+
const named = (at, n) => (typeof at === 'string' ? at + n : at(n));
|
|
112
|
+
const primed = (n) => `${n}'`;
|
|
102
113
|
export class Kind {
|
|
103
114
|
// A runtime value of the kind (what encode takes), for comparing a rule with the code it describes (compare).
|
|
104
115
|
sample(_rand, _bound) { throw new Error('this kind draws no runtime values'); }
|
|
116
|
+
// A state variable of this kind starting at a runtime value, or at a value over the params: `.state({open: bool.from(true)})`.
|
|
117
|
+
from(init) { return { kind: this, init }; }
|
|
105
118
|
}
|
|
106
119
|
const v = (name) => new Term({ op: 'var', name }, scope?.vars[name]);
|
|
107
120
|
let scope;
|
|
@@ -177,7 +190,6 @@ export class Int {
|
|
|
177
190
|
}
|
|
178
191
|
// The same value with at most one part nonzero (what a state variable holds).
|
|
179
192
|
const normal = (i) => (isZero(i.neg) || isZero(i.pos) ? i : new Int(i.pos.minus(i.neg), i.neg.minus(i.pos)));
|
|
180
|
-
const termValue = (t) => (t.e.op === 'const' ? t.e.value : 0);
|
|
181
193
|
const scaledOf = (x, places, what = 'a decimal') => {
|
|
182
194
|
const text = typeof x === 'number' ? x.toFixed(places + 3) : x.trim();
|
|
183
195
|
const m = /^(-?)(\d+)(?:\.(\d+))?$/.exec(text);
|
|
@@ -234,7 +246,10 @@ class NatKind extends Kind {
|
|
|
234
246
|
}
|
|
235
247
|
vars(name) { return [name]; }
|
|
236
248
|
assume(name) { return this.maxValue === undefined ? [] : [v(name).atMost(this.maxValue)]; }
|
|
237
|
-
value(name,
|
|
249
|
+
value(name, at = '') { return v(named(at, name)); }
|
|
250
|
+
store(name, value) { return { [name]: value }; }
|
|
251
|
+
decode(name, vars) { return vars[name]; }
|
|
252
|
+
empty() { return 0; }
|
|
238
253
|
encode(name, raw) {
|
|
239
254
|
const n = asNat(name, raw);
|
|
240
255
|
if (this.maxValue !== undefined && n > this.maxValue)
|
|
@@ -258,7 +273,10 @@ export class Bool extends Cond {
|
|
|
258
273
|
class BoolKind extends Kind {
|
|
259
274
|
vars(name) { return [name]; }
|
|
260
275
|
assume(name) { return [v(name).atMost(1)]; }
|
|
261
|
-
value(name,
|
|
276
|
+
value(name, at = '') { return new Bool(v(named(at, name))); }
|
|
277
|
+
store(name, value) { return { [name]: typeof value === 'boolean' ? (value ? 1 : 0) : count(value) }; }
|
|
278
|
+
decode(name, vars) { return vars[name] === 1; }
|
|
279
|
+
empty() { return false; }
|
|
262
280
|
encode(name, raw) {
|
|
263
281
|
if (typeof raw === 'boolean')
|
|
264
282
|
return { [name]: raw ? 1 : 0 };
|
|
@@ -304,7 +322,10 @@ class OneOfKind extends Kind {
|
|
|
304
322
|
}
|
|
305
323
|
vars(name) { return [name]; }
|
|
306
324
|
assume(name) { return [v(name).lt(this.values.length)]; }
|
|
307
|
-
value(name,
|
|
325
|
+
value(name, at = '') { return new Choice(v(named(at, name)), this.values); }
|
|
326
|
+
store(name, value) { return { [name]: value instanceof Choice ? value.n : this.encode(name, value)[name] }; }
|
|
327
|
+
decode(name, vars) { return this.values[vars[name]]; }
|
|
328
|
+
empty() { return this.values[0]; }
|
|
308
329
|
encode(name, raw) {
|
|
309
330
|
const i = this.values.indexOf(raw);
|
|
310
331
|
if (i < 0)
|
|
@@ -345,7 +366,17 @@ class MaybeKind extends Kind {
|
|
|
345
366
|
has(name) { return `${name}$some`; }
|
|
346
367
|
vars(name) { return [this.has(name), ...this.of.vars(name)]; }
|
|
347
368
|
assume(name) { return [v(this.has(name)).atMost(1), ...this.of.assume(name)]; }
|
|
348
|
-
value(name,
|
|
369
|
+
value(name, at = '') { return new Maybe(v(named(at, this.has(name))), this.of.value(name, at)); }
|
|
370
|
+
// null for none, a Maybe as it is, any other value as some.
|
|
371
|
+
store(name, value) {
|
|
372
|
+
if (value === null || value === undefined)
|
|
373
|
+
return { [this.has(name)]: 0, ...this.of.store(name, this.of.empty()) };
|
|
374
|
+
if (value instanceof Maybe)
|
|
375
|
+
return { [this.has(name)]: value.present, ...this.of.store(name, value.inner) };
|
|
376
|
+
return { [this.has(name)]: 1, ...this.of.store(name, value) };
|
|
377
|
+
}
|
|
378
|
+
decode(name, vars) { return vars[this.has(name)] === 1 ? this.of.decode(name, vars) : null; }
|
|
379
|
+
empty() { return null; }
|
|
349
380
|
encode(name, raw) {
|
|
350
381
|
if (raw === null || raw === undefined)
|
|
351
382
|
return { [this.has(name)]: 0, ...Object.fromEntries(this.of.vars(name).map(n => [n, 0])) };
|
|
@@ -414,6 +445,9 @@ export class Str {
|
|
|
414
445
|
this.length = length;
|
|
415
446
|
this.bits = bits;
|
|
416
447
|
}
|
|
448
|
+
get patterns() { return [...this.bits.keys()]; }
|
|
449
|
+
// This string where c holds, else `other` (the patterns both declare).
|
|
450
|
+
choose(c, other) { return new Str(when(c, this.id, other.id), when(c, this.length, other.length), new Map([...this.bits].filter(([p]) => other.bits.has(p)).map(([p, b]) => [p, when(c, b, other.bits.get(p))]))); }
|
|
417
451
|
// The string matches `pattern`, one the kind declared.
|
|
418
452
|
like(pattern) {
|
|
419
453
|
const b = this.bits.get(pattern);
|
|
@@ -422,6 +456,9 @@ export class Str {
|
|
|
422
456
|
return b.eq(1);
|
|
423
457
|
}
|
|
424
458
|
get isEmpty() { return this.id.eq(0); }
|
|
459
|
+
// A declared pattern's bit (0 or 1).
|
|
460
|
+
bit(pattern) { const b = this.bits.get(pattern); if (!b)
|
|
461
|
+
throw new Error(`no pattern "${pattern}" on this string`); return b; }
|
|
425
462
|
eq(b) {
|
|
426
463
|
if (typeof b === 'string')
|
|
427
464
|
return all(this.id.eq(literal(b)), this.length.eq(b.length), ...[...this.bits].map(([p, bit]) => bit.eq(globMatch(p, b) ? 1 : 0)));
|
|
@@ -443,7 +480,17 @@ class StrKind extends Kind {
|
|
|
443
480
|
return [v(name).eq(0).iff(v(`${name}$len`).eq(0)), ...(this.maxLength === undefined ? [] : [v(`${name}$len`).atMost(this.maxLength)]),
|
|
444
481
|
...this.patterns.map((p, i) => (p.replaceAll('\\*', '').includes('*') ? v(this.bit(name, i)).atMost(1) : v(this.bit(name, i)).eq(1).iff(v(name).eq(literal(p.replaceAll('\\*', '*'))))))];
|
|
445
482
|
}
|
|
446
|
-
value(name,
|
|
483
|
+
value(name, at = '') { return new Str(v(named(at, name)), v(named(at, `${name}$len`)), new Map(this.patterns.map((p, i) => [p, v(named(at, this.bit(name, i)))]))); }
|
|
484
|
+
store(name, value) {
|
|
485
|
+
if (typeof value === 'string')
|
|
486
|
+
return { [name]: literal(value), [`${name}$len`]: value.length, ...Object.fromEntries(this.patterns.map((p, i) => [this.bit(name, i), globMatch(p, value) ? 1 : 0])) };
|
|
487
|
+
const t = value;
|
|
488
|
+
return { [name]: t.id, [`${name}$len`]: t.length, ...Object.fromEntries(this.patterns.map((p, i) => [this.bit(name, i), t.bit(p)])) };
|
|
489
|
+
}
|
|
490
|
+
decode(name, vars) { const id = vars[name]; for (const [text, n] of internTable)
|
|
491
|
+
if (n === id)
|
|
492
|
+
return text; throw new Error(`${name}: no string is interned as ${id}`); }
|
|
493
|
+
empty() { return ''; }
|
|
447
494
|
encode(name, raw) {
|
|
448
495
|
if (typeof raw !== 'string')
|
|
449
496
|
throw new RangeError(`${name} must be a string, not ${String(raw)}`);
|
|
@@ -466,7 +513,10 @@ export const str = (opts = {}) => new StrKind(opts.like ?? [], opts.maxLength);
|
|
|
466
513
|
class IntKind extends Kind {
|
|
467
514
|
vars(name) { return [name, `${name}$neg`]; }
|
|
468
515
|
assume() { return []; }
|
|
469
|
-
value(name,
|
|
516
|
+
value(name, at = '') { return new Int(v(named(at, name)), v(named(at, `${name}$neg`))); }
|
|
517
|
+
store(name, value) { const i = normal(Int.of(value)); return { [name]: i.pos, [`${name}$neg`]: i.neg }; }
|
|
518
|
+
decode(name, vars) { return vars[name] - vars[`${name}$neg`]; }
|
|
519
|
+
empty() { return 0; }
|
|
470
520
|
encode(name, raw) {
|
|
471
521
|
if (typeof raw !== 'number' || !Number.isSafeInteger(raw))
|
|
472
522
|
throw new RangeError(`${name} must be an integer, not ${String(raw)}`);
|
|
@@ -485,7 +535,13 @@ class DecimalKind extends Kind {
|
|
|
485
535
|
}
|
|
486
536
|
vars(name) { return [name, `${name}$neg`]; }
|
|
487
537
|
assume() { return []; }
|
|
488
|
-
value(name,
|
|
538
|
+
value(name, at = '') { return new Decimal(new Int(v(named(at, name)), v(named(at, `${name}$neg`))), this.places); }
|
|
539
|
+
store(name, value) {
|
|
540
|
+
const i = normal(typeof value === 'string' ? Int.of(scaledOf(value, this.places)) : new Decimal(Int.of(0), this.places).lift(value));
|
|
541
|
+
return { [name]: i.pos, [`${name}$neg`]: i.neg };
|
|
542
|
+
}
|
|
543
|
+
decode(name, vars) { return (vars[name] - vars[`${name}$neg`]) / 10 ** this.places; }
|
|
544
|
+
empty() { return 0; }
|
|
489
545
|
encode(name, raw) {
|
|
490
546
|
if (typeof raw !== 'number' && typeof raw !== 'string')
|
|
491
547
|
throw new RangeError(`${name} must be a decimal, not ${String(raw)}`);
|
|
@@ -836,28 +892,39 @@ export class Rule {
|
|
|
836
892
|
get name() { return this.spec.name; }
|
|
837
893
|
params(kinds) { return this.with({ params: [...this.spec.params, ...Object.entries(kinds)] }); }
|
|
838
894
|
inputs(kinds) { return this.with({ inputs: [...this.spec.inputs, ...Object.entries(kinds)] }); }
|
|
839
|
-
// Each variable starts at a natural or at a term over the params
|
|
895
|
+
// Each variable starts at a natural or at a term over the params, or is declared by a kind and starts where `from`
|
|
896
|
+
// says: `.state({balance: int.from(-5), status: oneOf('open', 'closed').from('open'), note: maybe(nat).from(null)})`.
|
|
840
897
|
state(init) {
|
|
841
|
-
|
|
898
|
+
const entries = [], kinded = [];
|
|
899
|
+
for (const [n, x] of Object.entries(init)) {
|
|
900
|
+
if (typeof x === 'number' || typeof x === 'function') {
|
|
901
|
+
entries.push([n, x]);
|
|
902
|
+
continue;
|
|
903
|
+
}
|
|
904
|
+
const { kind, init: start } = x;
|
|
905
|
+
kinded.push([n, kind]);
|
|
906
|
+
if (typeof start === 'function') {
|
|
907
|
+
const parts = (p) => kind.store(n, start(p));
|
|
908
|
+
for (const part of kind.vars(n))
|
|
909
|
+
entries.push([part, p => parts(p)[part]]);
|
|
910
|
+
}
|
|
911
|
+
else {
|
|
912
|
+
const enc = kind.encode(n, start);
|
|
913
|
+
for (const part of kind.vars(n))
|
|
914
|
+
entries.push([part, enc[part]]);
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
return this.with({ state: [...this.spec.state, ...entries], kinded: [...(this.spec.kinded ?? []), ...kinded] });
|
|
842
918
|
}
|
|
843
919
|
maps(init) { return this.with({ maps: [...this.spec.maps, ...Object.entries(init)] }); }
|
|
844
920
|
// Integers (Int), each starting at an integer or at one over the params; kept as two naturals, <name> and <name>$neg.
|
|
845
|
-
ints(init) {
|
|
921
|
+
ints(init) {
|
|
922
|
+
return this.state(Object.fromEntries(Object.entries(init).map(([n, x]) => [n, int.from(x)])));
|
|
923
|
+
}
|
|
846
924
|
// Decimals with `places` places (Decimal), each starting at a number, a string ("12.50") or one over the params.
|
|
847
925
|
decimals(places, init) {
|
|
848
|
-
decimal(places);
|
|
849
|
-
return this.
|
|
850
|
-
}
|
|
851
|
-
signedState(init, places) {
|
|
852
|
-
const parts = (n, x) => {
|
|
853
|
-
if (typeof x !== 'function') {
|
|
854
|
-
const i = places === undefined ? Int.of(x) : Int.of(scaledOf(x, places));
|
|
855
|
-
return [[n, termValue(i.pos)], [`${n}$neg`, termValue(i.neg)]];
|
|
856
|
-
}
|
|
857
|
-
const at = (p) => normal(places === undefined ? Int.of(x(p)) : new Decimal(Int.of(0), places).lift(x(p)));
|
|
858
|
-
return [[n, p => at(p).pos], [`${n}$neg`, p => at(p).neg]];
|
|
859
|
-
};
|
|
860
|
-
return this.with({ state: [...this.spec.state, ...Object.entries(init).flatMap(([n, x]) => parts(n, x))], signed: [...(this.spec.signed ?? []), ...Object.keys(init).map(n => [n, places])] });
|
|
926
|
+
const kind = decimal(places);
|
|
927
|
+
return this.state(Object.fromEntries(Object.entries(init).map(([n, x]) => [n, kind.from(x)])));
|
|
861
928
|
}
|
|
862
929
|
// Lists, each empty at the start: a map of positions and its length <name>_len (ListRef).
|
|
863
930
|
lists(...names) {
|
|
@@ -917,19 +984,7 @@ export class Rule {
|
|
|
917
984
|
// counter.stepLaw('never goes back', ({used}, after) => used.atMost(after.used))
|
|
918
985
|
stepLaw(name, then, when) {
|
|
919
986
|
const env = this.env();
|
|
920
|
-
const after =
|
|
921
|
-
for (const [n] of this.spec.state)
|
|
922
|
-
after[n] = v(`${n}'`);
|
|
923
|
-
for (const [n] of this.spec.sums)
|
|
924
|
-
after[n] = v(`${n}'`);
|
|
925
|
-
for (const [n, init] of this.spec.maps)
|
|
926
|
-
after[n] = new MapRef(`${n}'`, init);
|
|
927
|
-
for (const n of this.spec.lists ?? [])
|
|
928
|
-
after[n] = new ListRef(`${n}'`, v(`${n}_len'`));
|
|
929
|
-
for (const [n, places] of this.spec.signed ?? []) {
|
|
930
|
-
after[n] = this.signedValue(n, places, `${n}'`, `${n}$neg'`);
|
|
931
|
-
delete after[`${n}$neg`];
|
|
932
|
-
}
|
|
987
|
+
const after = this.afterEnv();
|
|
933
988
|
const out = then(env, after), w = when ? { when: when(env).c } : {};
|
|
934
989
|
const key = `key$${++keyCounter}`, src = { step: { then: then, ...(when ? { when: when } : {}) } };
|
|
935
990
|
if (out instanceof Cond) {
|
|
@@ -945,19 +1000,7 @@ export class Rule {
|
|
|
945
1000
|
// meter.stepsLaw('two requests spend at most 2m', 2, ({spent, m}, after) => after.spent.atMost(spent.plus(m).plus(m)))
|
|
946
1001
|
stepsLaw(name, k, then, when) {
|
|
947
1002
|
const env = this.env();
|
|
948
|
-
const after =
|
|
949
|
-
for (const [n] of this.spec.state)
|
|
950
|
-
after[n] = v(`${n}'`);
|
|
951
|
-
for (const [n] of this.spec.sums)
|
|
952
|
-
after[n] = v(`${n}'`);
|
|
953
|
-
for (const [n, init] of this.spec.maps)
|
|
954
|
-
after[n] = new MapRef(`${n}'`, init);
|
|
955
|
-
for (const n of this.spec.lists ?? [])
|
|
956
|
-
after[n] = new ListRef(`${n}'`, v(`${n}_len'`));
|
|
957
|
-
for (const [n, places] of this.spec.signed ?? []) {
|
|
958
|
-
after[n] = this.signedValue(n, places, `${n}'`, `${n}$neg'`);
|
|
959
|
-
delete after[`${n}$neg`];
|
|
960
|
-
}
|
|
1003
|
+
const after = this.afterEnv();
|
|
961
1004
|
// The j-th request's values over marked names, renamed to the kernel's input@j once built.
|
|
962
1005
|
const mark = (j) => `${j}:`;
|
|
963
1006
|
const requests = Array.from({ length: k }, (_, j) => {
|
|
@@ -1019,10 +1062,23 @@ export class Rule {
|
|
|
1019
1062
|
const is = which === 'params' ? [] : this.spec.inputs.flatMap(([n, k]) => k.assume(n));
|
|
1020
1063
|
return [...ps, ...is].map(c => c.c);
|
|
1021
1064
|
}
|
|
1022
|
-
//
|
|
1023
|
-
|
|
1024
|
-
const
|
|
1025
|
-
|
|
1065
|
+
// The state after a step, as a step law reads it: every state variable, sum, map and list, primed.
|
|
1066
|
+
afterEnv() {
|
|
1067
|
+
const after = {};
|
|
1068
|
+
for (const [n] of this.spec.state)
|
|
1069
|
+
after[n] = v(`${n}'`);
|
|
1070
|
+
for (const [n] of this.spec.sums)
|
|
1071
|
+
after[n] = v(`${n}'`);
|
|
1072
|
+
for (const [n, init] of this.spec.maps)
|
|
1073
|
+
after[n] = new MapRef(`${n}'`, init);
|
|
1074
|
+
for (const n of this.spec.lists ?? [])
|
|
1075
|
+
after[n] = new ListRef(`${n}'`, v(`${n}_len'`));
|
|
1076
|
+
for (const [n, k] of this.spec.kinded ?? []) {
|
|
1077
|
+
for (const part of k.vars(n))
|
|
1078
|
+
delete after[part];
|
|
1079
|
+
after[n] = k.value(n, primed);
|
|
1080
|
+
}
|
|
1081
|
+
return after;
|
|
1026
1082
|
}
|
|
1027
1083
|
// The values handed to the builders' callbacks.
|
|
1028
1084
|
env(parts = {}) {
|
|
@@ -1047,9 +1103,10 @@ export class Rule {
|
|
|
1047
1103
|
out[n] = new ListRef(n, v(`${n}_len`));
|
|
1048
1104
|
for (const [n] of this.spec.sums)
|
|
1049
1105
|
out[n] = v(n);
|
|
1050
|
-
for (const [n,
|
|
1051
|
-
|
|
1052
|
-
|
|
1106
|
+
for (const [n, k] of this.spec.kinded ?? []) {
|
|
1107
|
+
for (const part of k.vars(n))
|
|
1108
|
+
delete out[part];
|
|
1109
|
+
out[n] = k.value(n);
|
|
1053
1110
|
}
|
|
1054
1111
|
}
|
|
1055
1112
|
if (parts.queries !== false)
|
|
@@ -1108,14 +1165,13 @@ export class Rule {
|
|
|
1108
1165
|
flags[n] = out;
|
|
1109
1166
|
}
|
|
1110
1167
|
const updates = { ...s.step?.(env) };
|
|
1111
|
-
// A
|
|
1112
|
-
for (const [n,
|
|
1168
|
+
// A kinded variable's update, as its variables' terms.
|
|
1169
|
+
for (const [n, k] of s.kinded ?? []) {
|
|
1113
1170
|
const u = updates[n];
|
|
1114
1171
|
if (u === undefined)
|
|
1115
1172
|
continue;
|
|
1116
|
-
|
|
1117
|
-
updates
|
|
1118
|
-
updates[`${n}$neg`] = i.neg;
|
|
1173
|
+
delete updates[n];
|
|
1174
|
+
Object.assign(updates, k.store(n, u));
|
|
1119
1175
|
}
|
|
1120
1176
|
const state = {}, maps = {};
|
|
1121
1177
|
for (const [n, u] of Object.entries(updates)) {
|
|
@@ -1253,22 +1309,17 @@ export class Rule {
|
|
|
1253
1309
|
if (sc.flags[n] !== (req[`?${n}`] === 1))
|
|
1254
1310
|
return `flag ${n}: its callback says ${sc.flags[n]}, the step decides ${req[`?${n}`] === 1}`;
|
|
1255
1311
|
}
|
|
1256
|
-
// The updates:
|
|
1312
|
+
// The updates: every state variable as its value is written (a kinded one through its kind's `store`, as the
|
|
1313
|
+
// machine is built) or kept, and each map write.
|
|
1257
1314
|
const updates = inScope(sc, () => s.step?.(this.env()) ?? {});
|
|
1258
|
-
const
|
|
1315
|
+
const written = { ...updates };
|
|
1316
|
+
inScope(sc, () => { for (const [n, k] of s.kinded ?? [])
|
|
1317
|
+
if (updates[n] !== undefined) {
|
|
1318
|
+
delete written[n];
|
|
1319
|
+
Object.assign(written, k.store(n, updates[n]));
|
|
1320
|
+
} });
|
|
1259
1321
|
for (const n of Object.keys(st.scalars)) {
|
|
1260
|
-
|
|
1261
|
-
continue;
|
|
1262
|
-
const u = updates[n], got = signed.has(n) ? next.scalars[n] - next.scalars[`${n}$neg`] : next.scalars[n];
|
|
1263
|
-
let want;
|
|
1264
|
-
if (u === undefined)
|
|
1265
|
-
want = signed.has(n) ? st.scalars[n] - st.scalars[`${n}$neg`] : st.scalars[n];
|
|
1266
|
-
else if (signed.has(n)) {
|
|
1267
|
-
const i = signed.get(n) === undefined ? Int.of(u) : new Decimal(Int.of(0), signed.get(n)).lift(u);
|
|
1268
|
-
want = both(i.pos.val, i.neg.val, (a, b) => a - b);
|
|
1269
|
-
}
|
|
1270
|
-
else
|
|
1271
|
-
want = valOf(u);
|
|
1322
|
+
const u = written[n], got = next.scalars[n], want = u === undefined ? st.scalars[n] : valOf(u);
|
|
1272
1323
|
if (want === undefined)
|
|
1273
1324
|
continue;
|
|
1274
1325
|
count();
|
|
@@ -1304,19 +1355,7 @@ export class Rule {
|
|
|
1304
1355
|
const after = { ...sc, keys, vars: { ...sc.vars, ...Object.fromEntries(Object.entries(next.scalars).map(([n, x]) => [`${n}'`, x])) },
|
|
1305
1356
|
maps: { ...sc.maps, ...Object.fromEntries(Object.entries(next.maps).map(([n, cells]) => [`${n}'`, { cells, dflt: next.defaults?.[n] ?? m.maps?.[n] ?? 0 }])) } };
|
|
1306
1357
|
const ok = inScope(after, () => {
|
|
1307
|
-
const env = this.env(), afterEnv =
|
|
1308
|
-
for (const [n] of s.state)
|
|
1309
|
-
afterEnv[n] = v(`${n}'`);
|
|
1310
|
-
for (const [n] of s.sums)
|
|
1311
|
-
afterEnv[n] = v(`${n}'`);
|
|
1312
|
-
for (const [n, init] of s.maps)
|
|
1313
|
-
afterEnv[n] = new MapRef(`${n}'`, init);
|
|
1314
|
-
for (const n of s.lists ?? [])
|
|
1315
|
-
afterEnv[n] = new ListRef(`${n}'`, v(`${n}_len'`));
|
|
1316
|
-
for (const [n, places] of s.signed ?? []) {
|
|
1317
|
-
afterEnv[n] = this.signedValue(n, places, `${n}'`, `${n}$neg'`);
|
|
1318
|
-
delete afterEnv[`${n}$neg`];
|
|
1319
|
-
}
|
|
1358
|
+
const env = this.env(), afterEnv = this.afterEnv();
|
|
1320
1359
|
const w = src.when ? src.when(env).ok : true;
|
|
1321
1360
|
return w === undefined ? undefined : w ? this.verdict(src.then(env, afterEnv), keys) : true;
|
|
1322
1361
|
});
|
|
@@ -1373,13 +1412,14 @@ export class Rule {
|
|
|
1373
1412
|
out[n] = new Map(Array.from({ length: bound + 1 }, (_, key) => [key, rand(2)]).filter(([, cell]) => cell === 1));
|
|
1374
1413
|
return out;
|
|
1375
1414
|
}
|
|
1376
|
-
// The state's scalars as values:
|
|
1415
|
+
// The state's scalars as runtime values: each kinded variable as its kind decodes it (an integer as one signed
|
|
1416
|
+
// number, a decimal as its number, a choice by name, a string as itself, none as null).
|
|
1377
1417
|
decode(st) {
|
|
1378
1418
|
const out = { ...st.scalars };
|
|
1379
|
-
for (const [n,
|
|
1380
|
-
const
|
|
1381
|
-
|
|
1382
|
-
|
|
1419
|
+
for (const [n, k] of this.spec.kinded ?? []) {
|
|
1420
|
+
for (const part of k.vars(n))
|
|
1421
|
+
delete out[part];
|
|
1422
|
+
out[n] = k.decode(n, st.scalars);
|
|
1383
1423
|
}
|
|
1384
1424
|
return out;
|
|
1385
1425
|
}
|
|
@@ -1392,6 +1432,8 @@ export class Rule {
|
|
|
1392
1432
|
}
|
|
1393
1433
|
const encode = (kinds, values) => Object.assign({}, ...kinds.map(([n, k]) => k.encode(n, values[n])));
|
|
1394
1434
|
export const rule = (name) => new Rule({ name, params: [], inputs: [], state: [], maps: [], flags: [], pre: [], assume: [], sums: [], queries: [] });
|
|
1435
|
+
// The kind a result field is kept in.
|
|
1436
|
+
const kindOf = (t) => (t instanceof Cond ? bool : t instanceof Int ? int : t instanceof Decimal ? decimal(t.places) : t instanceof Choice ? oneOf(...t.values) : t instanceof Str ? str({ like: t.patterns }) : nat);
|
|
1395
1437
|
export class Fun {
|
|
1396
1438
|
r;
|
|
1397
1439
|
out;
|
|
@@ -1411,7 +1453,7 @@ export class Fun {
|
|
|
1411
1453
|
result(env) {
|
|
1412
1454
|
if (!this.out)
|
|
1413
1455
|
throw new Error(`${this.name}: no result (returns)`);
|
|
1414
|
-
return Object.fromEntries(Object.entries(this.out(env)).map(([k, t]) => [k,
|
|
1456
|
+
return Object.fromEntries(Object.entries(this.out(env)).map(([k, t]) => [k, typeof t === 'number' ? lit(t) : t]));
|
|
1415
1457
|
}
|
|
1416
1458
|
// Whenever the arguments (and the result) meet `given`, the result meets `then`.
|
|
1417
1459
|
law(name, given, then) {
|
|
@@ -1442,11 +1484,9 @@ export class Fun {
|
|
|
1442
1484
|
// The one-step rule it compiles to: the result's fields are its state (named out.<field>, apart from the arguments).
|
|
1443
1485
|
get rule() {
|
|
1444
1486
|
const fields = Object.entries(this.result(this.r['env']({ flags: false, state: false, queries: false })));
|
|
1445
|
-
const name = (k) => `out.${k}
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
r = r.decimals(places, of(t => t instanceof Decimal && t.places === places));
|
|
1449
|
-
return r.step(env => Object.fromEntries(Object.entries(this.result(env)).map(([k, t]) => [name(k), t instanceof Cond ? count(t) : t])));
|
|
1487
|
+
const name = (k) => `out.${k}`;
|
|
1488
|
+
return this.r.state(Object.fromEntries(fields.map(([k, t]) => { const kind = kindOf(t); return [name(k), kind.from(kind.empty())]; })))
|
|
1489
|
+
.step(env => Object.fromEntries(Object.entries(this.result(env)).map(([k, t]) => [name(k), t])));
|
|
1450
1490
|
}
|
|
1451
1491
|
compiledRule;
|
|
1452
1492
|
// The result at these arguments (and params), from the compiled rule: numbers, and booleans for the conditions.
|
|
@@ -1455,7 +1495,7 @@ export class Fun {
|
|
|
1455
1495
|
const { rule, gen } = this.compiledRule, ps = rule.encodeParams(params);
|
|
1456
1496
|
const kinds = Object.entries(this.result(this.r['env']({ flags: false, state: false, queries: false })));
|
|
1457
1497
|
const st = rule.decode(gen.step(ps, gen.init(ps), rule.encodeInput(values)));
|
|
1458
|
-
return Object.fromEntries(kinds.map(([k
|
|
1498
|
+
return Object.fromEntries(kinds.map(([k]) => [k, st[`out.${k}`]]));
|
|
1459
1499
|
}
|
|
1460
1500
|
}
|
|
1461
1501
|
export const fun = (name) => new Fun(rule(name));
|
package/dist/kernel/kernel.d.ts
CHANGED
|
@@ -20,6 +20,10 @@ export type Path = Map<string, {
|
|
|
20
20
|
export declare const canon: (lin: Lin) => string;
|
|
21
21
|
export declare const atomOf: (lin: Lin, holds: boolean) => Constraint;
|
|
22
22
|
export declare function linear(e: E, path: Path): Lin | undefined;
|
|
23
|
+
export declare const termColumn: (e: E) => string;
|
|
24
|
+
export declare const columnTerm: (name: string) => E | undefined;
|
|
25
|
+
export declare function decided(e: E, path: Path): boolean;
|
|
26
|
+
export declare function termRows(name: string, path: Path): Lin[];
|
|
23
27
|
export declare function truth(c: C, path: Path): boolean | undefined;
|
|
24
28
|
export declare function facts(c: C, path: Path, positive?: boolean): Lin[];
|
|
25
29
|
export declare function leafConstraints(goal: Goal, path: Path): Constraint[];
|
|
@@ -33,6 +37,7 @@ export type Subst = {
|
|
|
33
37
|
export declare function substE(e: E, s: Subst): E;
|
|
34
38
|
export declare function substC(c: C, s: Subst): C;
|
|
35
39
|
export type Instances = Record<string, E[]>;
|
|
40
|
+
export declare const stateName: (n: string, j: number) => string;
|
|
36
41
|
export declare const assumed: (laws: Law[], instances: Instances) => C[];
|
|
37
42
|
export declare function scope(m: Machine, laws?: Law[]): void;
|
|
38
43
|
export declare const keyTerm: (e: E, key: string) => boolean;
|
|
@@ -104,6 +109,7 @@ export declare function checkStepLaws(m: Machine, laws: Law[], stepLaws: StepLaw
|
|
|
104
109
|
perStep?: Record<string, Instances>;
|
|
105
110
|
trees: Record<string, Tree>;
|
|
106
111
|
}): boolean;
|
|
112
|
+
export declare const stepZone: (m: Machine, k: number) => string[];
|
|
107
113
|
export type Fold = {
|
|
108
114
|
name: string;
|
|
109
115
|
params: string[];
|