tla-ts 0.0.1 → 0.0.3-dev.a50fc89
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 +81 -42
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +215 -30
- package/dist/index.d.ts +215 -30
- package/dist/index.js +1 -1
- package/dist/parallel-worker.cjs +2 -2
- 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 -1
- package/dist/run-cli.js +20 -1
- package/dist/transfer.cjs +388 -39
- package/dist/transfer.js +388 -39
- package/package.json +6 -2
- package/src/ast.ts +122 -5
- package/src/bags.ts +161 -71
- package/src/builtins.ts +203 -23
- package/src/bundled-modules.ts +63 -32
- package/src/check.ts +945 -238
- package/src/cli-options.ts +58 -0
- package/src/codegen.ts +52 -18
- package/src/config.ts +39 -11
- package/src/environment.ts +485 -140
- package/src/errors.ts +59 -1
- package/src/evaluate.ts +489 -137
- package/src/exit-codes.ts +75 -0
- package/src/fingerprints.ts +2 -2
- package/src/index.ts +9 -4
- package/src/instances.ts +221 -66
- package/src/java-overrides.ts +241 -0
- package/src/json.ts +153 -72
- package/src/levels.ts +850 -0
- package/src/lexer.ts +103 -28
- package/src/liveness.ts +47 -91
- 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 +57 -28
- package/src/parallel.ts +116 -62
- package/src/parser.ts +958 -205
- package/src/pretty-print.ts +259 -0
- package/src/randomization.ts +24 -113
- package/src/resolve.ts +472 -41
- package/src/run-cli.ts +427 -61
- package/src/runtime.ts +63 -28
- package/src/spec-description.ts +509 -0
- package/src/standard-levels.ts +105 -0
- package/src/standard-modules.ts +223 -92
- package/src/subexpressions.ts +21 -35
- package/src/substitution-levels.ts +184 -0
- package/src/successors.ts +261 -46
- package/src/symmetry.ts +65 -21
- package/src/tableau.ts +43 -37
- package/src/tlc-ext.ts +120 -33
- package/src/tlc-module.ts +71 -47
- package/src/tlc-modules.ts +1022 -0
- package/src/tlc-order.ts +497 -0
- package/src/tlc-random.ts +411 -0
- package/src/tlc-summary.ts +109 -0
- package/src/transfer.ts +26 -5
- package/src/values.ts +1231 -176
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { LivenessChecker } from "./liveness";
|
|
2
|
+
|
|
3
|
+
// TLC's exit statuses, from EC.ExitStatus.
|
|
4
|
+
export const EXIT_STATUS = {
|
|
5
|
+
success: 0,
|
|
6
|
+
assumptionViolation: 10,
|
|
7
|
+
deadlock: 11,
|
|
8
|
+
safetyViolation: 12,
|
|
9
|
+
livenessViolation: 13,
|
|
10
|
+
assertViolation: 14,
|
|
11
|
+
specEvaluationFailure: 75,
|
|
12
|
+
safetyEvaluationFailure: 76,
|
|
13
|
+
livenessEvaluationFailure: 77,
|
|
14
|
+
specParseError: 150,
|
|
15
|
+
configParseError: 151,
|
|
16
|
+
error: 255,
|
|
17
|
+
} as const;
|
|
18
|
+
|
|
19
|
+
const exitCodes = new WeakMap<Error, number>();
|
|
20
|
+
const moduleExitCodes = new WeakMap<Error, number>();
|
|
21
|
+
const phase = { isNextState: false };
|
|
22
|
+
|
|
23
|
+
export const setNextStatePhase = (isNextState: boolean) => {
|
|
24
|
+
phase.isNextState = isNextState;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// The innermost site that knows TLC's exit status for an error sets it first and wins.
|
|
28
|
+
export const setExitCode = <Thrown>(error: Thrown, code: number) => {
|
|
29
|
+
if (error instanceof Error && !exitCodes.has(error)) exitCodes.set(error, code);
|
|
30
|
+
return error;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// TLC exits with the code of an exception from a standard module's Java code only when the exception
|
|
34
|
+
// escapes the computation of next states. In every other phase, the phase decides the code.
|
|
35
|
+
export const setModuleExitCode = (error: Error, code: number) => {
|
|
36
|
+
if (phase.isNextState) moduleExitCodes.set(error, code);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const getExitCode = (error: Error) => exitCodes.get(error) ?? moduleExitCodes.get(error);
|
|
40
|
+
|
|
41
|
+
// TLC exits with 255 when it fails to process or evaluate a liveness property, in any phase.
|
|
42
|
+
export const withLivenessExitCode = (
|
|
43
|
+
create: () => LivenessChecker | undefined,
|
|
44
|
+
): LivenessChecker | undefined => {
|
|
45
|
+
try {
|
|
46
|
+
const checker = create();
|
|
47
|
+
return (
|
|
48
|
+
checker && {
|
|
49
|
+
addInitialState: (index, state) => {
|
|
50
|
+
try {
|
|
51
|
+
checker.addInitialState(index, state);
|
|
52
|
+
} catch (error) {
|
|
53
|
+
throw setExitCode(error, EXIT_STATUS.error);
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
addState: (index, state, successors) => {
|
|
57
|
+
try {
|
|
58
|
+
return checker.addState(index, state, successors);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
throw setExitCode(error, EXIT_STATUS.error);
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
findViolation: () => {
|
|
64
|
+
try {
|
|
65
|
+
return checker.findViolation();
|
|
66
|
+
} catch (error) {
|
|
67
|
+
throw setExitCode(error, EXIT_STATUS.error);
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
throw setExitCode(error, EXIT_STATUS.error);
|
|
74
|
+
}
|
|
75
|
+
};
|
package/src/fingerprints.ts
CHANGED
|
@@ -4,7 +4,7 @@ const EMPTY = 0;
|
|
|
4
4
|
const TOMBSTONE = 1;
|
|
5
5
|
const INITIAL_CAPACITY = 1 << 12;
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
interface FingerprintTable {
|
|
8
8
|
insert: (first: number, second: number) => boolean;
|
|
9
9
|
remove: (first: number, second: number) => void;
|
|
10
10
|
}
|
|
@@ -15,7 +15,7 @@ const mix = (hash: number) => {
|
|
|
15
15
|
return second ^ (second >>> 16);
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
-
export const
|
|
18
|
+
export const writeStateFingerprint = (state: Value[], fingerprint: Int32Array) => {
|
|
19
19
|
let first = 0x811c9dc5;
|
|
20
20
|
let second = 0x6a09e667;
|
|
21
21
|
for (let index = 0; index < state.length; index++) {
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
export {
|
|
2
|
-
|
|
1
|
+
export type {
|
|
2
|
+
CheckOptions,
|
|
3
|
+
CheckResult,
|
|
4
|
+
CheckStatus,
|
|
5
|
+
EvaluationFailure,
|
|
6
|
+
TraceStep,
|
|
7
|
+
Violation,
|
|
8
|
+
} from "./check";
|
|
3
9
|
export { parseConfig } from "./config";
|
|
4
10
|
export type { ConfigValue, ConstantAssignment, ModelConfig, Substitution } from "./config";
|
|
5
11
|
export {
|
|
@@ -9,8 +15,7 @@ export {
|
|
|
9
15
|
TlaSyntaxError,
|
|
10
16
|
TlaUnsupportedError,
|
|
11
17
|
} from "./errors";
|
|
12
|
-
export {
|
|
13
|
-
export type { ParallelCheckOptions } from "./parallel";
|
|
18
|
+
export { check } from "./parallel";
|
|
14
19
|
export { parseModule } from "./parser";
|
|
15
20
|
export type { Module } from "./ast";
|
|
16
21
|
export { formatValue } from "./values";
|
package/src/instances.ts
CHANGED
|
@@ -7,19 +7,18 @@ import type {
|
|
|
7
7
|
Module,
|
|
8
8
|
OperatorParam,
|
|
9
9
|
SourceLocation,
|
|
10
|
+
Theorem,
|
|
10
11
|
} from "./ast";
|
|
11
12
|
import { TlaSyntaxError } from "./errors";
|
|
12
13
|
import { getChildren, isLocallyResolved } from "./evaluate";
|
|
13
|
-
import { isBefore } from "./resolve";
|
|
14
|
-
|
|
15
|
-
const getPatternNames = (pattern: BoundPattern) =>
|
|
16
|
-
pattern.kind === "Name" ? [pattern.name] : pattern.names;
|
|
17
|
-
|
|
18
|
-
const getBoundNames = (bounds: Bound[]) =>
|
|
19
|
-
bounds.flatMap((bound) => getPatternNames(bound.pattern));
|
|
14
|
+
import { getArity, getBoundNames, getPatternNames, isBefore } from "./resolve";
|
|
20
15
|
|
|
21
16
|
export const substitutedExpressions = new WeakSet<Expression>();
|
|
22
17
|
|
|
18
|
+
// Applications inside an instance with substitutions or parameters, which TLC evaluates in a
|
|
19
|
+
// context that binds them.
|
|
20
|
+
export const contextualApplications = new WeakSet<Expression>();
|
|
21
|
+
|
|
23
22
|
export const renameLocals = (
|
|
24
23
|
expression: Expression,
|
|
25
24
|
names: Map<string, string>,
|
|
@@ -41,26 +40,39 @@ export const renameLocals = (
|
|
|
41
40
|
return mapped;
|
|
42
41
|
};
|
|
43
42
|
|
|
44
|
-
const mapBounds = (bounds: Bound[], map: (child: Expression) => Expression)
|
|
43
|
+
const mapBounds = (bounds: Bound[], map: (child: Expression) => Expression) =>
|
|
45
44
|
bounds.map((bound) => ({ pattern: bound.pattern, set: map(bound.set) }));
|
|
46
45
|
|
|
46
|
+
const sourceNames = new WeakMap<Definition, string>();
|
|
47
|
+
|
|
48
|
+
export const getSourceName = (definition: Definition) =>
|
|
49
|
+
sourceNames.get(definition) ?? definition.name;
|
|
50
|
+
|
|
51
|
+
const withSourceName = (definition: Definition, source: Definition) => {
|
|
52
|
+
if (getSourceName(source) !== definition.name) sourceNames.set(definition, getSourceName(source));
|
|
53
|
+
return definition;
|
|
54
|
+
};
|
|
55
|
+
|
|
47
56
|
export const mapDefinition = (
|
|
48
57
|
definition: Definition,
|
|
49
58
|
map: (child: Expression, bound: string[]) => Expression,
|
|
50
59
|
): Definition =>
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
withSourceName(
|
|
61
|
+
definition.kind === "Function"
|
|
62
|
+
? {
|
|
63
|
+
...definition,
|
|
64
|
+
bounds: mapBounds(definition.bounds, (child) => map(child, [])),
|
|
65
|
+
body: map(definition.body, getBoundNames(definition.bounds)),
|
|
66
|
+
}
|
|
67
|
+
: {
|
|
68
|
+
...definition,
|
|
69
|
+
body: map(
|
|
70
|
+
definition.body,
|
|
71
|
+
definition.params.map((param) => param.name),
|
|
72
|
+
),
|
|
73
|
+
},
|
|
74
|
+
definition,
|
|
75
|
+
);
|
|
64
76
|
|
|
65
77
|
export const mapChildren = (
|
|
66
78
|
expression: Expression,
|
|
@@ -69,6 +81,7 @@ export const mapChildren = (
|
|
|
69
81
|
const plain = (child: Expression) => map(child, []);
|
|
70
82
|
switch (expression.kind) {
|
|
71
83
|
case "Number":
|
|
84
|
+
case "Decimal":
|
|
72
85
|
case "String":
|
|
73
86
|
case "Identifier":
|
|
74
87
|
case "OperatorReference":
|
|
@@ -199,29 +212,131 @@ export const mapChildren = (
|
|
|
199
212
|
}
|
|
200
213
|
};
|
|
201
214
|
|
|
202
|
-
|
|
215
|
+
interface Instantiation {
|
|
203
216
|
params: OperatorParam[];
|
|
204
217
|
substitutions: Map<string, Expression>;
|
|
205
218
|
variables: Set<string>;
|
|
206
219
|
getDefinition: (key: string) => Definition | undefined;
|
|
207
220
|
isParameterized: (key: string) => boolean;
|
|
208
221
|
addDefinition: (name: string, create: (key: string) => Definition) => string;
|
|
222
|
+
createName: (name: string) => string;
|
|
209
223
|
}
|
|
210
224
|
|
|
225
|
+
// LAMBDA is a keyword, so no name bound inside the instance can capture it.
|
|
226
|
+
const LAMBDA_NAME = "LAMBDA";
|
|
227
|
+
|
|
211
228
|
type RewriteMode = "Normal" | "Enabled" | "Primed";
|
|
212
229
|
|
|
213
230
|
interface Rewrite {
|
|
214
231
|
instantiation: Instantiation;
|
|
232
|
+
substitutedNames: Set<string>;
|
|
215
233
|
copies: Map<string, string>;
|
|
216
234
|
mode: RewriteMode;
|
|
217
235
|
modes: Map<RewriteMode, Rewrite>;
|
|
218
236
|
}
|
|
219
237
|
|
|
238
|
+
const getFreeNames = (expression: Expression, bound = new Set<string>()): string[] => {
|
|
239
|
+
const name =
|
|
240
|
+
expression.kind === "Identifier"
|
|
241
|
+
? expression.name
|
|
242
|
+
: expression.kind === "Apply" || expression.kind === "OperatorReference"
|
|
243
|
+
? expression.operator
|
|
244
|
+
: undefined;
|
|
245
|
+
const names = name === undefined || bound.has(name) ? [] : [name];
|
|
246
|
+
mapChildren(expression, (child, childBound) => {
|
|
247
|
+
names.push(
|
|
248
|
+
...getFreeNames(child, childBound.length === 0 ? bound : new Set([...bound, ...childBound])),
|
|
249
|
+
);
|
|
250
|
+
return child;
|
|
251
|
+
});
|
|
252
|
+
return names;
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
const createRenamer = (rewrite: Rewrite) => {
|
|
256
|
+
const renamed = new Map<string, string>();
|
|
257
|
+
return (name: string) => {
|
|
258
|
+
if (!rewrite.substitutedNames.has(name)) return name;
|
|
259
|
+
const fresh = renamed.get(name) ?? rewrite.instantiation.createName(name);
|
|
260
|
+
renamed.set(name, fresh);
|
|
261
|
+
return fresh;
|
|
262
|
+
};
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
const bindNames = (bound: Map<string, string>, names: string[], rename: (name: string) => string) =>
|
|
266
|
+
new Map([...bound, ...names.map((name): [string, string] => [name, rename(name)])]);
|
|
267
|
+
|
|
268
|
+
const renamePattern = (pattern: BoundPattern, rename: (name: string) => string): BoundPattern =>
|
|
269
|
+
pattern.kind === "Name"
|
|
270
|
+
? { kind: "Name", name: rename(pattern.name) }
|
|
271
|
+
: { kind: "Tuple", names: pattern.names.map(rename) };
|
|
272
|
+
|
|
273
|
+
const renameBounds = (bounds: Bound[], rename: (name: string) => string) =>
|
|
274
|
+
bounds.map((bound) => ({ pattern: renamePattern(bound.pattern, rename), set: bound.set }));
|
|
275
|
+
|
|
276
|
+
const renameParams = (definition: Definition, rename: (name: string) => string): Definition =>
|
|
277
|
+
definition.kind === "Function"
|
|
278
|
+
? { ...definition, bounds: renameBounds(definition.bounds, rename) }
|
|
279
|
+
: {
|
|
280
|
+
...definition,
|
|
281
|
+
params: definition.params.map((param) => ({
|
|
282
|
+
name: rename(param.name),
|
|
283
|
+
arity: param.arity,
|
|
284
|
+
})),
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
const renameNames = (
|
|
288
|
+
expression: Expression,
|
|
289
|
+
bound: Map<string, string>,
|
|
290
|
+
rename: (name: string) => string,
|
|
291
|
+
): Expression => {
|
|
292
|
+
const getBoundName = (name: string) => bound.get(name) ?? name;
|
|
293
|
+
switch (expression.kind) {
|
|
294
|
+
case "Identifier":
|
|
295
|
+
return { ...expression, name: getBoundName(expression.name) };
|
|
296
|
+
case "Apply":
|
|
297
|
+
case "OperatorReference":
|
|
298
|
+
return { ...expression, operator: getBoundName(expression.operator) };
|
|
299
|
+
case "Label":
|
|
300
|
+
return { ...expression, params: expression.params.map(getBoundName) };
|
|
301
|
+
case "Lambda":
|
|
302
|
+
return { ...expression, params: expression.params.map(rename) };
|
|
303
|
+
case "Let":
|
|
304
|
+
return {
|
|
305
|
+
...expression,
|
|
306
|
+
definitions: expression.definitions.map((definition) =>
|
|
307
|
+
withSourceName(
|
|
308
|
+
{ ...renameParams(definition, rename), name: rename(definition.name) },
|
|
309
|
+
definition,
|
|
310
|
+
),
|
|
311
|
+
),
|
|
312
|
+
};
|
|
313
|
+
case "Forall":
|
|
314
|
+
case "Exists":
|
|
315
|
+
case "FunctionConstruction":
|
|
316
|
+
case "SetMap":
|
|
317
|
+
return { ...expression, bounds: renameBounds(expression.bounds, rename) };
|
|
318
|
+
case "UnboundedQuantifier":
|
|
319
|
+
return { ...expression, names: expression.names.map(rename) };
|
|
320
|
+
case "Choose":
|
|
321
|
+
return { ...expression, pattern: renamePattern(expression.pattern, rename) };
|
|
322
|
+
case "SetFilter":
|
|
323
|
+
return {
|
|
324
|
+
...expression,
|
|
325
|
+
bound: {
|
|
326
|
+
pattern: renamePattern(expression.bound.pattern, rename),
|
|
327
|
+
set: expression.bound.set,
|
|
328
|
+
},
|
|
329
|
+
};
|
|
330
|
+
default:
|
|
331
|
+
return expression;
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
|
|
220
335
|
export const getEnabledVariableName = (name: string) => `${name}@ENABLED`;
|
|
221
336
|
|
|
222
337
|
const getMode = (rewrite: Rewrite, mode: RewriteMode) => rewrite.modes.get(mode) ?? rewrite;
|
|
223
338
|
|
|
224
|
-
const isVariable = (expression: Expression, rewrite: Rewrite, bound:
|
|
339
|
+
const isVariable = (expression: Expression, rewrite: Rewrite, bound: Map<string, string>) =>
|
|
225
340
|
expression.kind === "Identifier" &&
|
|
226
341
|
!bound.has(expression.name) &&
|
|
227
342
|
!isLocallyResolved(expression) &&
|
|
@@ -230,7 +345,7 @@ const isVariable = (expression: Expression, rewrite: Rewrite, bound: Set<string>
|
|
|
230
345
|
const getUnchangedVariables = (
|
|
231
346
|
expression: Expression,
|
|
232
347
|
rewrite: Rewrite,
|
|
233
|
-
bound:
|
|
348
|
+
bound: Map<string, string>,
|
|
234
349
|
): string[] | undefined => {
|
|
235
350
|
if (expression.kind === "Tuple") {
|
|
236
351
|
const names = expression.elements.map((element) =>
|
|
@@ -244,14 +359,14 @@ const getUnchangedVariables = (
|
|
|
244
359
|
? undefined
|
|
245
360
|
: rewrite.instantiation.getDefinition(expression.name);
|
|
246
361
|
return definition?.kind === "Operator" && definition.params.length === 0
|
|
247
|
-
? getUnchangedVariables(definition.body, rewrite, new
|
|
362
|
+
? getUnchangedVariables(definition.body, rewrite, new Map())
|
|
248
363
|
: undefined;
|
|
249
364
|
};
|
|
250
365
|
|
|
251
366
|
const substituteInEnabled = (
|
|
252
367
|
expression: Expression,
|
|
253
368
|
rewrite: Rewrite,
|
|
254
|
-
bound:
|
|
369
|
+
bound: Map<string, string>,
|
|
255
370
|
): Expression | undefined => {
|
|
256
371
|
if (rewrite.mode === "Normal") return undefined;
|
|
257
372
|
if (rewrite.mode === "Primed") {
|
|
@@ -318,16 +433,15 @@ const substituteInEnabled = (
|
|
|
318
433
|
};
|
|
319
434
|
};
|
|
320
435
|
|
|
321
|
-
const
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
}));
|
|
436
|
+
const createLocalIdentifier = (name: string, location: SourceLocation): Expression => ({
|
|
437
|
+
kind: "Identifier",
|
|
438
|
+
name,
|
|
439
|
+
location,
|
|
440
|
+
resolution: { kind: "Local" },
|
|
441
|
+
});
|
|
328
442
|
|
|
329
|
-
const
|
|
330
|
-
|
|
443
|
+
const getParamArguments = (params: OperatorParam[], location: SourceLocation) =>
|
|
444
|
+
params.map((param) => createLocalIdentifier(param.name, location));
|
|
331
445
|
|
|
332
446
|
const getInstanceOperator = (
|
|
333
447
|
key: string,
|
|
@@ -344,12 +458,7 @@ const getInstanceOperator = (
|
|
|
344
458
|
operator: key,
|
|
345
459
|
args: [
|
|
346
460
|
...getParamArguments(params, location),
|
|
347
|
-
...names.map((name)
|
|
348
|
-
kind: "Identifier",
|
|
349
|
-
name,
|
|
350
|
-
location,
|
|
351
|
-
resolution: { kind: "Local" },
|
|
352
|
-
})),
|
|
461
|
+
...names.map((name) => createLocalIdentifier(name, location)),
|
|
353
462
|
],
|
|
354
463
|
location,
|
|
355
464
|
},
|
|
@@ -360,7 +469,11 @@ const getInstanceOperator = (
|
|
|
360
469
|
const getCopy = (rewrite: Rewrite, key: string) =>
|
|
361
470
|
rewrite.instantiation.isParameterized(key) ? copyDefinition(rewrite, key) : undefined;
|
|
362
471
|
|
|
363
|
-
const substitute = (
|
|
472
|
+
const substitute = (
|
|
473
|
+
expression: Expression,
|
|
474
|
+
rewrite: Rewrite,
|
|
475
|
+
bound: Map<string, string>,
|
|
476
|
+
): Expression => {
|
|
364
477
|
const recurse = (node: Expression) => substitute(node, rewrite, bound);
|
|
365
478
|
const instantiation = rewrite.instantiation;
|
|
366
479
|
const params = instantiation.params;
|
|
@@ -375,7 +488,6 @@ const substitute = (expression: Expression, rewrite: Rewrite, bound: Set<string>
|
|
|
375
488
|
(expression.kind === "WeakFairness" || expression.kind === "StrongFairness") &&
|
|
376
489
|
rewrite.mode === "Normal"
|
|
377
490
|
) {
|
|
378
|
-
const enabled = getMode(rewrite, "Enabled");
|
|
379
491
|
return {
|
|
380
492
|
...expression,
|
|
381
493
|
subscript: recurse(expression.subscript),
|
|
@@ -387,7 +499,7 @@ const substitute = (expression: Expression, rewrite: Rewrite, bound: Set<string>
|
|
|
387
499
|
subscript: expression.subscript,
|
|
388
500
|
location: expression.location,
|
|
389
501
|
},
|
|
390
|
-
|
|
502
|
+
getMode(rewrite, "Enabled"),
|
|
391
503
|
bound,
|
|
392
504
|
),
|
|
393
505
|
};
|
|
@@ -417,22 +529,30 @@ const substitute = (expression: Expression, rewrite: Rewrite, bound: Set<string>
|
|
|
417
529
|
if (expression.kind === "Apply" && isFree(expression.operator)) {
|
|
418
530
|
const args = expression.args.map(recurse);
|
|
419
531
|
const replacement = instantiation.substitutions.get(expression.operator);
|
|
532
|
+
const isContextual =
|
|
533
|
+
instantiation.substitutions.size > 0 ||
|
|
534
|
+
params.length > 0 ||
|
|
535
|
+
contextualApplications.has(expression);
|
|
536
|
+
const mark = (rewritten: Expression) => {
|
|
537
|
+
if (isContextual) contextualApplications.add(rewritten);
|
|
538
|
+
return rewritten;
|
|
539
|
+
};
|
|
420
540
|
if (replacement?.kind === "Identifier" || replacement?.kind === "OperatorReference") {
|
|
421
541
|
const operator = replacement.kind === "Identifier" ? replacement.name : replacement.operator;
|
|
422
|
-
return {
|
|
542
|
+
return mark({
|
|
423
543
|
...expression,
|
|
424
544
|
operator,
|
|
425
545
|
args,
|
|
426
546
|
resolution: replacement.kind === "Identifier" ? replacement.resolution : undefined,
|
|
427
|
-
};
|
|
547
|
+
});
|
|
428
548
|
}
|
|
429
549
|
if (replacement?.kind === "Apply") {
|
|
430
|
-
return {
|
|
550
|
+
return mark({
|
|
431
551
|
...expression,
|
|
432
552
|
operator: replacement.operator,
|
|
433
553
|
args: [...replacement.args, ...args],
|
|
434
554
|
resolution: replacement.resolution,
|
|
435
|
-
};
|
|
555
|
+
});
|
|
436
556
|
}
|
|
437
557
|
if (replacement?.kind === "Lambda") {
|
|
438
558
|
return {
|
|
@@ -440,7 +560,7 @@ const substitute = (expression: Expression, rewrite: Rewrite, bound: Set<string>
|
|
|
440
560
|
definitions: [
|
|
441
561
|
{
|
|
442
562
|
kind: "Operator",
|
|
443
|
-
name:
|
|
563
|
+
name: LAMBDA_NAME,
|
|
444
564
|
params: replacement.params.map((name) => ({ name, arity: 0 })),
|
|
445
565
|
body: replacement.body,
|
|
446
566
|
isLocal: true,
|
|
@@ -450,7 +570,7 @@ const substitute = (expression: Expression, rewrite: Rewrite, bound: Set<string>
|
|
|
450
570
|
],
|
|
451
571
|
body: {
|
|
452
572
|
...expression,
|
|
453
|
-
operator:
|
|
573
|
+
operator: LAMBDA_NAME,
|
|
454
574
|
args,
|
|
455
575
|
resolution: { kind: "Local" },
|
|
456
576
|
},
|
|
@@ -463,7 +583,7 @@ const substitute = (expression: Expression, rewrite: Rewrite, bound: Set<string>
|
|
|
463
583
|
);
|
|
464
584
|
}
|
|
465
585
|
const copy = getCopy(rewrite, expression.operator);
|
|
466
|
-
if (copy === undefined) return { ...expression, args };
|
|
586
|
+
if (copy === undefined) return mark({ ...expression, args });
|
|
467
587
|
return {
|
|
468
588
|
...expression,
|
|
469
589
|
operator: copy,
|
|
@@ -479,9 +599,13 @@ const substitute = (expression: Expression, rewrite: Rewrite, bound: Set<string>
|
|
|
479
599
|
}
|
|
480
600
|
return { ...expression, operator: copy };
|
|
481
601
|
}
|
|
482
|
-
|
|
483
|
-
|
|
602
|
+
const rename = createRenamer(rewrite);
|
|
603
|
+
const mapped = mapChildren(expression, (child, names) =>
|
|
604
|
+
names.length === 0
|
|
605
|
+
? recurse(child)
|
|
606
|
+
: substitute(child, rewrite, bindNames(bound, names, rename)),
|
|
484
607
|
);
|
|
608
|
+
return renameNames(mapped, bound, rename);
|
|
485
609
|
};
|
|
486
610
|
|
|
487
611
|
const withInstanceParams = (
|
|
@@ -500,7 +624,7 @@ const withInstanceParams = (
|
|
|
500
624
|
body: {
|
|
501
625
|
kind: "Let",
|
|
502
626
|
definitions: [definition],
|
|
503
|
-
body:
|
|
627
|
+
body: createLocalIdentifier(definition.name, location),
|
|
504
628
|
location,
|
|
505
629
|
},
|
|
506
630
|
isLocal: definition.isLocal,
|
|
@@ -517,8 +641,12 @@ const copyDefinition = (rewrite: Rewrite, key: string): string => {
|
|
|
517
641
|
if (!definition) return key;
|
|
518
642
|
return instantiation.addDefinition(definition.name, (copyKey) => {
|
|
519
643
|
rewrite.copies.set(key, copyKey);
|
|
520
|
-
const
|
|
521
|
-
|
|
644
|
+
const rename = createRenamer(rewrite);
|
|
645
|
+
const mapped = renameParams(
|
|
646
|
+
mapDefinition(definition, (child, bound) =>
|
|
647
|
+
substitute(child, rewrite, bindNames(new Map(), bound, rename)),
|
|
648
|
+
),
|
|
649
|
+
rename,
|
|
522
650
|
);
|
|
523
651
|
return withInstanceParams(mapped, instantiation.params, definition.location);
|
|
524
652
|
});
|
|
@@ -526,8 +654,17 @@ const copyDefinition = (rewrite: Rewrite, key: string): string => {
|
|
|
526
654
|
|
|
527
655
|
export const createInstantiation = (instantiation: Instantiation) => {
|
|
528
656
|
const modes = new Map<RewriteMode, Rewrite>();
|
|
657
|
+
const substitutedNames = new Set(
|
|
658
|
+
[...instantiation.substitutions.values()].flatMap((expression) => getFreeNames(expression)),
|
|
659
|
+
);
|
|
529
660
|
const createRewrite = (mode: RewriteMode): Rewrite => {
|
|
530
|
-
const created = {
|
|
661
|
+
const created = {
|
|
662
|
+
instantiation,
|
|
663
|
+
substitutedNames,
|
|
664
|
+
copies: new Map<string, string>(),
|
|
665
|
+
mode,
|
|
666
|
+
modes,
|
|
667
|
+
};
|
|
531
668
|
modes.set(mode, created);
|
|
532
669
|
return created;
|
|
533
670
|
};
|
|
@@ -554,36 +691,54 @@ const hasLetInstance = (expression: Expression): boolean =>
|
|
|
554
691
|
|
|
555
692
|
const expandLetInstances = (
|
|
556
693
|
expression: Expression,
|
|
557
|
-
instantiate: (instance: Instance) => Definition[],
|
|
694
|
+
instantiate: (instance: Instance, lets: Definition[]) => Definition[],
|
|
695
|
+
lets: Definition[],
|
|
558
696
|
): Expression => {
|
|
559
|
-
const
|
|
697
|
+
const inner = expression.kind === "Let" ? [...lets, ...expression.definitions] : lets;
|
|
698
|
+
const mapped = mapChildren(expression, (child) => expandLetInstances(child, instantiate, inner));
|
|
560
699
|
if (mapped.kind !== "Let" || !mapped.instances) return mapped;
|
|
561
700
|
const definitions = mapped.instances.reduce((current, instance) => {
|
|
562
701
|
const position = current.findIndex((definition) =>
|
|
563
702
|
isBefore(instance.location, definition.location),
|
|
564
703
|
);
|
|
565
704
|
const index = position === -1 ? current.length : position;
|
|
566
|
-
return [...current.slice(0, index), ...instantiate(instance), ...current.slice(index)];
|
|
705
|
+
return [...current.slice(0, index), ...instantiate(instance, inner), ...current.slice(index)];
|
|
567
706
|
}, mapped.definitions);
|
|
568
707
|
return { kind: "Let", definitions, body: mapped.body, location: mapped.location };
|
|
569
708
|
};
|
|
570
709
|
|
|
710
|
+
// A named theorem's statement is linked as its definition.
|
|
711
|
+
const getUnnamedStatement = (theorem: Theorem) =>
|
|
712
|
+
theorem.name === undefined && theorem.statement.kind !== "AssumeProve"
|
|
713
|
+
? theorem.statement
|
|
714
|
+
: undefined;
|
|
715
|
+
|
|
571
716
|
export const withLetInstances = (
|
|
572
717
|
module: Module,
|
|
573
|
-
instantiate: (instance: Instance) => Definition[],
|
|
718
|
+
instantiate: (instance: Instance, lets: Definition[]) => Definition[],
|
|
574
719
|
): Module => {
|
|
575
|
-
const
|
|
720
|
+
const hasLetInstances =
|
|
576
721
|
module.definitions.some((definition) => hasLetInstance(definition.body)) ||
|
|
577
|
-
module.assumptions.some((assumption) => hasLetInstance(assumption.expression))
|
|
578
|
-
|
|
722
|
+
module.assumptions.some((assumption) => hasLetInstance(assumption.expression)) ||
|
|
723
|
+
module.theorems.some((theorem) => {
|
|
724
|
+
const statement = getUnnamedStatement(theorem);
|
|
725
|
+
return statement !== undefined && hasLetInstance(statement);
|
|
726
|
+
});
|
|
727
|
+
if (!hasLetInstances) return module;
|
|
579
728
|
return {
|
|
580
729
|
...module,
|
|
581
730
|
definitions: module.definitions.map((definition) =>
|
|
582
|
-
mapDefinition(definition, (child) => expandLetInstances(child, instantiate)),
|
|
731
|
+
mapDefinition(definition, (child) => expandLetInstances(child, instantiate, [])),
|
|
583
732
|
),
|
|
584
733
|
assumptions: module.assumptions.map((assumption) => ({
|
|
585
734
|
...assumption,
|
|
586
|
-
expression: expandLetInstances(assumption.expression, instantiate),
|
|
735
|
+
expression: expandLetInstances(assumption.expression, instantiate, []),
|
|
587
736
|
})),
|
|
737
|
+
theorems: module.theorems.map((theorem) => {
|
|
738
|
+
const statement = getUnnamedStatement(theorem);
|
|
739
|
+
return statement === undefined
|
|
740
|
+
? theorem
|
|
741
|
+
: { ...theorem, statement: expandLetInstances(statement, instantiate, []) };
|
|
742
|
+
}),
|
|
588
743
|
};
|
|
589
744
|
};
|