tla-ts 0.0.1 → 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 +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 +78 -30
- 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
package/src/levels.ts
ADDED
|
@@ -0,0 +1,850 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AssumeProve,
|
|
3
|
+
Definition,
|
|
4
|
+
Expression,
|
|
5
|
+
Module,
|
|
6
|
+
OperatorParam,
|
|
7
|
+
SourceLocation,
|
|
8
|
+
} from "./ast";
|
|
9
|
+
import { TlaSyntaxError } from "./errors";
|
|
10
|
+
import { getChildren } from "./evaluate";
|
|
11
|
+
import { mapChildren } from "./instances";
|
|
12
|
+
import { getBoundNames, getPatternNames } from "./resolve";
|
|
13
|
+
import { STANDARD_LEVELS } from "./standard-levels";
|
|
14
|
+
|
|
15
|
+
interface LevelData {
|
|
16
|
+
level: number;
|
|
17
|
+
params: Set<string>;
|
|
18
|
+
constraints: Map<string, number>;
|
|
19
|
+
argConstraints: Map<string, number>;
|
|
20
|
+
argParams: Set<string>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface OperatorLevels extends LevelData {
|
|
24
|
+
name: string;
|
|
25
|
+
formals: OperatorParam[];
|
|
26
|
+
maxLevels: number[];
|
|
27
|
+
weights: number[];
|
|
28
|
+
minMaxLevels: number[][];
|
|
29
|
+
conditions: Set<string>;
|
|
30
|
+
nonLeibniz: Set<number>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface LevelContext {
|
|
34
|
+
constants: Set<string>;
|
|
35
|
+
variables: Set<string>;
|
|
36
|
+
getDefinition: (name: string) => Definition | undefined;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
type ScopeEntry =
|
|
40
|
+
| { kind: "Formal"; arity: number }
|
|
41
|
+
| { kind: "Let"; definition: Definition; scope: Scope };
|
|
42
|
+
|
|
43
|
+
export type Scope = Map<string, ScopeEntry>;
|
|
44
|
+
|
|
45
|
+
type Resolved =
|
|
46
|
+
| { kind: "Declared"; name: string; level: number; arity: number }
|
|
47
|
+
| { kind: "Defined"; levels: OperatorLevels };
|
|
48
|
+
|
|
49
|
+
interface Operand {
|
|
50
|
+
data: LevelData;
|
|
51
|
+
operator?: OperatorLevels;
|
|
52
|
+
declared?: OperatorParam;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const MAX_LEVEL = 3;
|
|
56
|
+
|
|
57
|
+
const SEPARATOR = "\u0000";
|
|
58
|
+
|
|
59
|
+
export const getArgumentKey = (operator: string, index: number) =>
|
|
60
|
+
`${operator}${SEPARATOR}${index}`;
|
|
61
|
+
|
|
62
|
+
const getArgumentParamKey = (operator: string, index: number, param: string) =>
|
|
63
|
+
`${operator}${SEPARATOR}${index}${SEPARATOR}${param}`;
|
|
64
|
+
|
|
65
|
+
export const parseArgumentParamKey = (key: string) => {
|
|
66
|
+
const parts = key.split(SEPARATOR);
|
|
67
|
+
return { operator: parts[0], index: Number(parts[1]), param: parts[2] };
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const getConditionKey = (operator: number, operand: number, argument: number) =>
|
|
71
|
+
`${operator},${operand},${argument}`;
|
|
72
|
+
|
|
73
|
+
const createData = (level = 0): LevelData => ({
|
|
74
|
+
level,
|
|
75
|
+
params: new Set(),
|
|
76
|
+
constraints: new Map(),
|
|
77
|
+
argConstraints: new Map(),
|
|
78
|
+
argParams: new Set(),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
export const putConstraint = (constraints: Map<string, number>, name: string, level: number) =>
|
|
82
|
+
constraints.set(name, Math.min(level, constraints.get(name) ?? MAX_LEVEL));
|
|
83
|
+
|
|
84
|
+
export const putArgConstraint = (constraints: Map<string, number>, key: string, level: number) =>
|
|
85
|
+
constraints.set(key, Math.max(level, constraints.get(key) ?? 0));
|
|
86
|
+
|
|
87
|
+
const createSignature = (
|
|
88
|
+
name: string,
|
|
89
|
+
level: number,
|
|
90
|
+
maxLevels: number[],
|
|
91
|
+
weights: number[],
|
|
92
|
+
): OperatorLevels => ({
|
|
93
|
+
...createData(level),
|
|
94
|
+
name,
|
|
95
|
+
formals: [],
|
|
96
|
+
maxLevels,
|
|
97
|
+
weights,
|
|
98
|
+
minMaxLevels: [],
|
|
99
|
+
conditions: new Set(),
|
|
100
|
+
nonLeibniz: new Set(),
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const getMaxLevel = (operator: OperatorLevels, index: number) =>
|
|
104
|
+
operator.formals.length === 0 && operator.maxLevels.length > 0
|
|
105
|
+
? operator.maxLevels[Math.min(index, operator.maxLevels.length - 1)]
|
|
106
|
+
: (operator.maxLevels[index] ?? MAX_LEVEL);
|
|
107
|
+
|
|
108
|
+
const getWeight = (operator: OperatorLevels, index: number) =>
|
|
109
|
+
operator.formals.length === 0 && operator.weights.length > 0
|
|
110
|
+
? operator.weights[Math.min(index, operator.weights.length - 1)]
|
|
111
|
+
: (operator.weights[index] ?? 1);
|
|
112
|
+
|
|
113
|
+
const CORE_SIGNATURES = new Map(
|
|
114
|
+
(
|
|
115
|
+
[
|
|
116
|
+
["=", 0, [2, 2], [1, 1]],
|
|
117
|
+
["#", 0, [2, 2], [1, 1]],
|
|
118
|
+
["\\in", 0, [2, 2], [1, 1]],
|
|
119
|
+
["\\notin", 0, [2, 2], [1, 1]],
|
|
120
|
+
["\\subseteq", 0, [2, 2], [1, 1]],
|
|
121
|
+
["\\cup", 0, [2, 2], [1, 1]],
|
|
122
|
+
["\\cap", 0, [2, 2], [1, 1]],
|
|
123
|
+
["\\", 0, [2, 2], [1, 1]],
|
|
124
|
+
["=>", 0, [3, 3], [1, 1]],
|
|
125
|
+
["<=>", 0, [3, 3], [1, 1]],
|
|
126
|
+
["/\\", 0, [3, 3], [1, 1]],
|
|
127
|
+
["\\/", 0, [3, 3], [1, 1]],
|
|
128
|
+
["~", 0, [3], [1]],
|
|
129
|
+
["SUBSET", 0, [2], [1]],
|
|
130
|
+
["UNION", 0, [2], [1]],
|
|
131
|
+
["DOMAIN", 0, [2], [1]],
|
|
132
|
+
["~>", 3, [3, 3], [0, 0]],
|
|
133
|
+
["-+->", 3, [3, 3], [0, 0]],
|
|
134
|
+
["\\cdot", 2, [2, 2], [0, 0]],
|
|
135
|
+
["TRUE", 0, [], []],
|
|
136
|
+
["FALSE", 0, [], []],
|
|
137
|
+
["BOOLEAN", 0, [], []],
|
|
138
|
+
["STRING", 0, [], []],
|
|
139
|
+
] as const
|
|
140
|
+
).map(([name, level, maxLevels, weights]): [string, OperatorLevels] => [
|
|
141
|
+
name,
|
|
142
|
+
{
|
|
143
|
+
...createSignature(name, level, [...maxLevels], [...weights]),
|
|
144
|
+
nonLeibniz: new Set(level > 1 ? [0, 1] : []),
|
|
145
|
+
},
|
|
146
|
+
]),
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
const SET_LEVELS = createSignature("$Set", 0, [2], [1]);
|
|
150
|
+
const JUNCTION_LEVELS = createSignature("$Junction", 0, [3], [1]);
|
|
151
|
+
const CHOOSE_LEVELS = createSignature("$Choose", 0, [2], [1]);
|
|
152
|
+
const FUNCTION_LEVELS = createSignature("$Function", 0, [3], [1]);
|
|
153
|
+
const APPLY_LEVELS = createSignature("$FcnApply", 0, [2, 2], [1, 1]);
|
|
154
|
+
const SELECT_LEVELS = createSignature("$RcdSelect", 0, [2, 0], [1, 1]);
|
|
155
|
+
const PRIME_LEVELS = createSignature("'", 2, [1], [0]);
|
|
156
|
+
const ENABLED_LEVELS = createSignature("ENABLED", 1, [2], [0]);
|
|
157
|
+
const TEMPORAL_LEVELS = createSignature("[]", 3, [3], [0]);
|
|
158
|
+
const ACTION_LEVELS = createSignature("$SquareAct", 2, [2, 1], [0, 0]);
|
|
159
|
+
const FAIRNESS_LEVELS = createSignature("$WF", 3, [1, 2], [0, 0]);
|
|
160
|
+
const TEMPORAL_QUANTIFIER_LEVELS = createSignature("$TemporalExists", 3, [3], [0]);
|
|
161
|
+
|
|
162
|
+
const bindFormals = (scope: Scope, formals: OperatorParam[]) =>
|
|
163
|
+
formals.length === 0
|
|
164
|
+
? scope
|
|
165
|
+
: new Map([
|
|
166
|
+
...scope,
|
|
167
|
+
...formals.map((formal): [string, ScopeEntry] => [
|
|
168
|
+
formal.name,
|
|
169
|
+
{ kind: "Formal", arity: formal.arity },
|
|
170
|
+
]),
|
|
171
|
+
]);
|
|
172
|
+
|
|
173
|
+
const toFormals = (names: string[]) => names.map((name) => ({ name, arity: 0 }));
|
|
174
|
+
|
|
175
|
+
export const ACTION_LEVEL = 2;
|
|
176
|
+
|
|
177
|
+
export const TEMPORAL_LEVEL = 3;
|
|
178
|
+
|
|
179
|
+
const createLevelError = (message: string, location: SourceLocation) =>
|
|
180
|
+
new TlaSyntaxError(`${message} at line ${location.line}, column ${location.column}`);
|
|
181
|
+
|
|
182
|
+
const LOGICAL_OPERATORS = new Set(["/\\", "\\/", "=>", "<=>"]);
|
|
183
|
+
|
|
184
|
+
// SANY's OpApplNode.levelCheck rejects raw actions inside temporal formulas, but not inside a LET
|
|
185
|
+
// or a label.
|
|
186
|
+
const isOperatorApplication = (expression: Expression) =>
|
|
187
|
+
expression.kind !== "Let" && expression.kind !== "Label";
|
|
188
|
+
|
|
189
|
+
export const createLevelAnalyzer = (context: LevelContext) => {
|
|
190
|
+
const definitionLevels = new Map<Definition, OperatorLevels>();
|
|
191
|
+
|
|
192
|
+
const resolve = (name: string, scope: Scope): Resolved => {
|
|
193
|
+
const entry = scope.get(name);
|
|
194
|
+
if (entry?.kind === "Formal") {
|
|
195
|
+
return { kind: "Declared", name, level: 0, arity: entry.arity };
|
|
196
|
+
}
|
|
197
|
+
if (entry?.kind === "Let") {
|
|
198
|
+
return { kind: "Defined", levels: analyzeDefinition(entry.definition, entry.scope) };
|
|
199
|
+
}
|
|
200
|
+
if (context.constants.has(name)) return { kind: "Declared", name, level: 0, arity: 0 };
|
|
201
|
+
if (context.variables.has(name)) return { kind: "Declared", name, level: 1, arity: 0 };
|
|
202
|
+
const definition = context.getDefinition(name);
|
|
203
|
+
if (definition) {
|
|
204
|
+
return { kind: "Defined", levels: analyzeDefinition(definition, new Map()) };
|
|
205
|
+
}
|
|
206
|
+
const signature = CORE_SIGNATURES.get(name) ?? STANDARD_LEVELS.get(name);
|
|
207
|
+
if (signature) return { kind: "Defined", levels: signature };
|
|
208
|
+
return { kind: "Declared", name, level: 0, arity: 0 };
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const applyDeclared = (name: string, level: number, operands: Operand[]) => {
|
|
212
|
+
const data = createData(Math.max(level, ...operands.map((operand) => operand.data.level)));
|
|
213
|
+
data.params.add(name);
|
|
214
|
+
operands.forEach((operand, index) => {
|
|
215
|
+
operand.data.params.forEach((param) => {
|
|
216
|
+
data.params.add(param);
|
|
217
|
+
data.argParams.add(getArgumentParamKey(name, index, param));
|
|
218
|
+
});
|
|
219
|
+
operand.data.constraints.forEach((value, key) => putConstraint(data.constraints, key, value));
|
|
220
|
+
putArgConstraint(data.argConstraints, getArgumentKey(name, index), operand.data.level);
|
|
221
|
+
operand.data.argConstraints.forEach((value, key) =>
|
|
222
|
+
putArgConstraint(data.argConstraints, key, value),
|
|
223
|
+
);
|
|
224
|
+
operand.data.argParams.forEach((key) => data.argParams.add(key));
|
|
225
|
+
});
|
|
226
|
+
return data;
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
const applyDefined = (
|
|
230
|
+
operator: OperatorLevels,
|
|
231
|
+
operands: Operand[],
|
|
232
|
+
ranges: LevelData[] = [],
|
|
233
|
+
bound: string[] = [],
|
|
234
|
+
) => {
|
|
235
|
+
const isBound = (name: string) => bound.includes(name);
|
|
236
|
+
operands.forEach((operand, index) => {
|
|
237
|
+
if (operand.operator || operand.declared) return;
|
|
238
|
+
if (operand.data.level > getMaxLevel(operator, index)) {
|
|
239
|
+
throw new TlaSyntaxError(
|
|
240
|
+
`Level error in applying operator ${operator.name}: The level of argument ${index + 1} exceeds the maximum level allowed by the operator.`,
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
if (ranges.some((range) => range.level > ACTION_LEVEL)) {
|
|
245
|
+
throw new TlaSyntaxError(
|
|
246
|
+
`Level error in applying operator ${operator.name}: The level of the range for a bounded variable exceeds the maximum level allowed by the operator.`,
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
const getOperand = (formal: string) => {
|
|
250
|
+
const index = operator.formals.findIndex((param) => param.name === formal);
|
|
251
|
+
return index === -1 ? undefined : operands[index];
|
|
252
|
+
};
|
|
253
|
+
const hasCondition = (operandIndex: number, otherIndex: number, argumentIndex: number) =>
|
|
254
|
+
operator.conditions.has(getConditionKey(operandIndex, otherIndex, argumentIndex));
|
|
255
|
+
const data = createData(
|
|
256
|
+
Math.max(
|
|
257
|
+
operator.level,
|
|
258
|
+
...operands
|
|
259
|
+
.filter((_, index) => getWeight(operator, index) === 1)
|
|
260
|
+
.map((operand) => operand.data.level),
|
|
261
|
+
...ranges.map((range) => range.level),
|
|
262
|
+
),
|
|
263
|
+
);
|
|
264
|
+
operator.params.forEach((param) => data.params.add(param));
|
|
265
|
+
operands.forEach((operand, index) => {
|
|
266
|
+
if (getWeight(operator, index) === 1) {
|
|
267
|
+
operand.data.params.forEach((param) => data.params.add(param));
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
ranges.forEach((range) => range.params.forEach((param) => data.params.add(param)));
|
|
271
|
+
bound.forEach((name) => data.params.delete(name));
|
|
272
|
+
|
|
273
|
+
operator.constraints.forEach((value, key) => putConstraint(data.constraints, key, value));
|
|
274
|
+
operands.forEach((operand) =>
|
|
275
|
+
operand.data.constraints.forEach((value, key) => {
|
|
276
|
+
if (!isBound(key)) putConstraint(data.constraints, key, value);
|
|
277
|
+
}),
|
|
278
|
+
);
|
|
279
|
+
ranges.forEach((range) =>
|
|
280
|
+
range.constraints.forEach((value, key) => putConstraint(data.constraints, key, value)),
|
|
281
|
+
);
|
|
282
|
+
operands.forEach((operand, index) =>
|
|
283
|
+
operand.data.params.forEach((param) => {
|
|
284
|
+
if (!isBound(param)) putConstraint(data.constraints, param, getMaxLevel(operator, index));
|
|
285
|
+
}),
|
|
286
|
+
);
|
|
287
|
+
operands.forEach((operand, operandIndex) => {
|
|
288
|
+
const argument = operand.operator;
|
|
289
|
+
if (!argument) return;
|
|
290
|
+
operands.forEach((other, otherIndex) =>
|
|
291
|
+
argument.formals.forEach((_, argumentIndex) => {
|
|
292
|
+
if (!hasCondition(operandIndex, otherIndex, argumentIndex)) return;
|
|
293
|
+
other.data.params.forEach((param) =>
|
|
294
|
+
putConstraint(data.constraints, param, getMaxLevel(argument, argumentIndex)),
|
|
295
|
+
);
|
|
296
|
+
}),
|
|
297
|
+
);
|
|
298
|
+
});
|
|
299
|
+
operator.argParams.forEach((key) => {
|
|
300
|
+
const parameter = parseArgumentParamKey(key);
|
|
301
|
+
const argument = getOperand(parameter.operator)?.operator;
|
|
302
|
+
if (argument) {
|
|
303
|
+
putConstraint(data.constraints, parameter.param, getMaxLevel(argument, parameter.index));
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
const argConstraints = [
|
|
308
|
+
operator.argConstraints,
|
|
309
|
+
...operands.map((operand) => operand.data.argConstraints),
|
|
310
|
+
...ranges.map((range) => range.argConstraints),
|
|
311
|
+
];
|
|
312
|
+
argConstraints.forEach((constraints) =>
|
|
313
|
+
constraints.forEach((value, key) => putArgConstraint(data.argConstraints, key, value)),
|
|
314
|
+
);
|
|
315
|
+
operands.forEach((operand, operandIndex) => {
|
|
316
|
+
const declared = operand.declared;
|
|
317
|
+
if (!declared) return;
|
|
318
|
+
Array.from({ length: declared.arity }, (_, argumentIndex) =>
|
|
319
|
+
putArgConstraint(
|
|
320
|
+
data.argConstraints,
|
|
321
|
+
getArgumentKey(declared.name, argumentIndex),
|
|
322
|
+
operator.minMaxLevels[operandIndex]?.[argumentIndex] ?? 0,
|
|
323
|
+
),
|
|
324
|
+
);
|
|
325
|
+
operands.forEach((other, otherIndex) =>
|
|
326
|
+
Array.from({ length: declared.arity }, (_, argumentIndex) => {
|
|
327
|
+
if (hasCondition(operandIndex, otherIndex, argumentIndex)) {
|
|
328
|
+
putArgConstraint(
|
|
329
|
+
data.argConstraints,
|
|
330
|
+
getArgumentKey(declared.name, argumentIndex),
|
|
331
|
+
other.data.level,
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
}),
|
|
335
|
+
);
|
|
336
|
+
});
|
|
337
|
+
operator.argParams.forEach((key) => {
|
|
338
|
+
const parameter = parseArgumentParamKey(key);
|
|
339
|
+
const argument = getOperand(parameter.operator);
|
|
340
|
+
if (argument) {
|
|
341
|
+
putArgConstraint(
|
|
342
|
+
data.argConstraints,
|
|
343
|
+
getArgumentKey(parameter.operator, parameter.index),
|
|
344
|
+
argument.data.level,
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
operands.forEach((operand) =>
|
|
350
|
+
operand.data.argParams.forEach((key) => {
|
|
351
|
+
if (!isBound(parseArgumentParamKey(key).param)) data.argParams.add(key);
|
|
352
|
+
}),
|
|
353
|
+
);
|
|
354
|
+
ranges.forEach((range) => range.argParams.forEach((key) => data.argParams.add(key)));
|
|
355
|
+
operator.argParams.forEach((key) => {
|
|
356
|
+
const parameter = parseArgumentParamKey(key);
|
|
357
|
+
const argument = getOperand(parameter.operator);
|
|
358
|
+
if (!argument) {
|
|
359
|
+
const value = getOperand(parameter.param);
|
|
360
|
+
if (!value) {
|
|
361
|
+
data.argParams.add(key);
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
value.data.params.forEach((param) =>
|
|
365
|
+
data.argParams.add(getArgumentParamKey(parameter.operator, parameter.index, param)),
|
|
366
|
+
);
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
if (argument.declared) {
|
|
370
|
+
data.argParams.add(
|
|
371
|
+
getArgumentParamKey(argument.declared.name, parameter.index, parameter.param),
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
operands.forEach((operand, operandIndex) => {
|
|
376
|
+
const declared = operand.declared;
|
|
377
|
+
if (!declared) return;
|
|
378
|
+
operands.forEach((other, otherIndex) =>
|
|
379
|
+
Array.from({ length: declared.arity }, (_, argumentIndex) => {
|
|
380
|
+
if (!hasCondition(operandIndex, otherIndex, argumentIndex)) return;
|
|
381
|
+
other.data.params.forEach((param) =>
|
|
382
|
+
data.argParams.add(getArgumentParamKey(declared.name, argumentIndex, param)),
|
|
383
|
+
);
|
|
384
|
+
}),
|
|
385
|
+
);
|
|
386
|
+
});
|
|
387
|
+
return data;
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
const toOperand = (expression: Expression, scope: Scope): Operand => {
|
|
391
|
+
if (expression.kind === "Lambda") {
|
|
392
|
+
const operator = analyzeOperator(
|
|
393
|
+
"LAMBDA",
|
|
394
|
+
toFormals(expression.params),
|
|
395
|
+
expression.body,
|
|
396
|
+
scope,
|
|
397
|
+
);
|
|
398
|
+
return { data: operator, operator };
|
|
399
|
+
}
|
|
400
|
+
const name =
|
|
401
|
+
expression.kind === "OperatorReference"
|
|
402
|
+
? expression.operator
|
|
403
|
+
: expression.kind === "Identifier"
|
|
404
|
+
? expression.name
|
|
405
|
+
: undefined;
|
|
406
|
+
if (name !== undefined) {
|
|
407
|
+
const resolved = resolve(name, scope);
|
|
408
|
+
if (
|
|
409
|
+
resolved.kind === "Defined" &&
|
|
410
|
+
(resolved.levels.formals.length > 0 || expression.kind === "OperatorReference")
|
|
411
|
+
) {
|
|
412
|
+
return { data: resolved.levels, operator: resolved.levels };
|
|
413
|
+
}
|
|
414
|
+
if (resolved.kind === "Declared" && resolved.arity > 0) {
|
|
415
|
+
const data = createData();
|
|
416
|
+
data.params.add(name);
|
|
417
|
+
return { data, declared: { name, arity: resolved.arity } };
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
return { data: analyze(expression, scope) };
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
const toOperands = (expressions: Expression[], scope: Scope) =>
|
|
424
|
+
expressions.map((expression) => toOperand(expression, scope));
|
|
425
|
+
|
|
426
|
+
const applyBuiltin = (
|
|
427
|
+
operator: OperatorLevels,
|
|
428
|
+
expressions: Expression[],
|
|
429
|
+
scope: Scope,
|
|
430
|
+
ranges: Expression[] = [],
|
|
431
|
+
bound: string[] = [],
|
|
432
|
+
) => {
|
|
433
|
+
const inner = bindFormals(scope, toFormals(bound));
|
|
434
|
+
return applyDefined(
|
|
435
|
+
operator,
|
|
436
|
+
toOperands(expressions, inner),
|
|
437
|
+
ranges.map((range) => analyze(range, scope)),
|
|
438
|
+
bound,
|
|
439
|
+
);
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
const applyResolved = (resolved: Resolved, operands: Operand[]) =>
|
|
443
|
+
resolved.kind === "Declared"
|
|
444
|
+
? applyDeclared(resolved.name, resolved.level, operands)
|
|
445
|
+
: applyDefined(resolved.levels, operands);
|
|
446
|
+
|
|
447
|
+
const checkJunction = (operands: Operand[], location: SourceLocation) => {
|
|
448
|
+
const levels = operands.map((operand) => operand.data.level);
|
|
449
|
+
if (levels.includes(ACTION_LEVEL) && levels.includes(TEMPORAL_LEVEL)) {
|
|
450
|
+
throw createLevelError("Formula has both temporal formula and action as arguments", location);
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
const checkApply = (expression: Expression & { kind: "Apply" }, operands: Operand[]) => {
|
|
455
|
+
const isLeadsTo = expression.operator === "~>" || expression.operator === "-+->";
|
|
456
|
+
if (isLeadsTo && operands.some((operand) => operand.data.level === ACTION_LEVEL)) {
|
|
457
|
+
throw createLevelError(
|
|
458
|
+
"Action used where only temporal formula or state predicate allowed",
|
|
459
|
+
expression.location,
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
if (LOGICAL_OPERATORS.has(expression.operator)) checkJunction(operands, expression.location);
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
const checkTemporal = (
|
|
466
|
+
expression: Expression & { kind: "Always" | "Eventually" },
|
|
467
|
+
level: number,
|
|
468
|
+
) => {
|
|
469
|
+
const operand = expression.expression;
|
|
470
|
+
const form = expression.kind === "Always" ? "BoxAction" : "AngleAction";
|
|
471
|
+
if (level !== ACTION_LEVEL || !isOperatorApplication(operand) || operand.kind === form) return;
|
|
472
|
+
throw createLevelError(
|
|
473
|
+
expression.kind === "Always"
|
|
474
|
+
? "[] followed by action not of form [A]_v"
|
|
475
|
+
: "<> followed by action not of form <<A>>_v",
|
|
476
|
+
expression.location,
|
|
477
|
+
);
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
const analyzeLet = (expression: Expression & { kind: "Let" }, scope: Scope) => {
|
|
481
|
+
const inner = new Map(scope);
|
|
482
|
+
expression.definitions.forEach((definition) =>
|
|
483
|
+
inner.set(definition.name, { kind: "Let", definition, scope: inner }),
|
|
484
|
+
);
|
|
485
|
+
const body = analyze(expression.body, inner);
|
|
486
|
+
const data = {
|
|
487
|
+
level: body.level,
|
|
488
|
+
params: new Set(body.params),
|
|
489
|
+
constraints: new Map(body.constraints),
|
|
490
|
+
argConstraints: new Map(body.argConstraints),
|
|
491
|
+
argParams: new Set(body.argParams),
|
|
492
|
+
};
|
|
493
|
+
expression.definitions.forEach((definition) => {
|
|
494
|
+
const levels = analyzeDefinition(definition, inner);
|
|
495
|
+
levels.constraints.forEach((value, key) => putConstraint(data.constraints, key, value));
|
|
496
|
+
levels.argConstraints.forEach((value, key) =>
|
|
497
|
+
putArgConstraint(data.argConstraints, key, value),
|
|
498
|
+
);
|
|
499
|
+
levels.argParams.forEach((key) => data.argParams.add(key));
|
|
500
|
+
});
|
|
501
|
+
return data;
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
const analyze = (expression: Expression, scope: Scope): LevelData => {
|
|
505
|
+
switch (expression.kind) {
|
|
506
|
+
case "Number":
|
|
507
|
+
case "Decimal":
|
|
508
|
+
case "String":
|
|
509
|
+
case "ExceptAt":
|
|
510
|
+
return createData();
|
|
511
|
+
case "Identifier":
|
|
512
|
+
return applyResolved(resolve(expression.name, scope), []);
|
|
513
|
+
case "Apply": {
|
|
514
|
+
const operands = toOperands(expression.args, scope);
|
|
515
|
+
checkApply(expression, operands);
|
|
516
|
+
return applyResolved(resolve(expression.operator, scope), operands);
|
|
517
|
+
}
|
|
518
|
+
case "OperatorReference":
|
|
519
|
+
case "Lambda":
|
|
520
|
+
return toOperand(expression, scope).data;
|
|
521
|
+
case "And":
|
|
522
|
+
case "Or": {
|
|
523
|
+
const operands = toOperands(expression.operands, scope);
|
|
524
|
+
checkJunction(operands, expression.location);
|
|
525
|
+
return applyDefined(JUNCTION_LEVELS, operands);
|
|
526
|
+
}
|
|
527
|
+
case "Prime":
|
|
528
|
+
case "Unchanged":
|
|
529
|
+
return applyBuiltin(PRIME_LEVELS, [expression.expression], scope);
|
|
530
|
+
case "Enabled":
|
|
531
|
+
return applyBuiltin(ENABLED_LEVELS, [expression.expression], scope);
|
|
532
|
+
case "Always":
|
|
533
|
+
case "Eventually": {
|
|
534
|
+
const operand = toOperand(expression.expression, scope);
|
|
535
|
+
checkTemporal(expression, operand.data.level);
|
|
536
|
+
return applyDefined(TEMPORAL_LEVELS, [operand]);
|
|
537
|
+
}
|
|
538
|
+
case "If":
|
|
539
|
+
return applyBuiltin(
|
|
540
|
+
JUNCTION_LEVELS,
|
|
541
|
+
[expression.condition, expression.consequent, expression.alternative],
|
|
542
|
+
scope,
|
|
543
|
+
);
|
|
544
|
+
case "Case":
|
|
545
|
+
return applyBuiltin(
|
|
546
|
+
JUNCTION_LEVELS,
|
|
547
|
+
[
|
|
548
|
+
...expression.arms.flatMap((arm) => [arm.condition, arm.body]),
|
|
549
|
+
...(expression.other ? [expression.other] : []),
|
|
550
|
+
],
|
|
551
|
+
scope,
|
|
552
|
+
);
|
|
553
|
+
case "Let":
|
|
554
|
+
return analyzeLet(expression, scope);
|
|
555
|
+
case "Forall":
|
|
556
|
+
case "Exists": {
|
|
557
|
+
const data = applyBuiltin(
|
|
558
|
+
JUNCTION_LEVELS,
|
|
559
|
+
[expression.body],
|
|
560
|
+
scope,
|
|
561
|
+
expression.bounds.map((bound) => bound.set),
|
|
562
|
+
getBoundNames(expression.bounds),
|
|
563
|
+
);
|
|
564
|
+
if (
|
|
565
|
+
data.level === TEMPORAL_LEVEL &&
|
|
566
|
+
expression.bounds.some((bound) => analyze(bound.set, scope).level === ACTION_LEVEL)
|
|
567
|
+
) {
|
|
568
|
+
throw createLevelError(
|
|
569
|
+
"Action-level bound of quantified temporal formula",
|
|
570
|
+
expression.location,
|
|
571
|
+
);
|
|
572
|
+
}
|
|
573
|
+
return data;
|
|
574
|
+
}
|
|
575
|
+
case "UnboundedQuantifier":
|
|
576
|
+
return applyBuiltin(
|
|
577
|
+
expression.isTemporal ? TEMPORAL_QUANTIFIER_LEVELS : JUNCTION_LEVELS,
|
|
578
|
+
[expression.body],
|
|
579
|
+
scope,
|
|
580
|
+
[],
|
|
581
|
+
expression.names,
|
|
582
|
+
);
|
|
583
|
+
case "Choose":
|
|
584
|
+
return applyBuiltin(
|
|
585
|
+
CHOOSE_LEVELS,
|
|
586
|
+
[expression.body],
|
|
587
|
+
scope,
|
|
588
|
+
expression.set ? [expression.set] : [],
|
|
589
|
+
getPatternNames(expression.pattern),
|
|
590
|
+
);
|
|
591
|
+
case "SetEnumeration":
|
|
592
|
+
case "Tuple":
|
|
593
|
+
case "FunctionSet":
|
|
594
|
+
case "Product":
|
|
595
|
+
return applyBuiltin(
|
|
596
|
+
SET_LEVELS,
|
|
597
|
+
expression.kind === "FunctionSet"
|
|
598
|
+
? [expression.domain, expression.range]
|
|
599
|
+
: expression.kind === "Product"
|
|
600
|
+
? expression.factors
|
|
601
|
+
: expression.elements,
|
|
602
|
+
scope,
|
|
603
|
+
);
|
|
604
|
+
case "SetFilter":
|
|
605
|
+
return applyBuiltin(
|
|
606
|
+
SET_LEVELS,
|
|
607
|
+
[expression.predicate],
|
|
608
|
+
scope,
|
|
609
|
+
[expression.bound.set],
|
|
610
|
+
getPatternNames(expression.bound.pattern),
|
|
611
|
+
);
|
|
612
|
+
case "SetMap":
|
|
613
|
+
return applyBuiltin(
|
|
614
|
+
SET_LEVELS,
|
|
615
|
+
[expression.expression],
|
|
616
|
+
scope,
|
|
617
|
+
expression.bounds.map((bound) => bound.set),
|
|
618
|
+
getBoundNames(expression.bounds),
|
|
619
|
+
);
|
|
620
|
+
case "FunctionConstruction":
|
|
621
|
+
return applyBuiltin(
|
|
622
|
+
FUNCTION_LEVELS,
|
|
623
|
+
[expression.body],
|
|
624
|
+
scope,
|
|
625
|
+
expression.bounds.map((bound) => bound.set),
|
|
626
|
+
getBoundNames(expression.bounds),
|
|
627
|
+
);
|
|
628
|
+
case "Record":
|
|
629
|
+
return applyBuiltin(
|
|
630
|
+
FUNCTION_LEVELS,
|
|
631
|
+
expression.fields.map((field) => field.value),
|
|
632
|
+
scope,
|
|
633
|
+
);
|
|
634
|
+
case "RecordSet":
|
|
635
|
+
return applyBuiltin(
|
|
636
|
+
SET_LEVELS,
|
|
637
|
+
expression.fields.map((field) => field.value),
|
|
638
|
+
scope,
|
|
639
|
+
);
|
|
640
|
+
case "Except":
|
|
641
|
+
return applyBuiltin(
|
|
642
|
+
SET_LEVELS,
|
|
643
|
+
[
|
|
644
|
+
expression.function,
|
|
645
|
+
...expression.updates.flatMap((update) => [
|
|
646
|
+
...update.path.flatMap((step) => (step.kind === "Index" ? step.args : [])),
|
|
647
|
+
update.value,
|
|
648
|
+
]),
|
|
649
|
+
],
|
|
650
|
+
scope,
|
|
651
|
+
);
|
|
652
|
+
case "FunctionApplication":
|
|
653
|
+
return applyDefined(APPLY_LEVELS, [
|
|
654
|
+
toOperand(expression.function, scope),
|
|
655
|
+
expression.args.length === 1
|
|
656
|
+
? toOperand(expression.args[0], scope)
|
|
657
|
+
: { data: applyBuiltin(SET_LEVELS, expression.args, scope) },
|
|
658
|
+
]);
|
|
659
|
+
case "FieldAccess":
|
|
660
|
+
return applyDefined(SELECT_LEVELS, [
|
|
661
|
+
toOperand(expression.record, scope),
|
|
662
|
+
{ data: createData() },
|
|
663
|
+
]);
|
|
664
|
+
case "BoxAction":
|
|
665
|
+
case "AngleAction":
|
|
666
|
+
return applyBuiltin(ACTION_LEVELS, [expression.action, expression.subscript], scope);
|
|
667
|
+
case "WeakFairness":
|
|
668
|
+
case "StrongFairness":
|
|
669
|
+
return applyBuiltin(FAIRNESS_LEVELS, [expression.subscript, expression.action], scope);
|
|
670
|
+
case "Label":
|
|
671
|
+
return analyze(expression.body, scope);
|
|
672
|
+
case "Subexpression":
|
|
673
|
+
return createData();
|
|
674
|
+
}
|
|
675
|
+
};
|
|
676
|
+
|
|
677
|
+
const getNonLeibnizNames = (
|
|
678
|
+
expression: Expression,
|
|
679
|
+
scope: Scope,
|
|
680
|
+
isNonLeibniz = false,
|
|
681
|
+
names = new Set<string>(),
|
|
682
|
+
): Set<string> => {
|
|
683
|
+
const visit = (child: Expression, isChildNonLeibniz = isNonLeibniz, childScope = scope) =>
|
|
684
|
+
getNonLeibnizNames(child, childScope, isChildNonLeibniz, names);
|
|
685
|
+
const visitOperator = (name: string, args: Expression[]) => {
|
|
686
|
+
const resolved = resolve(name, scope);
|
|
687
|
+
if (resolved.kind === "Declared") {
|
|
688
|
+
if (isNonLeibniz) names.add(name);
|
|
689
|
+
args.forEach((arg) => visit(arg));
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
args.forEach((arg, index) =>
|
|
693
|
+
visit(arg, isNonLeibniz || resolved.levels.nonLeibniz.has(index)),
|
|
694
|
+
);
|
|
695
|
+
};
|
|
696
|
+
switch (expression.kind) {
|
|
697
|
+
case "Identifier":
|
|
698
|
+
visitOperator(expression.name, []);
|
|
699
|
+
return names;
|
|
700
|
+
case "Apply":
|
|
701
|
+
visitOperator(expression.operator, expression.args);
|
|
702
|
+
return names;
|
|
703
|
+
// SANY's LetInNode and OpArgNode don't carry the non-Leibniz parameters of their bodies.
|
|
704
|
+
case "Lambda":
|
|
705
|
+
case "Let":
|
|
706
|
+
return names;
|
|
707
|
+
case "Prime":
|
|
708
|
+
case "Unchanged":
|
|
709
|
+
case "Enabled":
|
|
710
|
+
case "Always":
|
|
711
|
+
case "Eventually":
|
|
712
|
+
case "BoxAction":
|
|
713
|
+
case "AngleAction":
|
|
714
|
+
case "WeakFairness":
|
|
715
|
+
case "StrongFairness":
|
|
716
|
+
getChildren(expression).forEach((child) => visit(child, true));
|
|
717
|
+
return names;
|
|
718
|
+
case "UnboundedQuantifier":
|
|
719
|
+
visit(
|
|
720
|
+
expression.body,
|
|
721
|
+
isNonLeibniz || expression.isTemporal === true,
|
|
722
|
+
bindFormals(scope, toFormals(expression.names)),
|
|
723
|
+
);
|
|
724
|
+
return names;
|
|
725
|
+
default:
|
|
726
|
+
mapChildren(expression, (child, bound) => {
|
|
727
|
+
visit(child, isNonLeibniz, bindFormals(scope, toFormals(bound)));
|
|
728
|
+
return child;
|
|
729
|
+
});
|
|
730
|
+
return names;
|
|
731
|
+
}
|
|
732
|
+
};
|
|
733
|
+
|
|
734
|
+
const analyzeOperator = (
|
|
735
|
+
name: string,
|
|
736
|
+
formals: OperatorParam[],
|
|
737
|
+
body: Expression,
|
|
738
|
+
scope: Scope,
|
|
739
|
+
) => {
|
|
740
|
+
const inner = bindFormals(scope, formals);
|
|
741
|
+
const data = analyze(body, inner);
|
|
742
|
+
const isFormal = (param: string) => formals.some((formal) => formal.name === param);
|
|
743
|
+
const nonLeibnizNames = getNonLeibnizNames(body, inner);
|
|
744
|
+
const levels: OperatorLevels = {
|
|
745
|
+
...createData(data.level),
|
|
746
|
+
name,
|
|
747
|
+
formals,
|
|
748
|
+
nonLeibniz: new Set(
|
|
749
|
+
formals.flatMap((formal, index) => (nonLeibnizNames.has(formal.name) ? [index] : [])),
|
|
750
|
+
),
|
|
751
|
+
maxLevels: formals.map((formal) =>
|
|
752
|
+
Math.min(MAX_LEVEL, data.constraints.get(formal.name) ?? MAX_LEVEL),
|
|
753
|
+
),
|
|
754
|
+
weights: formals.map((formal) => (data.params.has(formal.name) ? 1 : 0)),
|
|
755
|
+
minMaxLevels: formals.map((formal) =>
|
|
756
|
+
Array.from(
|
|
757
|
+
{ length: formal.arity },
|
|
758
|
+
(_, index) => data.argConstraints.get(getArgumentKey(formal.name, index)) ?? 0,
|
|
759
|
+
),
|
|
760
|
+
),
|
|
761
|
+
conditions: new Set(
|
|
762
|
+
formals.flatMap((operator, operatorIndex) =>
|
|
763
|
+
formals.flatMap((param, paramIndex) =>
|
|
764
|
+
Array.from({ length: operator.arity }, (_, argumentIndex) => argumentIndex)
|
|
765
|
+
.filter((argumentIndex) =>
|
|
766
|
+
data.argParams.has(getArgumentParamKey(operator.name, argumentIndex, param.name)),
|
|
767
|
+
)
|
|
768
|
+
.map((argumentIndex) => getConditionKey(operatorIndex, paramIndex, argumentIndex)),
|
|
769
|
+
),
|
|
770
|
+
),
|
|
771
|
+
),
|
|
772
|
+
};
|
|
773
|
+
data.params.forEach((param) => {
|
|
774
|
+
if (!isFormal(param)) levels.params.add(param);
|
|
775
|
+
});
|
|
776
|
+
data.constraints.forEach((value, key) => {
|
|
777
|
+
if (!isFormal(key)) levels.constraints.set(key, value);
|
|
778
|
+
});
|
|
779
|
+
data.argConstraints.forEach((value, key) => {
|
|
780
|
+
if (!isFormal(parseArgumentParamKey(key).operator)) levels.argConstraints.set(key, value);
|
|
781
|
+
});
|
|
782
|
+
data.argParams.forEach((key) => {
|
|
783
|
+
const parameter = parseArgumentParamKey(key);
|
|
784
|
+
if (!isFormal(parameter.operator) || !isFormal(parameter.param)) levels.argParams.add(key);
|
|
785
|
+
});
|
|
786
|
+
return levels;
|
|
787
|
+
};
|
|
788
|
+
|
|
789
|
+
const analyzeDefinition = (definition: Definition, scope: Scope): OperatorLevels => {
|
|
790
|
+
const cached = definitionLevels.get(definition);
|
|
791
|
+
if (cached) return cached;
|
|
792
|
+
const formals = definition.kind === "Operator" ? definition.params : [];
|
|
793
|
+
definitionLevels.set(definition, {
|
|
794
|
+
...createData(),
|
|
795
|
+
name: definition.name,
|
|
796
|
+
formals,
|
|
797
|
+
maxLevels: formals.map(() => MAX_LEVEL),
|
|
798
|
+
weights: formals.map(() => 0),
|
|
799
|
+
minMaxLevels: formals.map(() => []),
|
|
800
|
+
conditions: new Set(),
|
|
801
|
+
nonLeibniz: new Set(),
|
|
802
|
+
});
|
|
803
|
+
const body: Expression =
|
|
804
|
+
definition.kind === "Function"
|
|
805
|
+
? {
|
|
806
|
+
kind: "FunctionConstruction",
|
|
807
|
+
bounds: definition.bounds,
|
|
808
|
+
body: definition.body,
|
|
809
|
+
location: definition.location,
|
|
810
|
+
}
|
|
811
|
+
: definition.body;
|
|
812
|
+
const levels = analyzeOperator(definition.name, formals, body, scope);
|
|
813
|
+
definitionLevels.set(definition, levels);
|
|
814
|
+
return levels;
|
|
815
|
+
};
|
|
816
|
+
|
|
817
|
+
return { analyze, analyzeDefinition, toOperand };
|
|
818
|
+
};
|
|
819
|
+
|
|
820
|
+
const analyzeAssumeProve = (
|
|
821
|
+
assumeProve: AssumeProve,
|
|
822
|
+
scope: Scope,
|
|
823
|
+
analyzer: ReturnType<typeof createLevelAnalyzer>,
|
|
824
|
+
) => {
|
|
825
|
+
const inner = new Map(scope);
|
|
826
|
+
assumeProve.assumptions.forEach((assumption) => {
|
|
827
|
+
if (assumption.kind === "NewSymbol") {
|
|
828
|
+
if (assumption.set) analyzer.analyze(assumption.set, inner);
|
|
829
|
+
inner.set(assumption.name, { kind: "Formal", arity: assumption.arity });
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
if (assumption.kind === "AssumeProve") analyzeAssumeProve(assumption, inner, analyzer);
|
|
833
|
+
else analyzer.analyze(assumption, inner);
|
|
834
|
+
});
|
|
835
|
+
analyzer.analyze(assumeProve.conclusion, inner);
|
|
836
|
+
};
|
|
837
|
+
|
|
838
|
+
// SANY checks the level of every definition, assumption, and theorem of a module.
|
|
839
|
+
export const checkModuleLevels = (module: Module, context: LevelContext) => {
|
|
840
|
+
const analyzer = createLevelAnalyzer(context);
|
|
841
|
+
module.definitions
|
|
842
|
+
.filter((definition) => !definition.name.includes("!"))
|
|
843
|
+
.forEach((definition) => analyzer.analyzeDefinition(definition, new Map()));
|
|
844
|
+
module.assumptions.forEach((assumption) => analyzer.analyze(assumption.expression, new Map()));
|
|
845
|
+
module.theorems.forEach((theorem) =>
|
|
846
|
+
theorem.statement.kind === "AssumeProve"
|
|
847
|
+
? analyzeAssumeProve(theorem.statement, new Map(), analyzer)
|
|
848
|
+
: analyzer.analyze(theorem.statement, new Map()),
|
|
849
|
+
);
|
|
850
|
+
};
|