gdp-wired 0.13.0 → 0.13.2
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/dist/core/chart.d.ts +16 -3
- package/dist/core/chart.js +110 -35
- package/dist/core/dsl.d.ts +4 -1
- package/dist/core/dsl.js +27 -4
- package/package.json +1 -1
package/dist/core/chart.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type Kind, type Rule, type TermLike, type Verdict, Choice, Cond, Term } from './dsl.js';
|
|
2
2
|
import { type Law } from './machine.js';
|
|
3
3
|
import type { StepLaw } from '../kernel/kernel.js';
|
|
4
4
|
export type StateNode = {
|
|
@@ -19,10 +19,10 @@ type After<X> = X & {
|
|
|
19
19
|
};
|
|
20
20
|
export type Transition<P, I, X> = {
|
|
21
21
|
from: string | string[];
|
|
22
|
-
to
|
|
22
|
+
to?: string;
|
|
23
23
|
when?: (env: Env<P, I, X>) => Cond;
|
|
24
24
|
set?: (env: Env<P, I, X>) => Partial<{
|
|
25
|
-
[k in keyof X]: TermLike | Cond | X[k];
|
|
25
|
+
[k in keyof X]: X[k] extends Choice<infer K> ? K | X[k] : TermLike | Cond | X[k];
|
|
26
26
|
}>;
|
|
27
27
|
};
|
|
28
28
|
export declare class Chart<P extends Obj = {}, I extends Obj = {}, X extends Obj = {}> {
|
|
@@ -32,8 +32,11 @@ export declare class Chart<P extends Obj = {}, I extends Obj = {}, X extends Obj
|
|
|
32
32
|
params: Obj;
|
|
33
33
|
inputs: Obj;
|
|
34
34
|
context: Obj;
|
|
35
|
+
outputs: Obj;
|
|
35
36
|
regions: [string, StateNode][];
|
|
36
37
|
on: [string, Transition<P, I, X>][];
|
|
38
|
+
field?: string;
|
|
39
|
+
events?: string[];
|
|
37
40
|
});
|
|
38
41
|
private with;
|
|
39
42
|
params<K extends Record<string, Kind<unknown>>>(kinds: K): Chart<P & {
|
|
@@ -50,8 +53,18 @@ export declare class Chart<P extends Obj = {}, I extends Obj = {}, X extends Obj
|
|
|
50
53
|
kind: Kind<infer V>;
|
|
51
54
|
} ? V : Term;
|
|
52
55
|
}>;
|
|
56
|
+
outputs<T extends Record<string, number | {
|
|
57
|
+
kind: Kind<unknown>;
|
|
58
|
+
init: unknown;
|
|
59
|
+
}>>(init: T): Chart<P, I, X & {
|
|
60
|
+
[k in keyof T]: T[k] extends {
|
|
61
|
+
kind: Kind<infer V>;
|
|
62
|
+
} ? V : Term;
|
|
63
|
+
}>;
|
|
64
|
+
events(...names: string[]): Chart<P, I, X>;
|
|
53
65
|
states(tree: Record<string, StateNode>, opts?: {
|
|
54
66
|
initial?: string;
|
|
67
|
+
field?: string;
|
|
55
68
|
}): Chart<P, I, X>;
|
|
56
69
|
regions(regions: Record<string, StateNode>): Chart<P, I, X>;
|
|
57
70
|
on(event: string, t: Transition<P, I, X>): Chart<P, I, X>;
|
package/dist/core/chart.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// most specific source (an inner state over its parent) is taken, then the first declared; the regions react to the
|
|
17
17
|
// same event at once. A transition into a compound state enters its initial child, or the child it was last in when the
|
|
18
18
|
// compound keeps its history; `set` writes the extended state (a variable two regions both set takes the first region's).
|
|
19
|
-
import { all, any, oneOf, proveRule, proveSteps, rule, when } from "./dsl.js";
|
|
19
|
+
import { Choice, Cond, Term, all, any, no, oneOf, proveRule, proveSteps, rule, when, yes } from "./dsl.js";
|
|
20
20
|
import { interpret, numbersOf } from "./machine.js";
|
|
21
21
|
import { allowed } from "./watch.js";
|
|
22
22
|
export class Chart {
|
|
@@ -30,8 +30,15 @@ export class Chart {
|
|
|
30
30
|
inputs(kinds) { return this.with({ inputs: { ...this.s.inputs, ...kinds } }); }
|
|
31
31
|
// The extended state: a natural from a number, or any kind from `kind.from(init)`.
|
|
32
32
|
context(init) { return this.with({ context: { ...this.s.context, ...init } }); }
|
|
33
|
-
//
|
|
34
|
-
|
|
33
|
+
// What each step decides, read after it (a command, a verdict): every output starts each step at its default, and a
|
|
34
|
+
// transition taken may set it, so a step where none fires reads the default, never the last step's value. A default
|
|
35
|
+
// is a number, a choice (`oneOf(...).from('denied')`) or a yes/no (`bool.from(false)`).
|
|
36
|
+
outputs(init) { return this.with({ outputs: { ...this.s.outputs, ...init } }); }
|
|
37
|
+
// The events, in the order they are encoded (by default, the order they are first used in `on`).
|
|
38
|
+
events(...names) { return this.with({ events: names }); }
|
|
39
|
+
// One region's states (a chart of one region names its states plainly: 'active.paid'); `field` names the state
|
|
40
|
+
// variable the rule keeps them in (`state` by default).
|
|
41
|
+
states(tree, opts = {}) { return this.with({ regions: [['state', { states: tree, initial: opts.initial }]], field: opts.field }); }
|
|
35
42
|
// Parallel regions, each with its own states, reacting to the same events: paths start with the region ('payment.paid').
|
|
36
43
|
regions(regions) { return this.with({ regions: Object.entries(regions) }); }
|
|
37
44
|
on(event, t) { return this.with({ on: [...this.s.on, [event, t]] }); }
|
|
@@ -64,7 +71,7 @@ export class Chart {
|
|
|
64
71
|
}
|
|
65
72
|
};
|
|
66
73
|
walk('', root);
|
|
67
|
-
return { name, field: single ? 'state' : name, leaves, finals, compounds };
|
|
74
|
+
return { name, field: single ? s.field ?? 'state' : name, leaves, finals, compounds };
|
|
68
75
|
});
|
|
69
76
|
// A path as the region and the state within it.
|
|
70
77
|
const locate = (path) => {
|
|
@@ -81,14 +88,20 @@ export class Chart {
|
|
|
81
88
|
throw new Error(`${s.name}: no state ${path} in ${r.name}`);
|
|
82
89
|
return out;
|
|
83
90
|
};
|
|
84
|
-
const
|
|
91
|
+
const used = [...new Set(s.on.map(([e]) => e))], events = s.events ?? used;
|
|
92
|
+
for (const e of used)
|
|
93
|
+
if (!events.includes(e))
|
|
94
|
+
throw new Error(`${s.name}: the event ${e} isn't one of the events declared (${events.join(', ')})`);
|
|
95
|
+
if (new Set(events).size !== events.length)
|
|
96
|
+
throw new Error(`${s.name}: an event is declared twice`);
|
|
85
97
|
if (!events.length)
|
|
86
98
|
throw new Error(`${s.name}: a chart needs transitions`);
|
|
87
99
|
const placed = s.on.map(([event, t], order) => {
|
|
88
|
-
const froms = (Array.isArray(t.from) ? t.from : [t.from]).map(locate), [to] = locate(t.to);
|
|
100
|
+
const froms = (Array.isArray(t.from) ? t.from : [t.from]).map(locate), [to] = t.to === undefined ? froms[0] : locate(t.to);
|
|
89
101
|
if (froms.some(([r]) => r !== to))
|
|
90
|
-
throw new Error(`${s.name}: a transition on ${event} goes from one region to another (${t.to})`);
|
|
91
|
-
|
|
102
|
+
throw new Error(`${s.name}: a transition on ${event} goes from one region to another (${t.to ?? [t.from].flat().join(", ")})`);
|
|
103
|
+
if (t.to !== undefined)
|
|
104
|
+
under(to, locate(t.to)[1]);
|
|
92
105
|
const sources = [...new Set(froms.flatMap(([r, p]) => under(r, p)))];
|
|
93
106
|
return { ...t, event, region: to.name, sources, depth: Math.min(...froms.map(([, p]) => (p === '' ? 0 : p.split('.').length))), order };
|
|
94
107
|
});
|
|
@@ -103,9 +116,8 @@ export class Chart {
|
|
|
103
116
|
if (history && c)
|
|
104
117
|
state[lastOf(r, c)] = kind.from(enter(c));
|
|
105
118
|
}
|
|
106
|
-
for (const [k, v] of Object.entries(s.context))
|
|
119
|
+
for (const [k, v] of Object.entries({ ...s.context, ...s.outputs }))
|
|
107
120
|
state[k] = v;
|
|
108
|
-
const inState = (env) => (path) => { const [r, p] = locate(path); return any(...under(r, p).map(l => env[r.field].is(l))); };
|
|
109
121
|
const guardEnv = (env) => this.wrap(env);
|
|
110
122
|
// Entering a path: a leaf, or a compound's last child (with history) or its initial one.
|
|
111
123
|
const entered = (env, r, path) => {
|
|
@@ -117,23 +129,41 @@ export class Chart {
|
|
|
117
129
|
return entered(env, r, path ? `${path}.${c.initial}` : c.initial);
|
|
118
130
|
};
|
|
119
131
|
let built = rule(s.name).params(s.params).inputs({ event: oneOf(...events), ...s.inputs }).state(state);
|
|
120
|
-
|
|
121
|
-
//
|
|
122
|
-
|
|
132
|
+
// A set of a region's states as ranges of their indices (n <= 2, 3 <= n <= 4): a few comparisons, where one test per
|
|
133
|
+
// state would be a disjunction the prover splits on; the whole region is no condition at all.
|
|
134
|
+
const among = (env, r, leaves) => {
|
|
135
|
+
const n = env[r.field].index, ix = leaves.map(l => r.leaves.indexOf(l)).sort((a, b) => a - b), top = r.leaves.length - 1, ranges = [];
|
|
136
|
+
for (const i of ix) {
|
|
137
|
+
const last = ranges.at(-1);
|
|
138
|
+
if (last && i === last[1] + 1)
|
|
139
|
+
last[1] = i;
|
|
140
|
+
else
|
|
141
|
+
ranges.push([i, i]);
|
|
142
|
+
}
|
|
143
|
+
return any(...ranges.map(([a, b]) => (a === 0 && b === top ? yes : a === b ? n.eq(a) : a === 0 ? n.atMost(b) : b === top ? n.atLeast(a) : n.atLeast(a).and(n.atMost(b)))));
|
|
144
|
+
};
|
|
145
|
+
const regionOf = (t) => regions.find(r => r.name === t.region);
|
|
146
|
+
built = built.flags((env) => Object.fromEntries(placed.map((t, i) => [`on${i}`, all(env.event.is(t.event), among(env, regionOf(t), t.sources), ...(t.when ? [t.when(guardEnv(env))] : []))])));
|
|
147
|
+
// A transition is taken when it is enabled and no transition before it in its region, on its event, from a state it
|
|
148
|
+
// shares, is (transitions on other events are never enabled together).
|
|
149
|
+
built = built.flags((env) => Object.fromEntries(placed.map((t, i) => [`take${i}`, all(env[`on${i}`], ...placed.slice(0, i).flatMap((u, j) => (u.region === t.region && u.event === t.event && u.sources.some(l => t.sources.includes(l)) ? [env[`on${j}`].not()] : [])))])));
|
|
123
150
|
built = built.step((env) => {
|
|
124
151
|
const out = {};
|
|
125
152
|
for (const r of regions) {
|
|
126
|
-
// The transitions taken in a region exclude one another
|
|
127
|
-
|
|
128
|
-
|
|
153
|
+
// The transitions taken in a region exclude one another, so those entering one state share a `when` (any of
|
|
154
|
+
// them taken), and an internal one needs none (the state stays).
|
|
155
|
+
out[r.field] = grouped(placed.map((t, i) => [t, i]).filter(([t]) => t.region === r.name && t.to !== undefined)
|
|
156
|
+
.map(([t, i]) => ({ i, region: r.name, value: entered(env, r, locate(t.to)[1]) })), env, env[r.field]);
|
|
129
157
|
for (const [c, { history }] of r.compounds)
|
|
130
158
|
if (history && c)
|
|
131
159
|
out[lastOf(r, c)] = when(any(...under(r, c).map(l => out[r.field].is(l))), out[r.field], env[lastOf(r, c)]);
|
|
132
160
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
161
|
+
// Context keeps its value where no transition taken sets it; an output falls back to its default.
|
|
162
|
+
for (const k of [...Object.keys(s.context), ...Object.keys(s.outputs)]) {
|
|
163
|
+
const sets = placed.map((t, i) => [t, i]).filter(([t]) => t.set).map(([t, i]) => ({ i, region: t.region, value: t.set(guardEnv(env))[k] })).filter(x => x.value !== undefined);
|
|
164
|
+
const base = k in s.outputs ? defaultOf(env[k], s.outputs[k], k) : env[k];
|
|
165
|
+
if (sets.length || k in s.outputs)
|
|
166
|
+
out[k] = grouped(sets.map(x => ({ ...x, value: typeof x.value === 'string' ? env[k].of(x.value) : x.value })), env, base);
|
|
137
167
|
}
|
|
138
168
|
return out;
|
|
139
169
|
});
|
|
@@ -198,7 +228,9 @@ export class Chart {
|
|
|
198
228
|
reachedAt.set(label(reg, l), k);
|
|
199
229
|
} };
|
|
200
230
|
note(queue[0], start);
|
|
201
|
-
|
|
231
|
+
// Breadth first, so each state's run is a shortest one; done once every state has one.
|
|
232
|
+
const states = regions.reduce((n, reg) => n + reg.leaves.length, 0);
|
|
233
|
+
while (queue.length && seen.size < (opts.maxStates ?? 20_000) && reachedAt.size < states) {
|
|
202
234
|
const k = queue.shift(), st = seen.get(k).st;
|
|
203
235
|
for (const req of requests) {
|
|
204
236
|
if (!allowed(m, run, params, st, req))
|
|
@@ -213,29 +245,72 @@ export class Chart {
|
|
|
213
245
|
}
|
|
214
246
|
const runTo = (k) => { const out = []; for (let at = seen.get(k); at.by; at = seen.get(at.from))
|
|
215
247
|
out.unshift({ ...numbersOf(at.by), event: events[at.by.event] }); return out; };
|
|
216
|
-
|
|
248
|
+
// States no run reached: proven unreachable together (one may lean on another), else each on its own.
|
|
249
|
+
const reached = {}, missed = [];
|
|
217
250
|
for (const reg of regions)
|
|
218
251
|
for (const l of reg.leaves) {
|
|
219
252
|
const name = label(reg, l), at = reachedAt.get(name);
|
|
220
|
-
if (at)
|
|
253
|
+
if (at)
|
|
221
254
|
reached[name] = { run: runTo(at) };
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
const never = r.law(`${name} is never reached`, (env) => env[reg.field].is(l).not());
|
|
225
|
-
const v = proveRule(r, [...this.chartLaws(), never]);
|
|
226
|
-
reached[name] = v.proven && v.checked ? 'unreachable' : 'unknown';
|
|
255
|
+
else
|
|
256
|
+
missed.push({ name, law: r.law(`${name} is never reached`, (env) => env[reg.field].is(l).not()) });
|
|
227
257
|
}
|
|
228
|
-
const
|
|
229
|
-
const
|
|
258
|
+
const proven = (v) => v.proven && v.checked;
|
|
259
|
+
const allMissed = missed.length > 0 && proven(proveRule(r, [...this.chartLaws(), ...missed.map(x => x.law)]));
|
|
260
|
+
for (const x of missed)
|
|
261
|
+
reached[x.name] = allMissed || proven(proveRule(r, [...this.chartLaws(), x.law])) ? 'unreachable' : 'unknown';
|
|
262
|
+
// A way out is a transition to a state: an internal one stays where it is.
|
|
263
|
+
const stuck = regions.flatMap(reg => reg.leaves.filter(l => !reg.finals.has(l) && !placed.some(t => t.to !== undefined && t.region === reg.name && t.sources.includes(l))).map(l => label(reg, l)));
|
|
264
|
+
// Pairs as specific as each other, on one event, from a state they share: proven never both enabled, all at once,
|
|
265
|
+
// and only where that fails each on its own (to name the pair and the request).
|
|
266
|
+
const pairs = [];
|
|
230
267
|
placed.forEach((t, i) => placed.forEach((u, j) => {
|
|
231
268
|
if (j <= i || t.region !== u.region || t.event !== u.event || t.depth !== u.depth || !t.sources.some(l => u.sources.includes(l)))
|
|
232
269
|
return;
|
|
233
|
-
|
|
234
|
-
const v = proveSteps(r, this.chartLaws(), [apart]);
|
|
235
|
-
if (!(v.proven && v.checked))
|
|
236
|
-
ambiguous.push({ event: t.event, transitions: [t.order, u.order], verdict: v });
|
|
270
|
+
pairs.push({ t, u, law: r.stepLaw(`transitions ${t.order} and ${u.order} on ${t.event} are never both enabled`, (env) => all(env[`on${i}`], env[`on${j}`]).not()) });
|
|
237
271
|
}));
|
|
272
|
+
const ambiguous = [];
|
|
273
|
+
if (pairs.length && !proven(proveSteps(r, this.chartLaws(), pairs.map(x => x.law))))
|
|
274
|
+
for (const { t, u, law } of pairs) {
|
|
275
|
+
const v = proveSteps(r, this.chartLaws(), [law]);
|
|
276
|
+
if (!proven(v))
|
|
277
|
+
ambiguous.push({ event: t.event, transitions: [t.order, u.order], verdict: v });
|
|
278
|
+
}
|
|
238
279
|
return { ok: !stuck.length && !ambiguous.length && Object.values(reached).every(x => x !== 'unknown'), reached, stuck, ambiguous };
|
|
239
280
|
}
|
|
240
281
|
}
|
|
241
|
-
|
|
282
|
+
// The value a step writes: of the transitions taken, the first in order that writes one. Transitions of one region exclude
|
|
283
|
+
// one another, so consecutive ones of a region writing the same value share one `when` (any of them taken); the order
|
|
284
|
+
// between regions is kept.
|
|
285
|
+
function grouped(writes, env, base) {
|
|
286
|
+
const keyOf = (v) => JSON.stringify(v instanceof Choice ? v.index.e : v instanceof Cond ? v.c : v instanceof Term ? v.e : v);
|
|
287
|
+
const groups = [];
|
|
288
|
+
for (const w of writes) {
|
|
289
|
+
const key = keyOf(w.value);
|
|
290
|
+
let g;
|
|
291
|
+
for (let j = groups.length - 1; j >= 0 && groups[j].region === w.region; j--)
|
|
292
|
+
if (groups[j].key === key) {
|
|
293
|
+
g = groups[j];
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
if (g)
|
|
297
|
+
g.takes.push(env[`take${w.i}`]);
|
|
298
|
+
else
|
|
299
|
+
groups.push({ region: w.region, key, value: w.value, takes: [env[`take${w.i}`]] });
|
|
300
|
+
}
|
|
301
|
+
return groups.reduceRight((acc, g) => when(any(...g.takes), g.value, acc), base);
|
|
302
|
+
}
|
|
303
|
+
export const chart = (name) => new Chart({ name, params: {}, inputs: {}, context: {}, outputs: {}, regions: [], on: [] });
|
|
304
|
+
// An output's default as a value of its kind: a number, a choice's named value, or a yes/no.
|
|
305
|
+
function defaultOf(current, init, name) {
|
|
306
|
+
if (typeof init === 'number')
|
|
307
|
+
return init;
|
|
308
|
+
const { kind, init: v } = init;
|
|
309
|
+
if (current instanceof Choice)
|
|
310
|
+
return current.of(v);
|
|
311
|
+
if (typeof v === 'boolean')
|
|
312
|
+
return v ? yes : no;
|
|
313
|
+
if (typeof v === 'number')
|
|
314
|
+
return v;
|
|
315
|
+
throw new Error(`the output ${name}'s default is a number, a choice or a yes/no, not ${String(v)} of ${kind.constructor.name}`);
|
|
316
|
+
}
|
package/dist/core/dsl.d.ts
CHANGED
|
@@ -131,7 +131,10 @@ export declare class Choice<K extends string> {
|
|
|
131
131
|
constructor(n: Term, values: readonly K[]);
|
|
132
132
|
is(k: K): Cond;
|
|
133
133
|
of(k: K): Choice<K>;
|
|
134
|
-
|
|
134
|
+
get index(): Term;
|
|
135
|
+
eq(o: K | Choice<K> | number): Cond;
|
|
136
|
+
ne(o: K | Choice<K> | number): Cond;
|
|
137
|
+
private indexOf;
|
|
135
138
|
match(cases: {
|
|
136
139
|
[k in K]: () => Cond;
|
|
137
140
|
}): Cond;
|
package/dist/core/dsl.js
CHANGED
|
@@ -353,10 +353,15 @@ export class Choice {
|
|
|
353
353
|
this.n = n;
|
|
354
354
|
this.values = values;
|
|
355
355
|
}
|
|
356
|
-
is(k) { return this.n.eq(this.
|
|
356
|
+
is(k) { return this.n.eq(this.indexOf(k)); }
|
|
357
357
|
// The value named k, of this choice's kind (to write: when(c, status.of('closed'), status)).
|
|
358
|
-
of(k) { return new Choice(lit(this.
|
|
359
|
-
index(
|
|
358
|
+
of(k) { return new Choice(lit(this.indexOf(k)), this.values); }
|
|
359
|
+
// Its index among the values, in the order declared (oneOf('new', 'paid'): new is 0), as a term.
|
|
360
|
+
get index() { return this.n; }
|
|
361
|
+
// The same value as a name, another choice of the kind, or an index: status.eq('paid'), a.phase.eq(b.phase), phase.eq(3).
|
|
362
|
+
eq(o) { return typeof o === 'string' ? this.is(o) : typeof o === 'number' ? this.n.eq(o) : this.n.eq(o.n); }
|
|
363
|
+
ne(o) { return this.eq(o).not(); }
|
|
364
|
+
indexOf(k) {
|
|
360
365
|
const i = this.values.indexOf(k);
|
|
361
366
|
if (i < 0)
|
|
362
367
|
throw new Error(`${k} is not one of ${this.values.join(', ')}`);
|
|
@@ -2426,13 +2431,31 @@ function refuteByRuns(r, laws, stepLaws = []) {
|
|
|
2426
2431
|
st = next;
|
|
2427
2432
|
}
|
|
2428
2433
|
}
|
|
2434
|
+
// A step law holds from every state the laws allow, reached or not: from random such states (each scalar and cell
|
|
2435
|
+
// small, kept when every law and kind law holds there), a step that breaks it is a counterexample too, without a run.
|
|
2436
|
+
const steps1 = stepLaws.filter(sl => (sl.steps ?? 1) === 1), given = withKinds(r, laws);
|
|
2437
|
+
for (let trial = 0; steps1.length && trial < refuteBudget.trials * 4 && performance.now() < until; trial++) {
|
|
2438
|
+
const params = r.randomParams(rand, bound);
|
|
2439
|
+
if (m.pre && !evalC(m.pre, params, { scalars: {}, maps: {} }, m))
|
|
2440
|
+
continue;
|
|
2441
|
+
const st = { scalars: Object.fromEntries(Object.keys(m.state).map(n => [n, rand(8)])), maps: Object.fromEntries(Object.keys(m.maps ?? {}).map(n => [n, new Map(Array.from({ length: rand(3) }, () => [rand(bound + 1), rand(bound + 1)]))])) };
|
|
2442
|
+
if (m.history || !given.every(l => watch(m, run, l, params, bound).start(st)))
|
|
2443
|
+
continue;
|
|
2444
|
+
const input = r.randomInput(rand, bound, params);
|
|
2445
|
+
if (!allowed(m, run, params, st, input))
|
|
2446
|
+
continue;
|
|
2447
|
+
const next = run.step(params, st, input);
|
|
2448
|
+
for (const sl of steps1)
|
|
2449
|
+
if (!watch(m, run, sl, params, bound).step(st, input, next))
|
|
2450
|
+
return { goal: `${sl.name} @step law`, witness: { ...params, ...st.scalars, ...numbersOf(input) } };
|
|
2451
|
+
}
|
|
2429
2452
|
}
|
|
2430
2453
|
catch {
|
|
2431
2454
|
return undefined;
|
|
2432
2455
|
}
|
|
2433
2456
|
return undefined;
|
|
2434
2457
|
}
|
|
2435
|
-
const refutedVerdict = (out) => ({ proven: false, checked: false, goal: out.goal, stuck: 'sat', witness: out.witness, run: out.run });
|
|
2458
|
+
const refutedVerdict = (out) => ({ proven: false, checked: false, goal: out.goal, stuck: 'sat', witness: out.witness, ...(out.run ? { run: out.run } : {}) });
|
|
2436
2459
|
export function proveRule(r, laws) {
|
|
2437
2460
|
const refuted = refuteByRuns(r, laws);
|
|
2438
2461
|
if (refuted)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gdp-wired",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.2",
|
|
4
4
|
"description": "An automatic prover for stateful rules (budgets, caps, windows, workflows, policies): laws proven with no proof to write, checked again in Lean, counterexamples found on the GPU, the rule shipped as code",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"verification",
|