tla-ts 0.0.2 → 0.0.3-dev.763cc71
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 +56 -10
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +50 -1
- package/dist/index.d.ts +50 -1
- package/dist/index.js +1 -1
- package/dist/parallel-worker.cjs +1 -1
- package/dist/parallel-worker.js +2 -2
- package/dist/parallel.cjs +1 -1
- package/dist/parallel.js +2 -2
- package/dist/run-cli.cjs +20 -2
- package/dist/run-cli.js +20 -2
- package/dist/transfer.cjs +12 -12
- package/dist/transfer.js +12 -12
- package/package.json +3 -1
- package/src/ast.ts +43 -3
- package/src/check.ts +43 -10
- package/src/cli-options.ts +58 -0
- package/src/errors.ts +37 -1
- package/src/evaluate.ts +3 -1
- package/src/lexer.ts +6 -1
- package/src/obligation-action.ts +384 -0
- package/src/obligation-anon.ts +575 -0
- package/src/obligation-deref.ts +637 -0
- package/src/obligation-expression.ts +1060 -0
- package/src/obligation-format.ts +406 -0
- package/src/obligation-generate.ts +1074 -0
- package/src/obligation-levels.ts +329 -0
- package/src/obligation-module.ts +1042 -0
- package/src/obligation-printer.ts +889 -0
- package/src/obligation-proof.ts +76 -0
- package/src/obligation-source.ts +1084 -0
- package/src/obligation-tuples.ts +184 -0
- package/src/obligations.ts +316 -0
- package/src/parallel-worker.ts +26 -7
- package/src/parser.ts +285 -52
- package/src/run-cli.ts +295 -27
- package/src/standard-levels.ts +3 -3
- package/src/transfer.ts +22 -1
- package/src/values.ts +53 -20
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
// A port of tlapm's Expr.Levels: the level of an expression (0 constant, 1 state, 2 action,
|
|
2
|
+
// 3 temporal, 4 undefined), the weights of an operator's arguments, and the declared names the
|
|
3
|
+
// level depends on.
|
|
4
|
+
|
|
5
|
+
import { TlaProofError, TlapmBugError } from "./errors";
|
|
6
|
+
import {
|
|
7
|
+
adjoin,
|
|
8
|
+
getBoundedFresh,
|
|
9
|
+
getFresh,
|
|
10
|
+
getScopeAt,
|
|
11
|
+
mapAccumulate,
|
|
12
|
+
type Bound,
|
|
13
|
+
type Builtin,
|
|
14
|
+
type Expr,
|
|
15
|
+
type Level,
|
|
16
|
+
type Scope,
|
|
17
|
+
} from "./obligation-expression";
|
|
18
|
+
|
|
19
|
+
interface LevelInfo {
|
|
20
|
+
level: number;
|
|
21
|
+
weights: boolean[];
|
|
22
|
+
params: Set<string>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const getInfo = (level: number, arity = 0): LevelInfo => ({
|
|
26
|
+
level,
|
|
27
|
+
weights: Array.from({ length: arity }, () => true),
|
|
28
|
+
params: new Set(),
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const BUILTIN_LEVELS: Partial<Record<Builtin, [number, number]>> = {
|
|
32
|
+
TRUE: [0, 0],
|
|
33
|
+
FALSE: [0, 0],
|
|
34
|
+
Implies: [0, 2],
|
|
35
|
+
Equiv: [0, 2],
|
|
36
|
+
Conj: [0, 2],
|
|
37
|
+
Disj: [0, 2],
|
|
38
|
+
Neg: [0, 1],
|
|
39
|
+
Eq: [0, 2],
|
|
40
|
+
Neq: [0, 2],
|
|
41
|
+
STRING: [0, 0],
|
|
42
|
+
BOOLEAN: [0, 0],
|
|
43
|
+
SUBSET: [0, 1],
|
|
44
|
+
UNION: [0, 1],
|
|
45
|
+
DOMAIN: [0, 1],
|
|
46
|
+
Subseteq: [0, 2],
|
|
47
|
+
Mem: [0, 2],
|
|
48
|
+
Notmem: [0, 2],
|
|
49
|
+
Setminus: [0, 2],
|
|
50
|
+
Cap: [0, 2],
|
|
51
|
+
Cup: [0, 2],
|
|
52
|
+
Actplus: [3, 2],
|
|
53
|
+
BoxTrue: [3, 1],
|
|
54
|
+
BoxFalse: [3, 1],
|
|
55
|
+
Diamond: [3, 1],
|
|
56
|
+
Prime: [2, 1],
|
|
57
|
+
StrongPrime: [2, 1],
|
|
58
|
+
ENABLED: [1, 1],
|
|
59
|
+
UNCHANGED: [2, 1],
|
|
60
|
+
Leadsto: [3, 2],
|
|
61
|
+
Cdot: [2, 2],
|
|
62
|
+
Nat: [0, 0],
|
|
63
|
+
Int: [0, 0],
|
|
64
|
+
Real: [0, 0],
|
|
65
|
+
Infinity: [0, 0],
|
|
66
|
+
Plus: [0, 2],
|
|
67
|
+
Minus: [0, 2],
|
|
68
|
+
Times: [0, 2],
|
|
69
|
+
Ratio: [0, 2],
|
|
70
|
+
Quotient: [0, 2],
|
|
71
|
+
Remainder: [0, 2],
|
|
72
|
+
Exp: [0, 2],
|
|
73
|
+
Divides: [0, 2],
|
|
74
|
+
Lteq: [0, 2],
|
|
75
|
+
Lt: [0, 2],
|
|
76
|
+
Gteq: [0, 2],
|
|
77
|
+
Gt: [0, 2],
|
|
78
|
+
Range: [0, 2],
|
|
79
|
+
Uminus: [0, 1],
|
|
80
|
+
Seq: [0, 1],
|
|
81
|
+
Len: [0, 1],
|
|
82
|
+
BSeq: [0, 2],
|
|
83
|
+
Cat: [0, 2],
|
|
84
|
+
Append: [0, 2],
|
|
85
|
+
Head: [0, 1],
|
|
86
|
+
Tail: [0, 1],
|
|
87
|
+
SubSeq: [0, 3],
|
|
88
|
+
SelectSeq: [0, 2],
|
|
89
|
+
OneArg: [0, 2],
|
|
90
|
+
Extend: [0, 2],
|
|
91
|
+
Print: [0, 2],
|
|
92
|
+
PrintT: [0, 1],
|
|
93
|
+
Assert: [0, 2],
|
|
94
|
+
JavaTime: [0, 0],
|
|
95
|
+
TLCGet: [0, 1],
|
|
96
|
+
TLCSet: [0, 2],
|
|
97
|
+
Permutations: [0, 1],
|
|
98
|
+
SortSeq: [0, 2],
|
|
99
|
+
RandomElement: [0, 1],
|
|
100
|
+
Any: [0, 0],
|
|
101
|
+
ToString: [0, 1],
|
|
102
|
+
Unprimable: [0, 1],
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export const LEVEL_NUMBERS: Record<Level, number> = {
|
|
106
|
+
Constant: 0,
|
|
107
|
+
State: 1,
|
|
108
|
+
Action: 2,
|
|
109
|
+
Temporal: 3,
|
|
110
|
+
Unknown: 0,
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const union = (sets: Set<string>[]) => new Set(sets.flatMap((set) => [...set]));
|
|
114
|
+
|
|
115
|
+
const combine = (infos: LevelInfo[]): LevelInfo => ({
|
|
116
|
+
level: Math.max(0, ...infos.map((info) => info.level)),
|
|
117
|
+
weights: [],
|
|
118
|
+
params: union(infos.map((info) => info.params)),
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// The level of each hypothesis depends only on the scope before it.
|
|
122
|
+
const hypLevels = new WeakMap<Scope, LevelInfo>();
|
|
123
|
+
|
|
124
|
+
const computeHypLevel = (scope: Scope): LevelInfo => {
|
|
125
|
+
const hyp = scope.hyp;
|
|
126
|
+
switch (hyp.kind) {
|
|
127
|
+
case "Flex":
|
|
128
|
+
return getInfo(1);
|
|
129
|
+
case "Fresh":
|
|
130
|
+
return { ...getInfo(LEVEL_NUMBERS[hyp.level], hyp.shape), params: new Set([hyp.name]) };
|
|
131
|
+
case "Defn": {
|
|
132
|
+
const definition = hyp.definition;
|
|
133
|
+
if (definition.kind !== "Operator" && definition.kind !== "Bpragma") {
|
|
134
|
+
throw new TlapmBugError();
|
|
135
|
+
}
|
|
136
|
+
return getLevelInfo(scope.rest, definition.body);
|
|
137
|
+
}
|
|
138
|
+
case "Fact":
|
|
139
|
+
return getLevelInfo(scope.rest, hyp.expression);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const getHypLevel = (scope: Scope): LevelInfo => {
|
|
144
|
+
const cached = hypLevels.get(scope);
|
|
145
|
+
if (cached) return cached;
|
|
146
|
+
const info = computeHypLevel(scope);
|
|
147
|
+
hypLevels.set(scope, info);
|
|
148
|
+
return info;
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const getBoundsLevel = (scope: Scope | undefined, bounds: Bound[], body: Expr) => {
|
|
152
|
+
const domains = bounds.flatMap((bound) =>
|
|
153
|
+
bound.domain.kind === "Domain" ? [getLevelInfo(scope, bound.domain.expression)] : [],
|
|
154
|
+
);
|
|
155
|
+
const inner = bounds.reduce(
|
|
156
|
+
(current, bound) => adjoin(current, getFresh(bound.name, bound.level)),
|
|
157
|
+
scope,
|
|
158
|
+
);
|
|
159
|
+
return combine([getLevelInfo(inner, body), ...domains]);
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const applyOperator = (operator: LevelInfo, args: LevelInfo[]): LevelInfo => {
|
|
163
|
+
if (operator.weights.length !== args.length) {
|
|
164
|
+
throw new TlaProofError("Invalid number of arguments");
|
|
165
|
+
}
|
|
166
|
+
const weighted = args.filter((_arg, index) => operator.weights[index]);
|
|
167
|
+
return {
|
|
168
|
+
level: Math.max(operator.level, ...weighted.map((arg) => arg.level)),
|
|
169
|
+
weights: [],
|
|
170
|
+
params: union([operator.params, ...weighted.map((arg) => arg.params)]),
|
|
171
|
+
};
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
export const getLevelInfo = (scope: Scope | undefined, expression: Expr): LevelInfo => {
|
|
175
|
+
const recurse = (inner: Expr) => getLevelInfo(scope, inner);
|
|
176
|
+
switch (expression.kind) {
|
|
177
|
+
case "Ix": {
|
|
178
|
+
const hypScope = getScopeAt(scope, expression.index - 1);
|
|
179
|
+
if (!hypScope) throw new TlapmBugError();
|
|
180
|
+
return getHypLevel(hypScope);
|
|
181
|
+
}
|
|
182
|
+
case "Internal": {
|
|
183
|
+
const entry = BUILTIN_LEVELS[expression.builtin];
|
|
184
|
+
if (!entry) throw new TlapmBugError();
|
|
185
|
+
return getInfo(entry[0], entry[1]);
|
|
186
|
+
}
|
|
187
|
+
case "Opaque":
|
|
188
|
+
case "String":
|
|
189
|
+
case "Num":
|
|
190
|
+
return getInfo(0);
|
|
191
|
+
case "Lambda": {
|
|
192
|
+
const inner = expression.params.reduce(
|
|
193
|
+
(current, param) => adjoin(current, getFresh(param.name, "Unknown", param.shape)),
|
|
194
|
+
scope,
|
|
195
|
+
);
|
|
196
|
+
const body = getLevelInfo(inner, expression.body);
|
|
197
|
+
const names = new Set(expression.params.map((param) => param.name));
|
|
198
|
+
return {
|
|
199
|
+
level: body.level,
|
|
200
|
+
weights: expression.params.map((param) => body.params.has(param.name)),
|
|
201
|
+
params: new Set([...body.params].filter((name) => !names.has(name))),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
case "Apply": {
|
|
205
|
+
const operator = expression.operator;
|
|
206
|
+
if (operator.kind === "Opaque") {
|
|
207
|
+
return applyOperator(getInfo(0, expression.args.length), expression.args.map(recurse));
|
|
208
|
+
}
|
|
209
|
+
if (operator.kind === "Internal" && expression.args.length === 1) {
|
|
210
|
+
const arg = expression.args[0];
|
|
211
|
+
switch (operator.builtin) {
|
|
212
|
+
case "Prime":
|
|
213
|
+
case "StrongPrime":
|
|
214
|
+
if (recurse(arg).level > 1) throw new TlapmBugError();
|
|
215
|
+
return getInfo(2, 1);
|
|
216
|
+
case "ENABLED": {
|
|
217
|
+
const info = recurse(arg);
|
|
218
|
+
if (info.level > 2) throw new TlapmBugError();
|
|
219
|
+
return { level: 1, weights: [], params: info.params };
|
|
220
|
+
}
|
|
221
|
+
case "UNCHANGED": {
|
|
222
|
+
const info = recurse(arg);
|
|
223
|
+
if (info.level > 1) throw new TlapmBugError();
|
|
224
|
+
return { level: 2, weights: [], params: info.params };
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
if (operator.kind === "Internal" && operator.builtin === "Cdot") {
|
|
229
|
+
expression.args.forEach(recurse);
|
|
230
|
+
return getInfo(2);
|
|
231
|
+
}
|
|
232
|
+
if (operator.kind === "Internal" && operator.builtin === "Leadsto") {
|
|
233
|
+
expression.args.forEach(recurse);
|
|
234
|
+
return getInfo(3, 2);
|
|
235
|
+
}
|
|
236
|
+
return applyOperator(recurse(operator), expression.args.map(recurse));
|
|
237
|
+
}
|
|
238
|
+
case "Sequent": {
|
|
239
|
+
const [inner, levels] = mapAccumulate(scope, expression.sequent.context, (current, hyp) => {
|
|
240
|
+
const next = adjoin(current, hyp);
|
|
241
|
+
return [next, getHypLevel(next).level];
|
|
242
|
+
});
|
|
243
|
+
return getInfo(Math.max(0, ...levels, getLevelInfo(inner, expression.sequent.active).level));
|
|
244
|
+
}
|
|
245
|
+
case "With":
|
|
246
|
+
case "Dot":
|
|
247
|
+
case "Parens":
|
|
248
|
+
return recurse(expression.expression);
|
|
249
|
+
case "Let": {
|
|
250
|
+
const inner = expression.definitions.reduce(
|
|
251
|
+
(current, definition) =>
|
|
252
|
+
adjoin(current, {
|
|
253
|
+
kind: "Defn",
|
|
254
|
+
definition,
|
|
255
|
+
whereDefined: "User",
|
|
256
|
+
visibility: "Visible",
|
|
257
|
+
isLocal: true,
|
|
258
|
+
}),
|
|
259
|
+
scope,
|
|
260
|
+
);
|
|
261
|
+
return getLevelInfo(inner, expression.body);
|
|
262
|
+
}
|
|
263
|
+
case "If":
|
|
264
|
+
return combine(
|
|
265
|
+
[expression.condition, expression.consequent, expression.alternative].map(recurse),
|
|
266
|
+
);
|
|
267
|
+
case "List":
|
|
268
|
+
return combine(expression.items.map(recurse));
|
|
269
|
+
case "Quant":
|
|
270
|
+
case "Fcn":
|
|
271
|
+
return getBoundsLevel(scope, expression.bounds, expression.body);
|
|
272
|
+
case "SetOf":
|
|
273
|
+
return getBoundsLevel(scope, expression.bounds, expression.element);
|
|
274
|
+
case "Tquant":
|
|
275
|
+
getLevelInfo(
|
|
276
|
+
expression.names.reduce((current, name) => adjoin(current, { kind: "Flex", name }), scope),
|
|
277
|
+
expression.body,
|
|
278
|
+
);
|
|
279
|
+
return getInfo(3);
|
|
280
|
+
case "Choose":
|
|
281
|
+
case "SetSt": {
|
|
282
|
+
const domainLevel = expression.domain && recurse(expression.domain);
|
|
283
|
+
const hyp = expression.domain
|
|
284
|
+
? getBoundedFresh(expression.name, expression.domain)
|
|
285
|
+
: getFresh(expression.name, "Constant");
|
|
286
|
+
return combine([
|
|
287
|
+
getLevelInfo(adjoin(scope, hyp), expression.body),
|
|
288
|
+
...(domainLevel ? [domainLevel] : []),
|
|
289
|
+
]);
|
|
290
|
+
}
|
|
291
|
+
case "SetEnum":
|
|
292
|
+
case "Tuple":
|
|
293
|
+
return combine(expression.elements.map(recurse));
|
|
294
|
+
case "Product":
|
|
295
|
+
return combine(expression.factors.map(recurse));
|
|
296
|
+
case "FcnApp":
|
|
297
|
+
return combine([expression.function, ...expression.args].map(recurse));
|
|
298
|
+
case "Arrow":
|
|
299
|
+
return combine([recurse(expression.domain), recurse(expression.range)]);
|
|
300
|
+
case "Rect":
|
|
301
|
+
case "Record":
|
|
302
|
+
return combine(expression.fields.map((field) => recurse(field.value)));
|
|
303
|
+
case "Except":
|
|
304
|
+
return combine([
|
|
305
|
+
recurse(expression.function),
|
|
306
|
+
...expression.specs.flatMap((spec) => [
|
|
307
|
+
...spec.trail.flatMap((point) =>
|
|
308
|
+
point.kind === "Apply" ? [recurse(point.expression)] : [],
|
|
309
|
+
),
|
|
310
|
+
recurse(spec.value),
|
|
311
|
+
]),
|
|
312
|
+
]);
|
|
313
|
+
case "Sub":
|
|
314
|
+
return { ...combine([recurse(expression.action), recurse(expression.subscript)]), level: 2 };
|
|
315
|
+
case "Tsub":
|
|
316
|
+
case "Fair":
|
|
317
|
+
recurse(expression.action);
|
|
318
|
+
recurse(expression.subscript);
|
|
319
|
+
return getInfo(3);
|
|
320
|
+
case "Case":
|
|
321
|
+
return combine([
|
|
322
|
+
...expression.arms.flatMap((arm) => [recurse(arm.condition), recurse(arm.body)]),
|
|
323
|
+
...(expression.other ? [recurse(expression.other)] : []),
|
|
324
|
+
]);
|
|
325
|
+
case "Bang":
|
|
326
|
+
case "At":
|
|
327
|
+
throw new TlapmBugError();
|
|
328
|
+
}
|
|
329
|
+
};
|