tla-ts 0.0.1 → 0.0.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/README.md +34 -41
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +166 -30
- package/dist/index.d.ts +166 -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 +2 -1
- package/dist/run-cli.js +2 -1
- package/dist/transfer.cjs +388 -39
- package/dist/transfer.js +388 -39
- package/package.json +4 -2
- package/src/ast.ts +82 -5
- package/src/bags.ts +161 -71
- package/src/builtins.ts +203 -23
- package/src/bundled-modules.ts +63 -32
- package/src/check.ts +904 -230
- package/src/codegen.ts +52 -18
- package/src/config.ts +39 -11
- package/src/environment.ts +485 -140
- package/src/errors.ts +22 -0
- package/src/evaluate.ts +486 -136
- 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 +97 -27
- package/src/liveness.ts +47 -91
- package/src/parallel-worker.ts +57 -28
- package/src/parallel.ts +116 -62
- package/src/parser.ts +703 -183
- package/src/pretty-print.ts +259 -0
- package/src/randomization.ts +24 -113
- package/src/resolve.ts +472 -41
- package/src/run-cli.ts +142 -44
- 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 +4 -4
- package/src/values.ts +1186 -164
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tla-ts",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "A TLA+ interpreter and model checker in TypeScript, 2
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "A TLA+ interpreter and model checker in TypeScript, 2× faster than TLC",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"formal verification",
|
|
7
7
|
"model checker",
|
|
@@ -68,7 +68,9 @@
|
|
|
68
68
|
"bench:corpus": "pnpm build && tsx scripts/benchmark-corpus.ts",
|
|
69
69
|
"conformance": "tsx scripts/conformance.ts",
|
|
70
70
|
"conformance:fetch": "tsx scripts/fetch-corpora.ts",
|
|
71
|
+
"conformance:sany": "tsx scripts/proof-conformance.ts",
|
|
71
72
|
"conformance:record": "tsx scripts/record-tlc.ts",
|
|
73
|
+
"conformance:record-sany": "tsx scripts/record-sany.ts",
|
|
72
74
|
"publint": "publint",
|
|
73
75
|
"test": "vp test",
|
|
74
76
|
"typecheck": "tsc --noEmit"
|
package/src/ast.ts
CHANGED
|
@@ -5,11 +5,25 @@ export interface LocalResolution {
|
|
|
5
5
|
export interface SourceLocation {
|
|
6
6
|
line: number;
|
|
7
7
|
column: number;
|
|
8
|
+
range?: SourceRange;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface SourceRange {
|
|
12
|
+
module: string;
|
|
13
|
+
start: SourceLocation;
|
|
14
|
+
end: SourceLocation;
|
|
8
15
|
}
|
|
9
16
|
|
|
10
17
|
export interface NumberExpression {
|
|
11
18
|
kind: "Number";
|
|
12
19
|
value: number;
|
|
20
|
+
text?: string;
|
|
21
|
+
location: SourceLocation;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface DecimalExpression {
|
|
25
|
+
kind: "Decimal";
|
|
26
|
+
text: string;
|
|
13
27
|
location: SourceLocation;
|
|
14
28
|
}
|
|
15
29
|
|
|
@@ -266,6 +280,7 @@ export interface SubexpressionExpression {
|
|
|
266
280
|
|
|
267
281
|
export type Expression =
|
|
268
282
|
| NumberExpression
|
|
283
|
+
| DecimalExpression
|
|
269
284
|
| StringExpression
|
|
270
285
|
| IdentifierExpression
|
|
271
286
|
| ApplyExpression
|
|
@@ -309,6 +324,7 @@ export interface OperatorDefinition {
|
|
|
309
324
|
name: string;
|
|
310
325
|
params: OperatorParam[];
|
|
311
326
|
body: Expression;
|
|
327
|
+
bodyRange?: SourceRange;
|
|
312
328
|
isLocal: boolean;
|
|
313
329
|
isRecursive: boolean;
|
|
314
330
|
location: SourceLocation;
|
|
@@ -329,6 +345,7 @@ export type Definition = OperatorDefinition | FunctionDefinition;
|
|
|
329
345
|
export interface Declaration {
|
|
330
346
|
name: string;
|
|
331
347
|
arity: number;
|
|
348
|
+
location: SourceLocation;
|
|
332
349
|
}
|
|
333
350
|
|
|
334
351
|
export interface Assumption {
|
|
@@ -353,19 +370,79 @@ export interface Instance {
|
|
|
353
370
|
location: SourceLocation;
|
|
354
371
|
}
|
|
355
372
|
|
|
356
|
-
export interface
|
|
357
|
-
|
|
358
|
-
|
|
373
|
+
export interface NewSymbol {
|
|
374
|
+
kind: "NewSymbol";
|
|
375
|
+
name: string;
|
|
376
|
+
arity: number;
|
|
377
|
+
level: "Constant" | "Variable" | "State" | "Action" | "Temporal";
|
|
378
|
+
set: Expression | undefined;
|
|
379
|
+
location: SourceLocation;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
export interface AssumeProve {
|
|
383
|
+
kind: "AssumeProve";
|
|
384
|
+
assumptions: (Expression | NewSymbol | AssumeProve)[];
|
|
385
|
+
conclusion: Expression;
|
|
386
|
+
location: SourceLocation;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export type Fact = Expression | { kind: "ModuleFact"; name: string; location: SourceLocation };
|
|
390
|
+
|
|
391
|
+
export interface UseOrHide {
|
|
392
|
+
kind: "Use" | "Hide";
|
|
393
|
+
isOnly: boolean;
|
|
394
|
+
facts: Fact[];
|
|
395
|
+
defs: Fact[];
|
|
396
|
+
location: SourceLocation;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
export type Proof =
|
|
400
|
+
| { kind: "Obvious" | "Omitted"; location: SourceLocation }
|
|
401
|
+
| { kind: "By"; isOnly: boolean; facts: Fact[]; defs: Fact[]; location: SourceLocation }
|
|
402
|
+
| { kind: "Steps"; level: number; steps: ProofStep[]; location: SourceLocation };
|
|
403
|
+
|
|
404
|
+
export interface DefineStep {
|
|
405
|
+
kind: "Define";
|
|
406
|
+
definitions: (Definition | Instance)[];
|
|
407
|
+
location: SourceLocation;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
export type StepBody =
|
|
411
|
+
| UseOrHide
|
|
412
|
+
| Instance
|
|
413
|
+
| DefineStep
|
|
414
|
+
| { kind: "Have"; expression: Expression }
|
|
415
|
+
| { kind: "Take"; names: string[]; bounds: Bound[] }
|
|
416
|
+
| { kind: "Witness"; expressions: Expression[] }
|
|
417
|
+
| { kind: "Pick"; names: string[]; bounds: Bound[]; body: Expression }
|
|
418
|
+
| { kind: "Case"; expression: Expression }
|
|
419
|
+
| { kind: "Assert"; isSuffices: boolean; statement: Expression | AssumeProve }
|
|
420
|
+
| { kind: "Qed" };
|
|
421
|
+
|
|
422
|
+
export interface ProofStep {
|
|
423
|
+
name: string | undefined;
|
|
424
|
+
body: StepBody;
|
|
425
|
+
proof: Proof | undefined;
|
|
426
|
+
location: SourceLocation;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
export interface Theorem {
|
|
430
|
+
name: string | undefined;
|
|
431
|
+
statement: Expression | AssumeProve;
|
|
432
|
+
proof: Proof | undefined;
|
|
433
|
+
location: SourceLocation;
|
|
359
434
|
}
|
|
360
435
|
|
|
361
436
|
export interface Module {
|
|
362
437
|
name: string;
|
|
363
|
-
outer: OuterScope | undefined;
|
|
364
438
|
modules: Module[];
|
|
365
439
|
extends: string[];
|
|
366
440
|
constants: Declaration[];
|
|
367
|
-
variables:
|
|
441
|
+
variables: Declaration[];
|
|
368
442
|
definitions: Definition[];
|
|
369
443
|
instances: Instance[];
|
|
370
444
|
assumptions: Assumption[];
|
|
445
|
+
location: SourceLocation;
|
|
446
|
+
theorems: Theorem[];
|
|
447
|
+
uses: UseOrHide[];
|
|
371
448
|
}
|
package/src/bags.ts
CHANGED
|
@@ -1,75 +1,118 @@
|
|
|
1
1
|
import {
|
|
2
|
+
createJavaOverrideError,
|
|
3
|
+
createOverflowError,
|
|
2
4
|
defineConstant,
|
|
3
5
|
defineHigherOrderOperator,
|
|
4
6
|
defineOperator,
|
|
5
|
-
|
|
7
|
+
getJavaClassName,
|
|
6
8
|
getOperator,
|
|
7
9
|
getValue,
|
|
10
|
+
isOutOfRange,
|
|
8
11
|
} from "./builtins";
|
|
9
|
-
import { TlaEvaluationError } from "./errors";
|
|
12
|
+
import { TlaEvaluationError, TlaModuleError } from "./errors";
|
|
10
13
|
import type { Builtin, BuiltinArgument } from "./evaluate";
|
|
11
14
|
import {
|
|
12
|
-
checkInteger,
|
|
13
15
|
createFunction,
|
|
16
|
+
createSet,
|
|
14
17
|
createSortedFunction,
|
|
15
|
-
createSortedSet,
|
|
16
18
|
formatValue,
|
|
19
|
+
getDomain,
|
|
17
20
|
isEnumerable,
|
|
18
21
|
isFunctionValue,
|
|
22
|
+
isSetValue,
|
|
19
23
|
toFiniteFunction,
|
|
24
|
+
toFiniteSet,
|
|
20
25
|
valuesEqual,
|
|
21
26
|
type FunctionValue,
|
|
22
27
|
type Value,
|
|
23
28
|
} from "./values";
|
|
24
29
|
|
|
25
|
-
const
|
|
26
|
-
new
|
|
30
|
+
const createWrongValueError = (name: string, expected: string, value: Value) =>
|
|
31
|
+
new TlaModuleError(
|
|
27
32
|
`Applying ${name} to the following value,\nwhich is not ${expected}:\n${formatValue(value)}`,
|
|
28
33
|
);
|
|
29
34
|
|
|
30
|
-
const
|
|
31
|
-
new
|
|
35
|
+
const createWrongArgumentError = (position: string, name: string, expected: string, value: Value) =>
|
|
36
|
+
new TlaModuleError(
|
|
32
37
|
`The ${position} argument of ${name} should be a ${expected}, but instead it is:\n${formatValue(value)}`,
|
|
33
38
|
);
|
|
34
39
|
|
|
35
|
-
const toFunction = (value: Value)
|
|
36
|
-
isFunctionValue(value) && (value.kind === "Function" || isEnumerable(value.domain))
|
|
37
|
-
? toFiniteFunction(value)
|
|
38
|
-
: undefined;
|
|
40
|
+
const toFunction = (value: Value) => (isFunctionValue(value) ? toFiniteFunction(value) : undefined);
|
|
39
41
|
|
|
40
42
|
const getFunction = (args: BuiltinArgument[], index: number, name: string) => {
|
|
41
43
|
const value = getValue(args, index, name);
|
|
42
|
-
const
|
|
43
|
-
if (!
|
|
44
|
-
return
|
|
44
|
+
const functionValue = toFunction(value);
|
|
45
|
+
if (!functionValue) throw createWrongValueError(name, "a function with a finite domain", value);
|
|
46
|
+
return functionValue;
|
|
45
47
|
};
|
|
46
48
|
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
+
const getBagArgument = (args: BuiltinArgument[], index: number, name: string) => {
|
|
50
|
+
const value = getValue(args, index, name);
|
|
51
|
+
const functionValue = toFunction(value);
|
|
52
|
+
if (!functionValue)
|
|
53
|
+
throw createWrongArgumentError(index === 0 ? "first" : "second", name, "bag", value);
|
|
54
|
+
return functionValue;
|
|
55
|
+
};
|
|
49
56
|
|
|
50
|
-
const
|
|
51
|
-
if (
|
|
52
|
-
return
|
|
57
|
+
const getFiniteElements = (set: Value, describeError: () => TlaModuleError) => {
|
|
58
|
+
if (!isSetValue(set) || !isEnumerable(set)) throw describeError();
|
|
59
|
+
return toFiniteSet(set).elements;
|
|
53
60
|
};
|
|
54
61
|
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
62
|
+
const isBag = (functionValue: FunctionValue) =>
|
|
63
|
+
functionValue.range.every((count) => typeof count === "number" && count > 0);
|
|
64
|
+
|
|
65
|
+
const findCount = (functionValue: FunctionValue, element: Value) => {
|
|
66
|
+
const position = functionValue.domain.findIndex((key) => valuesEqual(key, element));
|
|
67
|
+
return position === -1 ? undefined : functionValue.range[position];
|
|
58
68
|
};
|
|
59
69
|
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
70
|
+
const getMethod = (name: string, result: string, arity: number) =>
|
|
71
|
+
`public static ${result} tlc2.module.Bags.${name}(${Array.from({ length: arity }, () => "tlc2.value.impl.Value").join(",")})`;
|
|
72
|
+
|
|
73
|
+
const BAG_DIFF = getMethod("BagDiff", "tlc2.value.impl.Value", 2);
|
|
74
|
+
const SQ_SUBSETEQ = getMethod("SqSubseteq", "tlc2.value.IBoolValue", 2);
|
|
75
|
+
const BAG_UNION = getMethod("BagUnion", "tlc2.value.impl.Value", 1);
|
|
76
|
+
const BAG_OF_ALL = getMethod("BagOfAll", "tlc2.value.impl.Value", 2);
|
|
77
|
+
const BAG_CUP = getMethod("BagCup", "tlc2.value.impl.Value", 2);
|
|
78
|
+
const BAG_CARDINALITY = getMethod("BagCardinality", "tlc2.value.impl.IntValue", 1);
|
|
79
|
+
|
|
80
|
+
// Bags.java casts each count to IntValue, so a count that isn't an integer fails with Java's
|
|
81
|
+
// ClassCastException.
|
|
82
|
+
const toCount = (value: Value, method: string) => {
|
|
83
|
+
if (typeof value === "number") return value;
|
|
84
|
+
const javaClass = `tlc2.value.impl.${getJavaClassName(value)}`;
|
|
85
|
+
throw createJavaOverrideError(
|
|
86
|
+
method,
|
|
87
|
+
`class ${javaClass} cannot be cast to class tlc2.value.impl.IntValue (${javaClass} and tlc2.value.impl.IntValue are in unnamed module of loader 'app')`,
|
|
88
|
+
);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const addCounts = (left: Value, right: Value, method: string) => {
|
|
92
|
+
const sum = toCount(left, method) + toCount(right, method);
|
|
93
|
+
if (isOutOfRange(sum)) throw createOverflowError(`${left}+${right}`);
|
|
63
94
|
return sum;
|
|
64
95
|
};
|
|
65
96
|
|
|
66
|
-
|
|
97
|
+
// TLC subtracts counts as Java ints, which wrap on overflow.
|
|
98
|
+
const subtractCounts = (
|
|
99
|
+
functionValue: FunctionValue,
|
|
100
|
+
index: number,
|
|
101
|
+
other: FunctionValue,
|
|
102
|
+
method: string,
|
|
103
|
+
) => {
|
|
104
|
+
const removed = findCount(other, functionValue.domain[index]);
|
|
105
|
+
const count = toCount(functionValue.range[index], method);
|
|
106
|
+
return removed === undefined ? count : (count - toCount(removed, method)) | 0;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const mergeBags = (bags: FunctionValue[], method: string) =>
|
|
67
110
|
createFunction(
|
|
68
111
|
bags.reduce<[Value, Value][]>((pairs, bag) => {
|
|
69
112
|
bag.domain.forEach((key, index) => {
|
|
70
113
|
const existing = pairs.find((pair) => valuesEqual(pair[0], key));
|
|
71
114
|
if (existing) {
|
|
72
|
-
existing[1] = addCounts(
|
|
115
|
+
existing[1] = addCounts(existing[1], bag.range[index], method);
|
|
73
116
|
return;
|
|
74
117
|
}
|
|
75
118
|
pairs.push([key, bag.range[index]]);
|
|
@@ -78,107 +121,154 @@ const mergeBags = (bags: FunctionValue[]) =>
|
|
|
78
121
|
}, []),
|
|
79
122
|
);
|
|
80
123
|
|
|
124
|
+
const EMPTY_BAG = createSortedFunction([], []);
|
|
125
|
+
|
|
81
126
|
export const BAGS_OPERATORS: Builtin[] = [
|
|
82
|
-
defineConstant("EmptyBag",
|
|
127
|
+
defineConstant("EmptyBag", EMPTY_BAG),
|
|
83
128
|
defineOperator("IsABag", 1, (args) => {
|
|
84
|
-
const
|
|
85
|
-
return
|
|
129
|
+
const functionValue = toFunction(getValue(args, 0, "IsABag"));
|
|
130
|
+
return functionValue !== undefined && isBag(functionValue);
|
|
86
131
|
}),
|
|
87
132
|
defineOperator("BagCardinality", 1, (args) => {
|
|
88
133
|
const bag = getValue(args, 0, "BagCardinality");
|
|
89
|
-
const
|
|
90
|
-
return
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
);
|
|
134
|
+
const functionValue = getFunction(args, 0, "BagCardinality");
|
|
135
|
+
return functionValue.range.reduce<number>((total, count) => {
|
|
136
|
+
if (typeof count !== "number" || count <= 0) {
|
|
137
|
+
throw createWrongValueError("BagCardinality", "a bag", bag);
|
|
138
|
+
}
|
|
139
|
+
return addCounts(total, count, BAG_CARDINALITY);
|
|
140
|
+
}, 0);
|
|
97
141
|
}),
|
|
98
142
|
defineOperator("BagIn", 2, (args) => {
|
|
99
143
|
const bag = getValue(args, 1, "BagIn");
|
|
100
144
|
const count = findCount(getFunction(args, 1, "BagIn"), getValue(args, 0, "BagIn"));
|
|
101
145
|
if (count === undefined) return false;
|
|
102
|
-
if (typeof count !== "number") throw
|
|
146
|
+
if (typeof count !== "number") throw createWrongArgumentError("second", "BagIn", "bag", bag);
|
|
103
147
|
return count > 0;
|
|
104
148
|
}),
|
|
105
149
|
defineOperator("CopiesIn", 2, (args) => {
|
|
106
150
|
const bag = getValue(args, 1, "CopiesIn");
|
|
107
151
|
const count = findCount(getFunction(args, 1, "CopiesIn"), getValue(args, 0, "CopiesIn"));
|
|
108
152
|
if (count === undefined) return 0;
|
|
109
|
-
if (typeof count !== "number") throw
|
|
153
|
+
if (typeof count !== "number") throw createWrongArgumentError("second", "CopiesIn", "bag", bag);
|
|
110
154
|
return count;
|
|
111
155
|
}),
|
|
112
156
|
defineOperator("\\oplus", 2, (args) => {
|
|
113
157
|
const bags = [0, 1].map((index) => {
|
|
114
|
-
const
|
|
115
|
-
if (!isBag(
|
|
116
|
-
throw
|
|
158
|
+
const functionValue = getBagArgument(args, index, "(+)");
|
|
159
|
+
if (!isBag(functionValue)) {
|
|
160
|
+
throw createWrongArgumentError(
|
|
117
161
|
index === 0 ? "first" : "second",
|
|
118
162
|
"(+)",
|
|
119
163
|
"bag",
|
|
120
164
|
getValue(args, index, "(+)"),
|
|
121
165
|
);
|
|
122
166
|
}
|
|
123
|
-
return
|
|
167
|
+
return functionValue;
|
|
124
168
|
});
|
|
125
|
-
return mergeBags(bags);
|
|
169
|
+
return mergeBags(bags, BAG_CUP);
|
|
126
170
|
}),
|
|
127
171
|
defineOperator("\\ominus", 2, (args) => {
|
|
128
|
-
const left =
|
|
129
|
-
const right =
|
|
172
|
+
const left = getBagArgument(args, 0, "(-)");
|
|
173
|
+
const right = getBagArgument(args, 1, "(-)");
|
|
130
174
|
return createFunction(
|
|
131
175
|
left.domain.flatMap((key, index): [Value, Value][] => {
|
|
132
|
-
const
|
|
133
|
-
const count = Number(left.range[index]) - (removed === undefined ? 0 : Number(removed));
|
|
176
|
+
const count = subtractCounts(left, index, right, BAG_DIFF);
|
|
134
177
|
return count > 0 ? [[key, count]] : [];
|
|
135
178
|
}),
|
|
136
179
|
);
|
|
137
180
|
}),
|
|
138
181
|
defineOperator("BagUnion", 1, (args) => {
|
|
139
182
|
const set = getValue(args, 0, "BagUnion");
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
183
|
+
const elements = getFiniteElements(set, () =>
|
|
184
|
+
createWrongValueError("BagUnion", "a finite enumerable set", set),
|
|
185
|
+
);
|
|
186
|
+
if (elements.length < 2) return elements[0] ?? EMPTY_BAG;
|
|
187
|
+
const bags = elements.map((element) => {
|
|
188
|
+
const functionValue = toFunction(element);
|
|
189
|
+
if (!functionValue) {
|
|
190
|
+
throw new TlaModuleError(
|
|
144
191
|
`Attempted to apply BagUnion to the following set, whose\nelement is not a bag:\n${formatValue(set)}`,
|
|
145
192
|
);
|
|
146
193
|
}
|
|
147
|
-
return
|
|
194
|
+
return functionValue;
|
|
148
195
|
});
|
|
149
|
-
|
|
150
|
-
return mergeBags(bags);
|
|
196
|
+
return mergeBags(bags, BAG_UNION);
|
|
151
197
|
}),
|
|
152
198
|
defineOperator("\\sqsubseteq", 2, (args) => {
|
|
153
199
|
const left = getFunction(args, 0, "\\sqsubseteq");
|
|
154
200
|
const right = getFunction(args, 1, "\\sqsubseteq");
|
|
155
|
-
return left.domain.every((
|
|
156
|
-
const count = findCount(right, key);
|
|
157
|
-
return Number(left.range[index]) - (count === undefined ? 0 : Number(count)) <= 0;
|
|
158
|
-
});
|
|
201
|
+
return left.domain.every((_, index) => subtractCounts(left, index, right, SQ_SUBSETEQ) <= 0);
|
|
159
202
|
}),
|
|
160
203
|
defineHigherOrderOperator("BagOfAll", 2, (args) => {
|
|
161
204
|
const operator = getOperator(args, 0, "BagOfAll");
|
|
205
|
+
// TLC passes an operator whose body is a number or string literal to Bags.java as that literal.
|
|
206
|
+
if (operator.literalBody !== undefined) {
|
|
207
|
+
throw new TlaModuleError(
|
|
208
|
+
`The first argument of BagOfAll should be an operator, but instead it is:\n${formatValue(operator.literalBody)}`,
|
|
209
|
+
);
|
|
210
|
+
}
|
|
162
211
|
const value = getValue(args, 1, "BagOfAll");
|
|
163
|
-
const
|
|
164
|
-
if (!
|
|
165
|
-
throw
|
|
212
|
+
const functionValue = toFunction(value);
|
|
213
|
+
if (!functionValue) {
|
|
214
|
+
throw createWrongArgumentError("second", "BagOfAll", "function with a finite domain", value);
|
|
166
215
|
}
|
|
167
216
|
return mergeBags(
|
|
168
|
-
|
|
169
|
-
createSortedFunction([operator.call([key])], [
|
|
217
|
+
functionValue.domain.map((key, index) =>
|
|
218
|
+
createSortedFunction([operator.call([key])], [functionValue.range[index]]),
|
|
170
219
|
),
|
|
220
|
+
BAG_OF_ALL,
|
|
171
221
|
);
|
|
172
222
|
}),
|
|
173
223
|
defineOperator("SubBag", 1, (args) => {
|
|
174
|
-
|
|
175
|
-
|
|
224
|
+
const value = getValue(args, 0, "SubBag");
|
|
225
|
+
if (!isFunctionValue(value)) {
|
|
226
|
+
throw new TlaEvaluationError(`DOMAIN expects a function but received ${formatValue(value)}`);
|
|
227
|
+
}
|
|
228
|
+
const functionValue = toFiniteFunction(value);
|
|
229
|
+
const range = createSet(functionValue.range).elements;
|
|
230
|
+
const invalid = range.find((count) => typeof count !== "number");
|
|
231
|
+
if (invalid !== undefined) {
|
|
232
|
+
const isFirst = typeof range[0] !== "number";
|
|
233
|
+
throw new TlaEvaluationError(
|
|
234
|
+
`The ${isFirst ? "first" : "second"} argument of >= should be an integer, but instead it is:\n${formatValue(isFirst ? range[0] : invalid)}`,
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
const counts = functionValue.range.filter((count) => typeof count === "number");
|
|
238
|
+
if (counts.some((count) => count < 1)) {
|
|
239
|
+
throw new TlaEvaluationError(
|
|
240
|
+
`Attempted to apply a function to ${formatValue(functionValue)}, which is not in its domain`,
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
return createSet(
|
|
244
|
+
functionValue.domain
|
|
245
|
+
.reduce<[Value, Value][][]>(
|
|
246
|
+
(subBags, key, index) =>
|
|
247
|
+
subBags.flatMap((entries) => [
|
|
248
|
+
entries,
|
|
249
|
+
...Array.from({ length: counts[index] }, (_, copies): [Value, Value][] => [
|
|
250
|
+
...entries,
|
|
251
|
+
[key, copies + 1],
|
|
252
|
+
]),
|
|
253
|
+
]),
|
|
254
|
+
[[]],
|
|
255
|
+
)
|
|
256
|
+
.map((entries) =>
|
|
257
|
+
createSortedFunction(
|
|
258
|
+
entries.map((entry) => entry[0]),
|
|
259
|
+
entries.map((entry) => entry[1]),
|
|
260
|
+
),
|
|
261
|
+
),
|
|
262
|
+
);
|
|
176
263
|
}),
|
|
177
|
-
defineOperator("BagToSet", 1, (args) =>
|
|
264
|
+
defineOperator("BagToSet", 1, (args) => getDomain(getFunction(args, 0, "BagToSet"))),
|
|
178
265
|
defineOperator("SetToBag", 1, (args) => {
|
|
179
|
-
const
|
|
266
|
+
const set = getValue(args, 0, "SetToBag");
|
|
267
|
+
const elements = getFiniteElements(set, () =>
|
|
268
|
+
createWrongValueError("BagToSet", "a function with a finite domain", set),
|
|
269
|
+
);
|
|
180
270
|
return createSortedFunction(
|
|
181
|
-
elements,
|
|
271
|
+
[...elements],
|
|
182
272
|
elements.map(() => 1),
|
|
183
273
|
);
|
|
184
274
|
}),
|