gdp-wired 0.1.0 → 0.3.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 +86 -35
- package/dist/core/compose.d.ts +38 -0
- package/dist/core/compose.js +245 -0
- package/dist/core/dsl.d.ts +85 -13
- package/dist/core/dsl.js +424 -19
- package/dist/core/machine.d.ts +34 -6
- package/dist/core/machine.js +194 -22
- package/dist/kernel/kernel.d.ts +31 -3
- package/dist/kernel/kernel.js +364 -87
- package/dist/kernel/prove.d.ts +33 -9
- package/dist/kernel/prove.js +298 -24
- package/dist/lean-kernel/emit.d.ts +132 -0
- package/dist/lean-kernel/emit.js +936 -0
- package/dist/lean-kernel/lean-terms.d.ts +5 -0
- package/dist/lean-kernel/lean-terms.js +44 -0
- package/dist/lean-kernel/library.d.ts +1 -0
- package/dist/lean-kernel/library.js +13 -0
- package/package.json +15 -4
package/dist/kernel/kernel.js
CHANGED
|
@@ -106,6 +106,7 @@ function comparison(c, path) {
|
|
|
106
106
|
export function truth(c, path) {
|
|
107
107
|
switch (c.op) {
|
|
108
108
|
case 'flag': throw new Error('flags are inlined before the kernel sees them');
|
|
109
|
+
case 'all': return undefined; // closed before a goal is checked; opaque here, so it gives a leaf nothing
|
|
109
110
|
case 'not': {
|
|
110
111
|
const t = truth(c.x, path);
|
|
111
112
|
return t === undefined ? undefined : !t;
|
|
@@ -131,6 +132,7 @@ export function truth(c, path) {
|
|
|
131
132
|
export function facts(c, path, positive = true) {
|
|
132
133
|
switch (c.op) {
|
|
133
134
|
case 'flag': throw new Error('flags are inlined before the kernel sees them');
|
|
135
|
+
case 'all': return [];
|
|
134
136
|
case 'not': return facts(c.x, path, !positive);
|
|
135
137
|
case 'and':
|
|
136
138
|
case 'or': {
|
|
@@ -234,8 +236,8 @@ function natural(e) {
|
|
|
234
236
|
default: return unreachable(e);
|
|
235
237
|
}
|
|
236
238
|
}
|
|
237
|
-
const naturalC = (c) => c.op === 'flag' ? true : c.op === 'not' ? naturalC(c.x) : c.op === 'and' || c.op === 'or' ? c.xs.every(naturalC) : natural(c.a) && natural(c.b);
|
|
238
|
-
function substE(e, s) {
|
|
239
|
+
const naturalC = (c) => c.op === 'flag' ? true : c.op === 'all' ? naturalC(c.c) : c.op === 'not' ? naturalC(c.x) : c.op === 'and' || c.op === 'or' ? c.xs.every(naturalC) : natural(c.a) && natural(c.b);
|
|
240
|
+
export function substE(e, s) {
|
|
239
241
|
switch (e.op) {
|
|
240
242
|
case 'var': return s.vars[e.name] ?? e;
|
|
241
243
|
case 'const': return e;
|
|
@@ -262,6 +264,10 @@ export function substC(c, s) {
|
|
|
262
264
|
}
|
|
263
265
|
if (c.op === 'not')
|
|
264
266
|
return { op: 'not', x: substC(c.x, s) };
|
|
267
|
+
if (c.op === 'all') {
|
|
268
|
+
const { [c.key]: _bound, ...vars } = s.vars;
|
|
269
|
+
return { op: 'all', key: c.key, c: substC(c.c, { ...s, vars }) };
|
|
270
|
+
}
|
|
265
271
|
if (c.op === 'and' || c.op === 'or')
|
|
266
272
|
return { op: c.op, xs: c.xs.map(y => substC(y, s)) };
|
|
267
273
|
return { ...c, a: substE(c.a, s), b: substE(c.b, s) };
|
|
@@ -274,6 +280,83 @@ const preOf = (o) => {
|
|
|
274
280
|
throw new Error('a precondition reads only the params');
|
|
275
281
|
return o.pre ? [o.pre] : [];
|
|
276
282
|
};
|
|
283
|
+
const identity = { vars: {}, maps: {}, flags: {} };
|
|
284
|
+
const from = (at = identity) => ({ hyps: [], at });
|
|
285
|
+
// Conditions that hold where the run is.
|
|
286
|
+
const holds = (r, cs) => ({ hyps: [...r.hyps, ...cs.map(c => substC(c, r.at))], at: r.at });
|
|
287
|
+
// Names read where the run is: inputs named for this step (a request's terms, or fresh names for a later step), and the
|
|
288
|
+
// maps a request carries, renamed for a later step.
|
|
289
|
+
const reading = (r, vars, maps = {}) => ({ hyps: r.hyps, at: { ...r.at, vars: { ...r.at.vars, ...vars },
|
|
290
|
+
maps: { ...r.at.maps, ...Object.fromEntries(Object.entries(maps).map(([map, name]) => [map, (key) => ({ op: 'get', map: name, key })])) } } });
|
|
291
|
+
// One step: each name the update sets is its term read where the run is; a map read after it is the written value at
|
|
292
|
+
// the written key, and what the map held before elsewhere (or, written at every key, its value at the key read).
|
|
293
|
+
const step = (r, u) => {
|
|
294
|
+
const at = r.at, before = (map, key) => (at.maps[map] ? at.maps[map](key) : { op: 'get', map, key });
|
|
295
|
+
return { hyps: r.hyps, at: {
|
|
296
|
+
vars: { ...at.vars, ...Object.fromEntries(Object.entries(u.vars).map(([n, e]) => [n, substE(e, at)])) },
|
|
297
|
+
maps: { ...at.maps, ...Object.fromEntries(Object.entries(u.maps).map(([map, w]) => [map, 'each' in w
|
|
298
|
+
? (key) => substE(w.value, { ...at, vars: { ...at.vars, [w.each]: key } })
|
|
299
|
+
: (key) => ({ op: 'if', c: { op: '=', a: key, b: substE(w.key, at) }, then: substE(w.value, at), else: before(map, key) })])) },
|
|
300
|
+
flags: at.flags,
|
|
301
|
+
} };
|
|
302
|
+
};
|
|
303
|
+
// The conclusion read at the run's end: plain names at the last frame.
|
|
304
|
+
const atEnd = (r, c) => substC(c, r.at);
|
|
305
|
+
// The conclusion read across the run: plain names at the first frame, primed names (s', and M'[k] as a read of the map
|
|
306
|
+
// named M') at the last; the first step's inputs as it read them, and its flags, decided where it was taken.
|
|
307
|
+
const across = (r, c, state, maps, first) => substC(c, {
|
|
308
|
+
vars: { ...first.inputs, ...Object.fromEntries(state.map(n => [`${n}'`, r.at.vars[n] ?? { op: 'var', name: n }])) },
|
|
309
|
+
maps: Object.fromEntries(maps.map(map => [`${map}'`, (key) => (r.at.maps[map] ? r.at.maps[map](key) : { op: 'get', map, key })])),
|
|
310
|
+
flags: first.flags,
|
|
311
|
+
});
|
|
312
|
+
const goal = (name, r, conclusion) => ({ name, hyps: [...r.hyps, { op: 'not', x: conclusion }] });
|
|
313
|
+
// A machine's flags as its terms read them (decided before the step, over the frame it starts from): a plain flag its
|
|
314
|
+
// condition (through the flags before it), a quantified flag (one whose condition holds at every key) its 0/1 variable
|
|
315
|
+
// ?f, which `closing` ties to the condition.
|
|
316
|
+
const flagsOf = (m) => {
|
|
317
|
+
const out = {};
|
|
318
|
+
for (const [f, c] of Object.entries(m.step.flags ?? {}))
|
|
319
|
+
out[f] = c.op === 'all' ? { op: '=', a: { op: 'var', name: `?${f}` }, b: { op: 'const', value: 1 } } : substC(c, { vars: {}, maps: {}, flags: out });
|
|
320
|
+
return out;
|
|
321
|
+
};
|
|
322
|
+
const quantified = (m) => Object.entries(m.step.flags ?? {}).filter((e) => e[1].op === 'all');
|
|
323
|
+
// A machine's step as an update, its flags inlined.
|
|
324
|
+
const updateOf = (m) => {
|
|
325
|
+
const flags = { vars: {}, maps: {}, flags: flagsOf(m) };
|
|
326
|
+
return {
|
|
327
|
+
vars: Object.fromEntries(Object.entries(m.step.state ?? {}).map(([n, e]) => [n, substE(e, flags)])),
|
|
328
|
+
maps: Object.fromEntries(Object.entries(m.step.maps ?? {}).map(([map, w]) => [map, 'each' in w ? { each: w.each, value: substE(w.value, flags) } : { key: substE(w.key, flags), value: substE(w.value, flags) }])),
|
|
329
|
+
};
|
|
330
|
+
};
|
|
331
|
+
const inlineFlags = (m, c) => substC(c, { vars: {}, maps: {}, flags: flagsOf(m) });
|
|
332
|
+
// A quantified flag ?f, closed: it is 0 or 1; where it is 1, its condition holds at each key the proof names (instances
|
|
333
|
+
// under ?f); where it is 0, the condition fails at a fresh witness w?f. A real step decides ?f by the condition, and if
|
|
334
|
+
// that fails some key fails it, so these hold of every real step: a goal that refutes them refutes the machine.
|
|
335
|
+
const closing = (m, instances) => quantified(m).flatMap(([f, q]) => {
|
|
336
|
+
const v = { op: 'var', name: `?${f}` }, zero = { op: 'const', value: 0 }, one = { op: 'const', value: 1 };
|
|
337
|
+
const body = (key) => substC(q.c, { vars: { [q.key]: key }, maps: {}, flags: flagsOf(m) });
|
|
338
|
+
return [{ op: '<=', a: v, b: one }, ...(instances[`?${f}`] ?? []).map((key) => ({ op: 'or', xs: [{ op: '=', a: v, b: zero }, body(key)] })),
|
|
339
|
+
{ op: 'or', xs: [{ op: '=', a: v, b: one }, { op: 'not', x: body({ op: 'var', name: `w?${f}` }) }] }];
|
|
340
|
+
});
|
|
341
|
+
// The names a later step (j >= 2) reads its request by: inputs, quantified flags and their witnesses, and carried maps.
|
|
342
|
+
const laterNames = (m, j) => ({
|
|
343
|
+
vars: Object.fromEntries([...m.inputs, ...quantified(m).flatMap(([f]) => [`?${f}`, `w?${f}`])].map(name => [name, { op: 'var', name: `${name}@${j}` }])),
|
|
344
|
+
maps: Object.fromEntries((m.inputMaps ?? []).map(map => [map, `${map}@${j}`])),
|
|
345
|
+
});
|
|
346
|
+
// The laws assumed where the run is: each plain law, and each every-key law at the keys the proof names.
|
|
347
|
+
const assumed = (laws, instances) => laws.flatMap(l => {
|
|
348
|
+
const keys = instances[l.name] ?? [];
|
|
349
|
+
if (l.keys?.length) {
|
|
350
|
+
const names = [l.forall, ...l.keys], n = names.length;
|
|
351
|
+
return Array.from({ length: Math.floor(keys.length / n) }, (_, t) => substC(l.holds, { vars: Object.fromEntries(names.map((name, i) => [name, keys[t * n + i]])), maps: {}, flags: {} }));
|
|
352
|
+
}
|
|
353
|
+
return l.forall ? keys.map(key => substC(l.holds, { vars: { [l.forall]: key }, maps: {}, flags: {} })) : [l.holds];
|
|
354
|
+
});
|
|
355
|
+
// The facts about the functions, at the keys the proof names: as they are (they read the params and functions alone, so
|
|
356
|
+
// any frame reads them alike).
|
|
357
|
+
const given = (m, r, instances) => ({ hyps: [...r.hyps, ...assumed(m.facts ?? [], instances)], at: r.at });
|
|
358
|
+
// The start: the state's start values over the params, every map cell its constant.
|
|
359
|
+
const startOf = (m) => ({ vars: { ...m.state }, maps: Object.fromEntries(Object.entries(m.maps ?? {}).map(([map, c]) => [map, () => ({ op: 'const', value: c })])), flags: {} });
|
|
277
360
|
function reads(t, out = { vars: new Set(), maps: new Set(), flags: new Set() }) {
|
|
278
361
|
switch (t.op) {
|
|
279
362
|
case 'var':
|
|
@@ -287,6 +370,14 @@ function reads(t, out = { vars: new Set(), maps: new Set(), flags: new Set() })
|
|
|
287
370
|
case 'flag':
|
|
288
371
|
out.flags.add(t.name);
|
|
289
372
|
break;
|
|
373
|
+
case 'all': {
|
|
374
|
+
const inner = reads(t.c);
|
|
375
|
+
inner.vars.delete(t.key);
|
|
376
|
+
for (const k of ['vars', 'maps', 'flags'])
|
|
377
|
+
for (const name of inner[k])
|
|
378
|
+
out[k].add(name);
|
|
379
|
+
break;
|
|
380
|
+
}
|
|
290
381
|
case 'not':
|
|
291
382
|
reads(t.x, out);
|
|
292
383
|
break;
|
|
@@ -345,10 +436,10 @@ const distinct = (what, names) => { const seen = new Set(); for (const name of n
|
|
|
345
436
|
// step reads params, inputs and the state before it; a flag reads the flags declared before it; a start value reads the
|
|
346
437
|
// params; a law reads the params, the state, the maps and its own key.
|
|
347
438
|
export function scope(m, laws = []) {
|
|
348
|
-
const state = Object.keys(m.state), maps = Object.keys(m.maps ?? {}), flags = Object.keys(m.step.flags ?? {});
|
|
439
|
+
const state = Object.keys(m.state), maps = Object.keys(m.maps ?? {}), flags = Object.keys(m.step.flags ?? {}), carried = m.inputMaps ?? [], fns = Object.keys(m.fns ?? {});
|
|
349
440
|
const names = [...m.params, ...m.inputs, ...state];
|
|
350
|
-
distinct(m.name, [...names, ...maps]);
|
|
351
|
-
for (const name of [...names, ...maps, ...flags])
|
|
441
|
+
distinct(m.name, [...names, ...maps, ...carried, ...fns]);
|
|
442
|
+
for (const name of [...names, ...maps, ...carried, ...fns, ...flags])
|
|
352
443
|
plainName(m.name, name);
|
|
353
444
|
for (const name of Object.keys(m.step.state ?? {}))
|
|
354
445
|
if (!state.includes(name))
|
|
@@ -356,19 +447,168 @@ export function scope(m, laws = []) {
|
|
|
356
447
|
for (const map of Object.keys(m.step.maps ?? {}))
|
|
357
448
|
if (!maps.includes(map))
|
|
358
449
|
throw new Error(`${m.name}'s step writes the map ${map}, which it doesn't declare`);
|
|
450
|
+
// The step reads the maps a request carries too. A key bound by a quantified flag or a write at every key is new and
|
|
451
|
+
// read only as a map's key (so every key the maps hold nothing at reads alike); a quantifier is a whole flag, nowhere
|
|
452
|
+
// else.
|
|
453
|
+
const readable = [...maps, ...carried, ...fns];
|
|
454
|
+
const bound = (what, key, t) => {
|
|
455
|
+
plainName(what, key);
|
|
456
|
+
if ([...names, ...readable, ...flags].includes(key))
|
|
457
|
+
throw new Error(`${what}: the key ${key} is already a name of ${m.name}`);
|
|
458
|
+
keyOnly(what, t, key, fns);
|
|
459
|
+
};
|
|
359
460
|
inScope(`${m.name}'s start`, Object.values(m.state), m.params);
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
461
|
+
flags.forEach((f, i) => {
|
|
462
|
+
const c = m.step.flags[f], what = `${m.name}'s flag ${f}`;
|
|
463
|
+
// A quantified flag's body reads no flag (the twin decides quantified flags before the plain ones).
|
|
464
|
+
if (c.op === 'all') {
|
|
465
|
+
bound(what, c.key, c.c);
|
|
466
|
+
unquantified(what, c.c);
|
|
467
|
+
inScope(what, [c.c], [...names, c.key], readable, []);
|
|
468
|
+
}
|
|
469
|
+
else {
|
|
470
|
+
unquantified(what, c);
|
|
471
|
+
inScope(what, [c], names, readable, flags.slice(0, i));
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
for (const [map, w] of Object.entries(m.step.maps ?? {})) {
|
|
475
|
+
const what = `${m.name}'s write to ${map}`;
|
|
476
|
+
if ('each' in w) {
|
|
477
|
+
bound(what, w.each, w.value);
|
|
478
|
+
unquantified(what, w.value);
|
|
479
|
+
inScope(what, [w.value], [...names, w.each], readable, flags);
|
|
480
|
+
}
|
|
481
|
+
else {
|
|
482
|
+
unquantified(what, w.key);
|
|
483
|
+
unquantified(what, w.value);
|
|
484
|
+
inScope(what, [w.key, w.value], names, readable, flags);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
for (const e of Object.values(m.step.state ?? {}))
|
|
488
|
+
unquantified(`${m.name}'s step`, e);
|
|
489
|
+
inScope(`${m.name}'s step`, Object.values(m.step.state ?? {}), names, readable, flags);
|
|
490
|
+
if (m.assume) {
|
|
491
|
+
unquantified(`${m.name}'s assumption`, m.assume);
|
|
492
|
+
inScope(`${m.name}'s assumption`, [m.assume], names, readable, flags);
|
|
493
|
+
}
|
|
364
494
|
if (m.pre)
|
|
365
495
|
inScope(`${m.name}'s precondition`, [m.pre], m.params);
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
496
|
+
const facts = m.facts ?? [];
|
|
497
|
+
distinct(`${m.name}'s laws and facts`, [...laws, ...facts].map(l => l.name));
|
|
498
|
+
for (const law of [...laws, ...facts]) {
|
|
499
|
+
const what = facts.includes(law) ? `fact ${law.name}` : `law ${law.name}`;
|
|
500
|
+
for (const key of [law.forall, ...(law.keys ?? [])])
|
|
501
|
+
if (key && [...names, ...maps, ...carried, ...fns].includes(key))
|
|
502
|
+
throw new Error(`${what}'s key ${key} is already a name of ${m.name}`);
|
|
503
|
+
if (law.keys?.length && (!law.forall || new Set([law.forall, ...law.keys]).size !== law.keys.length + 1))
|
|
504
|
+
throw new Error(`${what}: further keys come with a first, each a different name`);
|
|
505
|
+
if (law.name.startsWith('?'))
|
|
506
|
+
throw new Error(`${what}: a name starting with ? is a flag's (its instances)`);
|
|
507
|
+
unquantified(what, law.holds);
|
|
371
508
|
}
|
|
509
|
+
const keysOf = (l) => [...(l.forall ? [l.forall] : []), ...(l.keys ?? [])];
|
|
510
|
+
for (const law of laws)
|
|
511
|
+
inScope(`law ${law.name}`, [law.holds], [...m.params, ...state, ...keysOf(law)], [...maps, ...fns]);
|
|
512
|
+
// A fact reads the params and the functions alone: what holds of them holds at every state.
|
|
513
|
+
for (const fact of facts)
|
|
514
|
+
inScope(`fact ${fact.name}`, [fact.holds], [...m.params, ...keysOf(fact)], fns);
|
|
515
|
+
}
|
|
516
|
+
// A key bound in t is read in key terms only: the key, or the key plus or less a term that doesn't read it (k + e, e + k,
|
|
517
|
+
// k ∸ e), each a map's key (no function's: a function's values don't stop where a map's cells do) or a side of a
|
|
518
|
+
// comparison whose other side doesn't read the key. Then t is constant between finitely many keys (the runtimes'
|
|
519
|
+
// breakpoints: each map's cells and each compared value, shifted by the offsets), so a runtime decides it on those.
|
|
520
|
+
const readsKey = (t, key) => JSON.stringify(t).includes(`"op":"var","name":${JSON.stringify(key)}`);
|
|
521
|
+
export const keyTerm = (e, key) => e.op === 'var' ? e.name === key
|
|
522
|
+
: e.op === 'add' ? (e.a.op === 'var' && e.a.name === key && !readsKey(e.b, key)) || (e.b.op === 'var' && e.b.name === key && !readsKey(e.a, key))
|
|
523
|
+
: e.op === 'sub' && e.a.op === 'var' && e.a.name === key && !readsKey(e.b, key);
|
|
524
|
+
function keyOnly(what, t, key, fns = []) {
|
|
525
|
+
const bad = () => { throw new Error(`${what}: the key ${key} is read only in a map's key or a comparison, alone or plus or less a term that doesn't read it`); };
|
|
526
|
+
const walk = (u) => {
|
|
527
|
+
switch (u.op) {
|
|
528
|
+
case 'var':
|
|
529
|
+
if (u.name === key)
|
|
530
|
+
bad();
|
|
531
|
+
break;
|
|
532
|
+
case 'get':
|
|
533
|
+
if (!(keyTerm(u.key, key) && !fns.includes(u.map)))
|
|
534
|
+
walk(u.key);
|
|
535
|
+
break;
|
|
536
|
+
case 'const':
|
|
537
|
+
case 'flag': break;
|
|
538
|
+
case 'not':
|
|
539
|
+
walk(u.x);
|
|
540
|
+
break;
|
|
541
|
+
case 'all':
|
|
542
|
+
walk(u.c);
|
|
543
|
+
break;
|
|
544
|
+
case 'and':
|
|
545
|
+
case 'or':
|
|
546
|
+
u.xs.forEach(walk);
|
|
547
|
+
break;
|
|
548
|
+
case 'mul':
|
|
549
|
+
walk(u.a);
|
|
550
|
+
break;
|
|
551
|
+
case 'if':
|
|
552
|
+
walk(u.c);
|
|
553
|
+
walk(u.then);
|
|
554
|
+
walk(u.else);
|
|
555
|
+
break;
|
|
556
|
+
case '<':
|
|
557
|
+
case '<=':
|
|
558
|
+
case '=':
|
|
559
|
+
if (keyTerm(u.a, key) && !readsKey(u.b, key))
|
|
560
|
+
walk(u.b);
|
|
561
|
+
else if (keyTerm(u.b, key) && !readsKey(u.a, key))
|
|
562
|
+
walk(u.a);
|
|
563
|
+
else {
|
|
564
|
+
walk(u.a);
|
|
565
|
+
walk(u.b);
|
|
566
|
+
}
|
|
567
|
+
break;
|
|
568
|
+
case 'add':
|
|
569
|
+
case 'sub':
|
|
570
|
+
case 'min':
|
|
571
|
+
case 'max':
|
|
572
|
+
case 'prod':
|
|
573
|
+
walk(u.a);
|
|
574
|
+
walk(u.b);
|
|
575
|
+
break;
|
|
576
|
+
default: unreachable(u);
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
walk(t);
|
|
580
|
+
}
|
|
581
|
+
// Whether the key is read only as a map's key, plainly (what the GPU searches decide: every cell, and one past them).
|
|
582
|
+
export const plainKey = (t, key) => {
|
|
583
|
+
const walk = (u) => {
|
|
584
|
+
switch (u.op) {
|
|
585
|
+
case 'var': return u.name !== key;
|
|
586
|
+
case 'get': return (u.key.op === 'var' && u.key.name === key) || walk(u.key);
|
|
587
|
+
case 'const':
|
|
588
|
+
case 'flag': return true;
|
|
589
|
+
case 'not': return walk(u.x);
|
|
590
|
+
case 'all': return walk(u.c);
|
|
591
|
+
case 'and':
|
|
592
|
+
case 'or': return u.xs.every(walk);
|
|
593
|
+
case 'mul': return walk(u.a);
|
|
594
|
+
case 'if': return walk(u.c) && walk(u.then) && walk(u.else);
|
|
595
|
+
case 'add':
|
|
596
|
+
case 'sub':
|
|
597
|
+
case 'min':
|
|
598
|
+
case 'max':
|
|
599
|
+
case 'prod':
|
|
600
|
+
case '<':
|
|
601
|
+
case '<=':
|
|
602
|
+
case '=': return walk(u.a) && walk(u.b);
|
|
603
|
+
default: return unreachable(u);
|
|
604
|
+
}
|
|
605
|
+
};
|
|
606
|
+
return walk(t);
|
|
607
|
+
};
|
|
608
|
+
// No quantifier inside t: a quantifier is a whole flag.
|
|
609
|
+
function unquantified(what, t) {
|
|
610
|
+
if (JSON.stringify(t).includes('"op":"all"'))
|
|
611
|
+
throw new Error(`${what}: a condition at every key is a flag of its own (flags: {name: all(...)})`);
|
|
372
612
|
}
|
|
373
613
|
// --- histories ---------------------------------------------------------------------------------------------------------
|
|
374
614
|
// A term or condition that reads only these names, and no map.
|
|
@@ -383,6 +623,8 @@ export function historyMachine(m) {
|
|
|
383
623
|
const h = m.history;
|
|
384
624
|
if (!h)
|
|
385
625
|
return { machine: m, goals: [] };
|
|
626
|
+
if (m.inputMaps?.length || quantified(m).length || Object.values(m.step.maps ?? {}).some(w => 'each' in w))
|
|
627
|
+
throw new Error(`${m.name}: a machine with a history has no maps carried by requests, quantified flags or writes at every key`);
|
|
386
628
|
const queries = Object.keys(h.queries), sums = Object.keys(h.sums), plainInputs = m.inputs.filter(i => !queries.includes(i));
|
|
387
629
|
const fields = h.record.map(f => `r.${f}`);
|
|
388
630
|
const taken = [...m.params, ...m.inputs, ...Object.keys(m.state), ...Object.keys(m.step.state ?? {})];
|
|
@@ -425,32 +667,22 @@ export function obligations(m, laws, instances = {}) {
|
|
|
425
667
|
return [...obligations(machine, laws, instances), ...goals];
|
|
426
668
|
}
|
|
427
669
|
scope(m, laws);
|
|
428
|
-
const ok = [...Object.values(m.state), ...Object.values(m.step.state ?? {}), ...Object.values(m.step.maps ?? {}).flatMap(w => [w.key, w.value])].every(natural)
|
|
670
|
+
const ok = [...Object.values(m.state), ...Object.values(m.step.state ?? {}), ...Object.values(m.step.maps ?? {}).flatMap(w => ('each' in w ? [w.value] : [w.key, w.value]))].every(natural)
|
|
429
671
|
&& Object.values(m.step.flags ?? {}).every(naturalC) && laws.every(l => naturalC(l.holds)) && Object.values(m.maps ?? {}).every(c => Number.isInteger(c) && c >= 0);
|
|
430
|
-
if (!ok || laws.some(l => mentions(l.holds, m.inputs)))
|
|
672
|
+
if (!ok || !(m.facts ?? []).every(l => naturalC(l.holds)) || laws.some(l => mentions(l.holds, m.inputs)))
|
|
431
673
|
throw new Error('a negative constant or factor, or a law about a request, is outside the kernel');
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
const
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
flags: {},
|
|
445
|
-
maps: Object.fromEntries(Object.entries(m.step.maps ?? {}).map(([map, w]) => [map, (key) => ({ op: 'if', c: { op: '=', a: key, b: inlineE(w.key) }, then: inlineE(w.value), else: { op: 'get', map, key } })])),
|
|
446
|
-
};
|
|
447
|
-
const out = [];
|
|
448
|
-
for (const law of laws) {
|
|
449
|
-
out.push({ name: `${law.name} @start`, hyps: [...pre, { op: 'not', x: substC(goal(law), start) }] });
|
|
450
|
-
const before = laws.flatMap(l => (l.forall ? (instances[law.name]?.[l.name] ?? []).map(key => atKey(l, key)) : [l.holds]));
|
|
451
|
-
out.push({ name: `${law.name} @step`, hyps: [...pre, ...(m.assume ? [inline(m.assume)] : []), ...before, { op: 'not', x: substC(goal(law), step) }] });
|
|
452
|
-
}
|
|
453
|
-
return out;
|
|
674
|
+
// A law that holds at every key is proven at a fresh one.
|
|
675
|
+
// A law about every two keys keeps its second key as it is: a fresh name nothing else reads, so the goal is about every
|
|
676
|
+
// value of it too (the Lean twin reads it so).
|
|
677
|
+
const proven = (law) => (law.forall ? substC(law.holds, { vars: { [law.forall]: { op: 'var', name: `${law.forall}#` } }, maps: {}, flags: {} }) : law.holds);
|
|
678
|
+
const pre = preOf(m), u = updateOf(m), assume = m.assume ? [inlineFlags(m, m.assume)] : [];
|
|
679
|
+
return laws.flatMap(law => {
|
|
680
|
+
// At the start: the precondition, and no request. One step: from a state where the laws hold, a request allowed.
|
|
681
|
+
// The facts hold throughout, read where the run begins (they read no state).
|
|
682
|
+
const start = given(m, holds(from(startOf(m)), pre), instances[law.name] ?? {});
|
|
683
|
+
const turn = step(holds(given(m, from(), instances[law.name] ?? {}), [...pre, ...assume, ...assumed(laws, instances[law.name] ?? {}), ...closing(m, instances[law.name] ?? {})]), u);
|
|
684
|
+
return [goal(`${law.name} @start`, start, atEnd(start, proven(law))), goal(`${law.name} @step`, turn, atEnd(turn, proven(law)))];
|
|
685
|
+
});
|
|
454
686
|
}
|
|
455
687
|
// The whole set at once: every goal has a tree, and every tree checks.
|
|
456
688
|
export function checkLaws(m, laws, proof) {
|
|
@@ -474,17 +706,14 @@ export function loopObligations(l, laws, rank) {
|
|
|
474
706
|
inScope(`${l.name}'s rank and laws`, [...ranks, ...laws.map(law => law.holds)], [...l.params, ...Object.keys(l.state)]);
|
|
475
707
|
if (ranks.some(r => mentions({ op: '=', a: r, b: r }, l.inputs)))
|
|
476
708
|
throw new Error('a rank reads the state and params, not the turn\'s input');
|
|
477
|
-
if (!parts.every(natural) || !naturalC(l.while) || !laws.every(law => !law.forall && naturalC(law.holds) && !mentions(law.holds, l.inputs)))
|
|
709
|
+
if (!parts.every(natural) || !naturalC(l.while) || !laws.every(law => !law.forall && !law.keys?.length && naturalC(law.holds) && !mentions(law.holds, l.inputs)))
|
|
478
710
|
throw new Error('outside the kernel');
|
|
479
|
-
const start = { vars: l.state, maps: {}, flags: {} }, turn = { vars: l.body, maps: {}, flags: {} };
|
|
480
711
|
const pre = preOf(l);
|
|
481
|
-
const
|
|
712
|
+
const start = holds(from({ vars: { ...l.state }, maps: {}, flags: {} }), pre);
|
|
713
|
+
const turn = step(holds(from(), [...pre, ...(l.assume ? [l.assume] : []), l.while, ...laws.map(law => law.holds)]), { vars: l.body, maps: {} });
|
|
482
714
|
return [
|
|
483
|
-
...laws.flatMap(law => [
|
|
484
|
-
|
|
485
|
-
{ name: `${law.name} @turn`, hyps: [...during, { op: 'not', x: substC(law.holds, turn) }] },
|
|
486
|
-
]),
|
|
487
|
-
{ name: 'rank drops', hyps: [...during, { op: 'not', x: drops(ranks, turn) }] },
|
|
715
|
+
...laws.flatMap(law => [goal(`${law.name} @start`, start, atEnd(start, law.holds)), goal(`${law.name} @turn`, turn, atEnd(turn, law.holds))]),
|
|
716
|
+
goal('rank drops', turn, drops(ranks, turn.at)),
|
|
488
717
|
];
|
|
489
718
|
}
|
|
490
719
|
const drops = (ranks, turn) => {
|
|
@@ -504,57 +733,105 @@ export function progressObligations(m, laws, instances, p) {
|
|
|
504
733
|
if (mentions({ op: '=', a: e, b: e }, m.inputs))
|
|
505
734
|
throw new Error('a request is named from the state and params');
|
|
506
735
|
const req = { vars: p.request, maps: {}, flags: {} };
|
|
507
|
-
const flags = Object.fromEntries(Object.entries(m
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
...Object.fromEntries(Object.entries(m.step.maps ?? {}).map(([map, w]) => [`${map}'`, (key) => ({ op: 'if', c: { op: '=', a: key, b: now(w.key) }, then: now(w.value), else: { op: 'get', map, key } })])),
|
|
514
|
-
},
|
|
515
|
-
flags,
|
|
516
|
-
};
|
|
517
|
-
const before = [...preOf(m), ...laws.flatMap(l => (l.forall ? (instances[l.name] ?? []).map(key => substC(l.holds, { vars: { [l.forall]: key }, maps: {}, flags: {} })) : [l.holds])), p.when];
|
|
736
|
+
const flags = Object.fromEntries(Object.entries(flagsOf(m)).map(([f, c]) => [f, substC(c, req)]));
|
|
737
|
+
// The run: a state where the laws and `when` hold, the named request (its quantified flags closed), then the step it
|
|
738
|
+
// causes. Maps a request carries are left as any maps: the step is shown for all of them.
|
|
739
|
+
const before = holds(given(m, from(), instances), [...preOf(m), ...assumed(laws, instances), p.when]);
|
|
740
|
+
const asked = holds(reading(before, p.request), closing(m, instances));
|
|
741
|
+
const caused = step(asked, updateOf(m));
|
|
518
742
|
return [
|
|
519
|
-
...(m.assume ? [
|
|
520
|
-
|
|
743
|
+
...(m.assume ? [goal(`${p.name}: the request is allowed`, asked, substC(inlineFlags(m, m.assume), req))] : []),
|
|
744
|
+
goal(`${p.name}: the step it causes`, caused, across(caused, p.then, Object.keys(m.state), Object.keys(m.maps ?? {}), { inputs: p.request, flags })),
|
|
521
745
|
];
|
|
522
746
|
}
|
|
747
|
+
const zero = { op: 'const', value: 0 };
|
|
748
|
+
export const trueC = { op: '<=', a: zero, b: zero }, falseC = { op: '<', a: zero, b: zero };
|
|
749
|
+
const and2 = (a, b) => ({ op: 'and', xs: [a, b] }), or2 = (a, b) => ({ op: 'or', xs: [a, b] });
|
|
750
|
+
const lexLt = (after, before) => (after.length && before.length ? or2({ op: '<', a: after[0], b: before[0] }, and2({ op: '=', a: after[0], b: before[0] }, lexLt(after.slice(1), before.slice(1)))) : falseC);
|
|
751
|
+
const eqs = (after, before) => (after.length && before.length ? and2({ op: '=', a: after[0], b: before[0] }, eqs(after.slice(1), before.slice(1))) : trueC);
|
|
752
|
+
const keep = (ws, prime) => (ws.length ? and2(or2({ op: 'not', x: ws[0] }, prime(ws[0])), keep(ws.slice(1), prime)) : trueC);
|
|
753
|
+
const anyOf = (ws) => (ws.length ? or2(ws[0], anyOf(ws.slice(1))) : falseC);
|
|
754
|
+
const named = (req) => (req.length ? and2({ op: '=', a: { op: 'var', name: req[0][0] }, b: req[0][1] }, named(req.slice(1))) : trueC);
|
|
755
|
+
export const livenessRanks = (l) => (Array.isArray(l.rank) ? l.rank : [l.rank]);
|
|
756
|
+
export const livenessHelpful = (l) => {
|
|
757
|
+
if ((l.request === undefined) === (l.helpful === undefined))
|
|
758
|
+
throw new Error(`liveness ${l.name}: a helpful request, or helpful requests with their regions`);
|
|
759
|
+
return (l.helpful ?? [{ request: l.request }]).map(h => ({ when: h.when ?? trueC, request: h.request }));
|
|
760
|
+
};
|
|
761
|
+
export function livenessParts(m, l) {
|
|
762
|
+
// A machine with a history as its plain machine (its sums are state, its queries inputs no request names).
|
|
763
|
+
const plain = m.history ? historyMachine(m).machine : m;
|
|
764
|
+
const queries = Object.keys(m.history?.queries ?? {}), client = plain.inputs.filter(i => !queries.includes(i));
|
|
765
|
+
const ranks = livenessRanks(l), helpful = livenessHelpful(l);
|
|
766
|
+
if (!ranks.length || !helpful.length)
|
|
767
|
+
throw new Error(`liveness ${l.name}: a rank of one natural or more, and a helpful request or more`);
|
|
768
|
+
for (const [what, t] of [['from', l.from], ['to', l.to], ...ranks.map((r, i) => [`rank ${i + 1}`, { op: '=', a: r, b: r }]), ...helpful.map((h, i) => [`region ${i + 1}`, h.when])]) {
|
|
769
|
+
if (mentions(t, plain.inputs))
|
|
770
|
+
throw new Error(`liveness ${l.name}: ${what} reads the state and params, not a request`);
|
|
771
|
+
}
|
|
772
|
+
for (const h of helpful)
|
|
773
|
+
for (const [input, t] of Object.entries(h.request)) {
|
|
774
|
+
if (!client.includes(input))
|
|
775
|
+
throw new Error(`liveness ${l.name}: a helpful request names inputs a client sends (${input} is ${queries.includes(input) ? 'a query, computed from the log' : 'none'})`);
|
|
776
|
+
if (mentions({ op: '=', a: t, b: t }, plain.inputs))
|
|
777
|
+
throw new Error(`liveness ${l.name}: the request's ${input} is a term over the state and params`);
|
|
778
|
+
}
|
|
779
|
+
const after = { vars: Object.fromEntries(Object.keys(plain.state).map(n => [n, { op: 'var', name: `${n}'` }])), maps: Object.fromEntries(Object.keys(plain.maps ?? {}).map(map => [map, (key) => ({ op: 'get', map: `${map}'`, key })])), flags: {} };
|
|
780
|
+
const prime = (c) => substC(c, after), ranks2 = ranks.map(r => substE(r, after)), to2 = prime(l.to);
|
|
781
|
+
const pending = and2(l.from, { op: 'not', x: l.to }), lower = lexLt(ranks2, ranks), regions = helpful.map(h => h.when);
|
|
782
|
+
return {
|
|
783
|
+
stay: { name: `${l.name}: each step stays or gets there, the rank kept only with its region`, when: pending,
|
|
784
|
+
then: and2(or2(prime(l.from), to2), or2(to2, or2(lower, and2(eqs(ranks2, ranks), keep(regions, prime))))) },
|
|
785
|
+
cover: { name: `${l.name}: some helpful request applies`, when: pending, then: anyOf(regions) },
|
|
786
|
+
helps: helpful.map((h, i) => ({ name: `${l.name}: helpful request ${i + 1}`, when: and2(pending, and2(h.when, named(Object.entries(h.request)))), then: or2(to2, lower) })),
|
|
787
|
+
};
|
|
788
|
+
}
|
|
523
789
|
export function stepLawObligations(machine, laws, instances, sl) {
|
|
524
790
|
const m = machine.history ? historyMachine(machine).machine : machine;
|
|
525
791
|
scope(m, laws);
|
|
526
|
-
const
|
|
792
|
+
const k = sl.steps ?? 1;
|
|
793
|
+
if (!Number.isInteger(k) || k < 1 || k > 16)
|
|
794
|
+
throw new Error(`step law ${sl.name}: steps is a whole number from 1 to 16`);
|
|
795
|
+
const whens = sl.when === undefined ? [] : Array.isArray(sl.when) ? sl.when : [sl.when];
|
|
796
|
+
if (whens.length > k)
|
|
797
|
+
throw new Error(`step law ${sl.name}: one when per step at most`);
|
|
798
|
+
const state = Object.keys(m.state), maps = Object.keys(m.maps ?? {}), carried = m.inputMaps ?? [], flagNames = Object.keys(m.step.flags ?? {});
|
|
527
799
|
const primes = [...state, ...maps].map(n => `${n}'`);
|
|
528
|
-
const
|
|
529
|
-
|
|
800
|
+
const laters = Array.from({ length: k - 1 }, (_, i) => laterNames(m, i + 2));
|
|
801
|
+
const later = laters.flatMap(l => Object.values(l.vars).map(e => e.name)), laterMaps = laters.flatMap(l => Object.values(l.maps));
|
|
802
|
+
const taken = [...m.params, ...m.inputs, ...state, ...maps, ...carried];
|
|
803
|
+
for (const p of [...primes, ...later, ...laterMaps])
|
|
530
804
|
if (taken.includes(p))
|
|
531
|
-
throw new Error(`${m.name}: ${p} is a name of the machine and a
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
if (sl.
|
|
536
|
-
|
|
537
|
-
|
|
805
|
+
throw new Error(`${m.name}: ${p} is a name of the machine and a name a step law reads`);
|
|
806
|
+
for (const key of [sl.forall, ...(sl.keys ?? [])])
|
|
807
|
+
if (key && [...taken, ...primes, ...later].includes(key))
|
|
808
|
+
throw new Error(`step law ${sl.name}'s key ${key} is already a name of ${m.name}`);
|
|
809
|
+
if (sl.keys?.length && (!sl.forall || new Set([sl.forall, ...sl.keys]).size !== sl.keys.length + 1))
|
|
810
|
+
throw new Error(`step law ${sl.name}: further keys come with a first, each a different name`);
|
|
811
|
+
unquantified(`step law ${sl.name}`, sl.then);
|
|
812
|
+
inScope(`step law ${sl.name}`, [sl.then], [...taken, ...state.map(n => `${n}'`), ...later, ...(sl.forall ? [sl.forall] : []), ...(sl.keys ?? [])], [...maps, ...carried, ...maps.map(n => `${n}'`), ...laterMaps], flagNames);
|
|
813
|
+
for (const w of whens) {
|
|
814
|
+
unquantified(`step law ${sl.name}'s when`, w);
|
|
815
|
+
inScope(`step law ${sl.name}'s when`, [w], taken, [...maps, ...carried], flagNames);
|
|
816
|
+
}
|
|
817
|
+
if (!naturalC(sl.then) || !whens.every(naturalC))
|
|
538
818
|
throw new Error(`step law ${sl.name}: a negative constant or factor is outside the kernel`);
|
|
539
|
-
const
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
flags: m.step.flags ?? {},
|
|
548
|
-
};
|
|
549
|
-
const before = laws.flatMap(l => (l.forall ? (instances[l.name] ?? []).map(key => substC(l.holds, { vars: { [l.forall]: key }, maps: {}, flags: {} })) : [l.holds]));
|
|
550
|
-
return [{ name: `${sl.name} @step law`, hyps: [...preOf(m), ...before, ...(m.assume ? [nowC(m.assume)] : []), ...(sl.when ? [nowC(sl.when)] : []), { op: 'not', x: substC(sl.then, after) }] }];
|
|
819
|
+
const u = updateOf(m), assume = m.assume ? [inlineFlags(m, m.assume)] : [];
|
|
820
|
+
let run = given(m, holds(from(), preOf(m)), instances);
|
|
821
|
+
for (let j = 1; j <= k; j++) {
|
|
822
|
+
if (j > 1)
|
|
823
|
+
run = reading(run, laters[j - 2].vars, laters[j - 2].maps);
|
|
824
|
+
run = step(holds(run, [...assumed(laws, instances), ...assume, ...(whens[j - 1] ? [inlineFlags(m, whens[j - 1])] : []), ...closing(m, instances)]), u);
|
|
825
|
+
}
|
|
826
|
+
return [goal(k === 1 ? `${sl.name} @step law` : `${sl.name} @${k} steps`, run, across(run, sl.then, state, maps, { inputs: {}, flags: flagsOf(m) }))];
|
|
551
827
|
}
|
|
552
828
|
// The laws' proof and every step law's tree: all must check.
|
|
829
|
+
// `perStep`: a step law's own instances, where it has them (else `instances`).
|
|
553
830
|
export function checkStepLaws(m, laws, stepLaws, proof) {
|
|
554
|
-
return checkLaws(m, laws, proof.laws) && stepLaws.every(sl => stepLawObligations(m, laws, proof.instances, sl).every(g => proof.trees[g.name] !== undefined && check(g, proof.trees[g.name])));
|
|
831
|
+
return checkLaws(m, laws, proof.laws) && stepLaws.every(sl => stepLawObligations(m, laws, proof.perStep?.[sl.name] ?? proof.instances, sl).every(g => proof.trees[g.name] !== undefined && check(g, proof.trees[g.name])));
|
|
555
832
|
}
|
|
556
833
|
export function foldObligations(f, laws) {
|
|
557
|
-
if (![...Object.values(f.leaf), ...Object.values(f.node)].every(natural) || !laws.every(law => !law.forall && naturalC(law.holds) && !mentions(law.holds, f.inputs)))
|
|
834
|
+
if (![...Object.values(f.leaf), ...Object.values(f.node)].every(natural) || !laws.every(law => !law.forall && !law.keys?.length && naturalC(law.holds) && !mentions(law.holds, f.inputs)))
|
|
558
835
|
throw new Error('outside the kernel');
|
|
559
836
|
const names = Object.keys(f.leaf);
|
|
560
837
|
distinct(f.name, [...f.params, ...f.inputs, ...names]);
|
|
@@ -566,9 +843,9 @@ export function foldObligations(f, laws) {
|
|
|
566
843
|
inScope(`${f.name}'s laws`, laws.map(law => law.holds), [...f.params, ...names]);
|
|
567
844
|
const side = (p) => ({ vars: Object.fromEntries(names.map(n => [n, { op: 'var', name: `${p}${n}` }])), maps: {}, flags: {} });
|
|
568
845
|
// A fold's inputs are each leaf's and node's own, so its assumption holds at every leaf and node it reads them at.
|
|
846
|
+
// A leaf is a start from its inputs; a node, a step from the frame of its two children, the laws holding at each.
|
|
569
847
|
const assume = [...preOf(f), ...(f.assume ? [f.assume] : [])];
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
]);
|
|
848
|
+
const leaf = holds(from({ vars: { ...f.leaf }, maps: {}, flags: {} }), assume);
|
|
849
|
+
const node = step(holds(from(), [...assume, ...laws.flatMap(h => [substC(h.holds, side('l_')), substC(h.holds, side('r_'))])]), { vars: f.node, maps: {} });
|
|
850
|
+
return laws.flatMap(law => [goal(`${law.name} @leaf`, leaf, atEnd(leaf, law.holds)), goal(`${law.name} @node`, node, atEnd(node, law.holds))]);
|
|
574
851
|
}
|
package/dist/kernel/prove.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Law, Machine } from '../core/machine.js';
|
|
2
|
-
import { type Goal, type Instances, type Path, type StepLaw, type Tree } from './kernel.js';
|
|
1
|
+
import type { C, E, Law, Machine } from '../core/machine.js';
|
|
2
|
+
import { type Goal, type Instances, type Liveness, type Path, type StepLaw, type Tree } from './kernel.js';
|
|
3
3
|
export type Outcome = {
|
|
4
4
|
tree: Tree;
|
|
5
5
|
nodes: number;
|
|
@@ -24,20 +24,44 @@ export type Report = {
|
|
|
24
24
|
checked: boolean;
|
|
25
25
|
};
|
|
26
26
|
export declare function proveLaws(m: Machine, laws: Law[]): Report & {
|
|
27
|
+
laws?: Law[];
|
|
27
28
|
proof?: {
|
|
28
29
|
instances: Record<string, Instances>;
|
|
29
30
|
trees: Record<string, Tree>;
|
|
30
31
|
};
|
|
31
32
|
};
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
instances: Record<string, Instances>;
|
|
36
|
-
trees: Record<string, Tree>;
|
|
37
|
-
};
|
|
38
|
-
instances: Instances;
|
|
33
|
+
type StepProof = {
|
|
34
|
+
laws: {
|
|
35
|
+
instances: Record<string, Instances>;
|
|
39
36
|
trees: Record<string, Tree>;
|
|
40
37
|
};
|
|
38
|
+
instances: Instances;
|
|
39
|
+
perStep?: Record<string, Instances>;
|
|
40
|
+
trees: Record<string, Tree>;
|
|
41
|
+
};
|
|
42
|
+
export declare function proveStepLaws(m: Machine, given: Law[], stepLaws: StepLaw[]): Report & {
|
|
43
|
+
laws?: Law[];
|
|
44
|
+
stepLaws?: StepLaw[];
|
|
45
|
+
proof?: StepProof;
|
|
46
|
+
};
|
|
47
|
+
export declare function proveProgress(m: Machine, laws: Law[], p: {
|
|
48
|
+
name: string;
|
|
49
|
+
when: C;
|
|
50
|
+
then: C;
|
|
51
|
+
request: Record<string, E>;
|
|
52
|
+
}): Report & {
|
|
53
|
+
instances?: Instances;
|
|
54
|
+
trees?: Record<string, Tree>;
|
|
55
|
+
};
|
|
56
|
+
export declare function proveLiveness(m: Machine, laws: Law[], l: Liveness): Report & {
|
|
57
|
+
steps?: ReturnType<typeof proveStepLaws>;
|
|
41
58
|
};
|
|
42
59
|
export declare const countLeaves: (t: Tree) => number;
|
|
43
60
|
export declare function concretize(goal: Goal, bound?: number): Record<string, number> | undefined;
|
|
61
|
+
export declare function proveBuilt(m: Machine, laws: Law[], build: (inst: Instances) => Goal): {
|
|
62
|
+
proven: boolean;
|
|
63
|
+
instances?: Instances;
|
|
64
|
+
goal?: Goal;
|
|
65
|
+
tree?: Tree;
|
|
66
|
+
};
|
|
67
|
+
export {};
|