tla-ts 0.0.2 → 0.0.3-dev.3cf2ad0
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 +63 -11
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +51 -1
- package/dist/index.d.ts +51 -1
- 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 -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/fingerprints.ts +46 -11
- 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 +163 -211
- package/src/parallel.ts +58 -33
- package/src/parser.ts +285 -52
- package/src/run-cli.ts +295 -27
- package/src/standard-levels.ts +3 -3
- package/src/symmetry.ts +5 -1
- package/src/transfer.ts +22 -1
- package/src/values.ts +59 -23
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tla-ts",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3-dev.3cf2ad0",
|
|
4
4
|
"description": "A TLA+ interpreter and model checker in TypeScript, 2× faster than TLC",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"formal verification",
|
|
@@ -71,6 +71,8 @@
|
|
|
71
71
|
"conformance:sany": "tsx scripts/proof-conformance.ts",
|
|
72
72
|
"conformance:record": "tsx scripts/record-tlc.ts",
|
|
73
73
|
"conformance:record-sany": "tsx scripts/record-sany.ts",
|
|
74
|
+
"conformance:record-tlapm": "tsx scripts/record-tlapm.ts",
|
|
75
|
+
"conformance:tlapm": "tsx scripts/tlapm-conformance.ts",
|
|
74
76
|
"publint": "publint",
|
|
75
77
|
"test": "vp test",
|
|
76
78
|
"typecheck": "tsc --noEmit"
|
package/src/ast.ts
CHANGED
|
@@ -44,6 +44,9 @@ export interface ApplyExpression {
|
|
|
44
44
|
kind: "Apply";
|
|
45
45
|
operator: string;
|
|
46
46
|
args: Expression[];
|
|
47
|
+
// The number of args each part of a name like F(x)!l(y) takes, when a part before the last takes
|
|
48
|
+
// some.
|
|
49
|
+
argumentCounts?: number[];
|
|
47
50
|
location: SourceLocation;
|
|
48
51
|
resolution?: LocalResolution;
|
|
49
52
|
}
|
|
@@ -64,6 +67,7 @@ export interface LambdaExpression {
|
|
|
64
67
|
export interface JunctionExpression {
|
|
65
68
|
kind: "And" | "Or";
|
|
66
69
|
operands: Expression[];
|
|
70
|
+
isBulleted?: boolean;
|
|
67
71
|
location: SourceLocation;
|
|
68
72
|
}
|
|
69
73
|
|
|
@@ -129,6 +133,7 @@ export interface QuantifierExpression {
|
|
|
129
133
|
|
|
130
134
|
export interface UnboundedQuantifierExpression {
|
|
131
135
|
kind: "UnboundedQuantifier";
|
|
136
|
+
quantifier: "Forall" | "Exists";
|
|
132
137
|
names: string[];
|
|
133
138
|
isTemporal?: boolean;
|
|
134
139
|
body: Expression;
|
|
@@ -317,6 +322,7 @@ export type Expression =
|
|
|
317
322
|
export interface OperatorParam {
|
|
318
323
|
name: string;
|
|
319
324
|
arity: number;
|
|
325
|
+
location?: SourceLocation;
|
|
320
326
|
}
|
|
321
327
|
|
|
322
328
|
export interface OperatorDefinition {
|
|
@@ -368,6 +374,8 @@ export interface Instance {
|
|
|
368
374
|
substitutions: InstanceSubstitution[];
|
|
369
375
|
isLocal: boolean;
|
|
370
376
|
location: SourceLocation;
|
|
377
|
+
instanceRange: SourceRange;
|
|
378
|
+
definitionRange?: SourceRange;
|
|
371
379
|
}
|
|
372
380
|
|
|
373
381
|
export interface NewSymbol {
|
|
@@ -388,18 +396,33 @@ export interface AssumeProve {
|
|
|
388
396
|
|
|
389
397
|
export type Fact = Expression | { kind: "ModuleFact"; name: string; location: SourceLocation };
|
|
390
398
|
|
|
399
|
+
// keyword is the range of the USE or HIDE token and any ONLY after it.
|
|
391
400
|
export interface UseOrHide {
|
|
392
401
|
kind: "Use" | "Hide";
|
|
393
402
|
isOnly: boolean;
|
|
394
403
|
facts: Fact[];
|
|
395
404
|
defs: Fact[];
|
|
405
|
+
keyword: SourceRange;
|
|
396
406
|
location: SourceLocation;
|
|
397
407
|
}
|
|
398
408
|
|
|
409
|
+
// keyword is the range of the BY, OBVIOUS, or OMITTED token.
|
|
399
410
|
export type Proof =
|
|
400
|
-
| { kind: "Obvious" | "Omitted"; location: SourceLocation }
|
|
401
|
-
| {
|
|
402
|
-
|
|
411
|
+
| { kind: "Obvious" | "Omitted"; keyword: SourceRange; location: SourceLocation }
|
|
412
|
+
| {
|
|
413
|
+
kind: "By";
|
|
414
|
+
isOnly: boolean;
|
|
415
|
+
facts: Fact[];
|
|
416
|
+
defs: Fact[];
|
|
417
|
+
keyword: SourceRange;
|
|
418
|
+
location: SourceLocation;
|
|
419
|
+
}
|
|
420
|
+
| {
|
|
421
|
+
kind: "Steps";
|
|
422
|
+
level: number;
|
|
423
|
+
steps: ProofStep[];
|
|
424
|
+
location: SourceLocation;
|
|
425
|
+
};
|
|
403
426
|
|
|
404
427
|
export interface DefineStep {
|
|
405
428
|
kind: "Define";
|
|
@@ -419,9 +442,12 @@ export type StepBody =
|
|
|
419
442
|
| { kind: "Assert"; isSuffices: boolean; statement: Expression | AssumeProve }
|
|
420
443
|
| { kind: "Qed" };
|
|
421
444
|
|
|
445
|
+
// head is the range of the step number and its statement, without the proof.
|
|
422
446
|
export interface ProofStep {
|
|
423
447
|
name: string | undefined;
|
|
448
|
+
token: string;
|
|
424
449
|
body: StepBody;
|
|
450
|
+
head: SourceRange;
|
|
425
451
|
proof: Proof | undefined;
|
|
426
452
|
location: SourceLocation;
|
|
427
453
|
}
|
|
@@ -433,10 +459,23 @@ export interface Theorem {
|
|
|
433
459
|
location: SourceLocation;
|
|
434
460
|
}
|
|
435
461
|
|
|
462
|
+
// The units of a module in source order, for consumers that need the order across kinds.
|
|
463
|
+
export type ModuleUnit =
|
|
464
|
+
| { kind: "Constants"; declarations: Declaration[] }
|
|
465
|
+
| { kind: "Variables"; declarations: Declaration[] }
|
|
466
|
+
| { kind: "Recursive"; declarations: Declaration[] }
|
|
467
|
+
| { kind: "Definition"; definition: Definition }
|
|
468
|
+
| { kind: "Instance"; instance: Instance }
|
|
469
|
+
| { kind: "Assumption"; assumption: Assumption }
|
|
470
|
+
| { kind: "Theorem"; theorem: Theorem }
|
|
471
|
+
| { kind: "Use"; use: UseOrHide }
|
|
472
|
+
| { kind: "Module"; module: Module };
|
|
473
|
+
|
|
436
474
|
export interface Module {
|
|
437
475
|
name: string;
|
|
438
476
|
modules: Module[];
|
|
439
477
|
extends: string[];
|
|
478
|
+
extendsRanges: SourceRange[];
|
|
440
479
|
constants: Declaration[];
|
|
441
480
|
variables: Declaration[];
|
|
442
481
|
definitions: Definition[];
|
|
@@ -445,4 +484,5 @@ export interface Module {
|
|
|
445
484
|
location: SourceLocation;
|
|
446
485
|
theorems: Theorem[];
|
|
447
486
|
uses: UseOrHide[];
|
|
487
|
+
units: ModuleUnit[];
|
|
448
488
|
}
|
package/src/check.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
getDefinition,
|
|
14
14
|
isConstantLevel,
|
|
15
15
|
isImpure,
|
|
16
|
+
isLocallyResolved,
|
|
16
17
|
type Context,
|
|
17
18
|
type Environment,
|
|
18
19
|
type Scope,
|
|
@@ -54,6 +55,7 @@ import {
|
|
|
54
55
|
getValueHash,
|
|
55
56
|
isFunctionValue,
|
|
56
57
|
isSameValue,
|
|
58
|
+
isUpdatedElementOf,
|
|
57
59
|
setStringOrder,
|
|
58
60
|
toFiniteFunction,
|
|
59
61
|
type Value,
|
|
@@ -139,6 +141,13 @@ interface NamedExpression {
|
|
|
139
141
|
|
|
140
142
|
interface StateCheck extends NamedExpression {
|
|
141
143
|
variables: number[] | undefined;
|
|
144
|
+
membership: Membership | undefined;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// A conjunct `x \in S` whose S reads no variables.
|
|
148
|
+
interface Membership {
|
|
149
|
+
variable: number;
|
|
150
|
+
set: Expression;
|
|
142
151
|
}
|
|
143
152
|
|
|
144
153
|
interface ActionProperty {
|
|
@@ -481,14 +490,32 @@ const getPossiblePredicates = (names: string[], environment: Environment) =>
|
|
|
481
490
|
return { name, expression, scope: undefined };
|
|
482
491
|
});
|
|
483
492
|
|
|
493
|
+
const getMembership = (
|
|
494
|
+
expression: Expression,
|
|
495
|
+
variables: number[] | undefined,
|
|
496
|
+
environment: Environment,
|
|
497
|
+
): Membership | undefined => {
|
|
498
|
+
if (expression.kind !== "Apply" || expression.operator !== "\\in" || variables?.length !== 1)
|
|
499
|
+
return undefined;
|
|
500
|
+
const element = expression.args[0];
|
|
501
|
+
if (element.kind !== "Identifier" || isLocallyResolved(element)) return undefined;
|
|
502
|
+
return environment.variableIndex.get(element.name) === variables[0]
|
|
503
|
+
? { variable: variables[0], set: expression.args[1] }
|
|
504
|
+
: undefined;
|
|
505
|
+
};
|
|
506
|
+
|
|
484
507
|
const getStateChecks = (named: NamedExpression[], environment: Environment): StateCheck[] =>
|
|
485
508
|
named.flatMap((item) =>
|
|
486
|
-
getConjuncts(item.expression, environment).map((expression) =>
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
509
|
+
getConjuncts(item.expression, environment).map((expression) => {
|
|
510
|
+
const variables = getReadVariables(expression, environment);
|
|
511
|
+
return {
|
|
512
|
+
name: item.name,
|
|
513
|
+
expression,
|
|
514
|
+
scope: item.scope,
|
|
515
|
+
variables,
|
|
516
|
+
membership: getMembership(expression, variables, environment),
|
|
517
|
+
};
|
|
518
|
+
}),
|
|
492
519
|
);
|
|
493
520
|
|
|
494
521
|
const isUnchangedFor = (stateCheck: StateCheck, state: Value[], parent: Value[] | undefined) => {
|
|
@@ -743,10 +770,16 @@ export const createSearch = (
|
|
|
743
770
|
invariants.find((invariant) => {
|
|
744
771
|
if (isUnchangedFor(invariant, state, parent)) return false;
|
|
745
772
|
try {
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
773
|
+
const context = getContext(environment, state, undefined, invariant.scope);
|
|
774
|
+
const membership = invariant.membership;
|
|
775
|
+
// The parent satisfies every invariant, so only the entries the step changed can fail.
|
|
776
|
+
return parent && membership
|
|
777
|
+
? !isUpdatedElementOf(
|
|
778
|
+
state[membership.variable],
|
|
779
|
+
parent[membership.variable],
|
|
780
|
+
evaluate(membership.set, context),
|
|
781
|
+
)
|
|
782
|
+
: !holdsLikeTlc(invariant.expression, context);
|
|
750
783
|
} catch (error) {
|
|
751
784
|
// TLC reports a failure only on a state after the initial states as an invariant failure.
|
|
752
785
|
if (!parent) throw error;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export interface OptionSpec {
|
|
2
|
+
flag: string;
|
|
3
|
+
isRequired?: boolean;
|
|
4
|
+
isRepeatable?: boolean;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface ParsedOption {
|
|
8
|
+
name: string;
|
|
9
|
+
values: string[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface ParsedArgs {
|
|
13
|
+
options: ParsedOption[];
|
|
14
|
+
positionals: string[];
|
|
15
|
+
unknown: string[];
|
|
16
|
+
incomplete: ParsedOption | undefined;
|
|
17
|
+
has: (name: string) => boolean;
|
|
18
|
+
get: (name: string) => string[] | undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const formatOption = (spec: OptionSpec) => {
|
|
22
|
+
const text = spec.isRequired ? spec.flag : `[${spec.flag}]`;
|
|
23
|
+
return spec.isRepeatable ? `${text}...` : text;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// Each <name> in a flag such as "--toolbox <start> <end>" is one value the option takes, and
|
|
27
|
+
// {options} in the usage stands for the options.
|
|
28
|
+
export const defineCommand = (usage: string, specs: OptionSpec[]) => {
|
|
29
|
+
const arities = new Map(
|
|
30
|
+
specs.map((spec) => {
|
|
31
|
+
const [name, ...values] = spec.flag.split(" ");
|
|
32
|
+
return [name, values.length];
|
|
33
|
+
}),
|
|
34
|
+
);
|
|
35
|
+
const parse = (args: string[]): ParsedArgs => {
|
|
36
|
+
const options: ParsedOption[] = [];
|
|
37
|
+
const positionals: string[] = [];
|
|
38
|
+
const unknown: string[] = [];
|
|
39
|
+
for (let index = 0; index < args.length; index++) {
|
|
40
|
+
const arity = arities.get(args[index]);
|
|
41
|
+
if (arity === undefined) {
|
|
42
|
+
(args[index].startsWith("-") ? unknown : positionals).push(args[index]);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
options.push({ name: args[index], values: args.slice(index + 1, index + 1 + arity) });
|
|
46
|
+
index += arity;
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
options,
|
|
50
|
+
positionals,
|
|
51
|
+
unknown,
|
|
52
|
+
incomplete: options.find((option) => option.values.length < (arities.get(option.name) ?? 0)),
|
|
53
|
+
has: (name) => options.some((option) => option.name === name),
|
|
54
|
+
get: (name) => options.findLast((option) => option.name === name)?.values,
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
return { usage: usage.replace("{options}", specs.map(formatOption).join(" ")), parse };
|
|
58
|
+
};
|
package/src/errors.ts
CHANGED
|
@@ -7,10 +7,20 @@ export class TlaError extends Error {
|
|
|
7
7
|
}
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
export interface SyntaxFailure {
|
|
11
|
+
reason: string;
|
|
12
|
+
line: number;
|
|
13
|
+
column: number;
|
|
14
|
+
isEnd: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
10
17
|
export class TlaSyntaxError extends TlaError {
|
|
11
|
-
|
|
18
|
+
failure: SyntaxFailure | undefined;
|
|
19
|
+
|
|
20
|
+
constructor(message: string, cause?: unknown, failure?: SyntaxFailure) {
|
|
12
21
|
super(message, cause);
|
|
13
22
|
this.name = "TlaSyntaxError";
|
|
23
|
+
this.failure = failure;
|
|
14
24
|
}
|
|
15
25
|
}
|
|
16
26
|
|
|
@@ -38,6 +48,32 @@ export class TlaModuleError extends TlaEvaluationError {
|
|
|
38
48
|
}
|
|
39
49
|
}
|
|
40
50
|
|
|
51
|
+
export interface TlapmReport {
|
|
52
|
+
failure?: string;
|
|
53
|
+
exception?: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export class TlaProofError extends TlaError {
|
|
57
|
+
failure: string | undefined;
|
|
58
|
+
exception: string;
|
|
59
|
+
|
|
60
|
+
constructor(message: string, report: TlapmReport = {}) {
|
|
61
|
+
super(message);
|
|
62
|
+
this.name = "TlaProofError";
|
|
63
|
+
this.failure = report.exception === undefined ? (report.failure ?? message) : undefined;
|
|
64
|
+
this.exception = report.exception ?? `Failure(${JSON.stringify(this.failure)})`;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// tlapm fails one of its own assertions, which it reports as a bug in itself. It keeps the name
|
|
69
|
+
// TlaProofError, and index.ts doesn't export it.
|
|
70
|
+
export class TlapmBugError extends TlaProofError {
|
|
71
|
+
constructor(exception = "Assert_failure") {
|
|
72
|
+
super("Oops, this seems to be a bug in TLAPM.", { exception });
|
|
73
|
+
this.name = "TlaProofError";
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
41
77
|
export class TlaUnsupportedError extends TlaError {
|
|
42
78
|
constructor(message: string, cause?: unknown) {
|
|
43
79
|
super(message, cause);
|
package/src/evaluate.ts
CHANGED
|
@@ -734,7 +734,9 @@ const compileArgumentBinder = (argument: Expression, environment: Environment):
|
|
|
734
734
|
if (global === undefined) {
|
|
735
735
|
return (context) => {
|
|
736
736
|
const binding = lookupScope(context.scope, argument.name);
|
|
737
|
-
return binding?.kind === "Value"
|
|
737
|
+
return binding?.kind === "Value" || binding?.kind === "Thunk"
|
|
738
|
+
? binding
|
|
739
|
+
: createThunk(argument, context);
|
|
738
740
|
};
|
|
739
741
|
}
|
|
740
742
|
}
|
package/src/fingerprints.ts
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { getValueFingerprint, getValueHash, type Value } from "./values";
|
|
2
2
|
|
|
3
3
|
const EMPTY = 0;
|
|
4
|
-
const TOMBSTONE = 1;
|
|
5
4
|
const INITIAL_CAPACITY = 1 << 12;
|
|
6
5
|
|
|
7
6
|
interface FingerprintTable {
|
|
8
7
|
insert: (first: number, second: number) => boolean;
|
|
9
|
-
remove: (first: number, second: number) => void;
|
|
10
8
|
}
|
|
11
9
|
|
|
12
10
|
const mix = (hash: number) => {
|
|
@@ -26,15 +24,13 @@ export const writeStateFingerprint = (state: Value[], fingerprint: Int32Array) =
|
|
|
26
24
|
fingerprint[1] = mix(second);
|
|
27
25
|
};
|
|
28
26
|
|
|
29
|
-
export const getFingerprintOwner = (second: number, count: number) => (second >>> 0) % count;
|
|
30
|
-
|
|
31
27
|
export const createFingerprintTable = (): FingerprintTable => {
|
|
32
28
|
const table = { slots: new Int32Array(INITIAL_CAPACITY * 2), used: 0 };
|
|
33
29
|
const findSlot = (slots: Int32Array, first: number, second: number) => {
|
|
34
30
|
const mask = slots.length / 2 - 1;
|
|
35
31
|
for (let index = first & mask; ; index = (index + 1) & mask) {
|
|
36
32
|
const stored = slots[index * 2];
|
|
37
|
-
if (stored === EMPTY
|
|
33
|
+
if (stored === EMPTY) return index;
|
|
38
34
|
if (stored === first && slots[index * 2 + 1] === second) return index;
|
|
39
35
|
}
|
|
40
36
|
};
|
|
@@ -59,11 +55,50 @@ export const createFingerprintTable = (): FingerprintTable => {
|
|
|
59
55
|
table.used++;
|
|
60
56
|
return true;
|
|
61
57
|
},
|
|
62
|
-
remove: (first, second) => {
|
|
63
|
-
const slot = findSlot(table.slots, first, second);
|
|
64
|
-
if (table.slots[slot * 2] !== first) return;
|
|
65
|
-
table.slots[slot * 2] = EMPTY;
|
|
66
|
-
table.slots[slot * 2 + 1] = TOMBSTONE;
|
|
67
|
-
},
|
|
68
58
|
};
|
|
69
59
|
};
|
|
60
|
+
|
|
61
|
+
// Workers share one table of 64-bit fingerprints. The first word is never 0, so 0 marks an empty slot.
|
|
62
|
+
export interface SharedFingerprints {
|
|
63
|
+
slots: BigInt64Array;
|
|
64
|
+
used: Int32Array;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const INITIAL_SHARED_CAPACITY = 1 << 10;
|
|
68
|
+
|
|
69
|
+
const insertSharedKey = (slots: BigInt64Array, key: bigint) => {
|
|
70
|
+
const mask = slots.length - 1;
|
|
71
|
+
for (let index = Number(key >> 32n) & mask; ; index = (index + 1) & mask) {
|
|
72
|
+
const stored = Atomics.load(slots, index);
|
|
73
|
+
if (stored === key) return false;
|
|
74
|
+
if (stored !== 0n) continue;
|
|
75
|
+
const previous = Atomics.compareExchange(slots, index, 0n, key);
|
|
76
|
+
if (previous === 0n) return true;
|
|
77
|
+
if (previous === key) return false;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const createSharedFingerprints = (): SharedFingerprints => ({
|
|
82
|
+
slots: new BigInt64Array(new SharedArrayBuffer(INITIAL_SHARED_CAPACITY * 8)),
|
|
83
|
+
used: new Int32Array(new SharedArrayBuffer(4)),
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
export const insertSharedFingerprint = (
|
|
87
|
+
table: SharedFingerprints,
|
|
88
|
+
first: number,
|
|
89
|
+
second: number,
|
|
90
|
+
) => {
|
|
91
|
+
const key = BigInt.asIntN(64, (BigInt(first) << 32n) | BigInt(second >>> 0));
|
|
92
|
+
if (!insertSharedKey(table.slots, key)) return false;
|
|
93
|
+
Atomics.add(table.used, 0, 1);
|
|
94
|
+
return true;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export const isSharedFingerprintsFull = (table: SharedFingerprints) =>
|
|
98
|
+
Atomics.load(table.used, 0) * 2 >= table.slots.length;
|
|
99
|
+
|
|
100
|
+
export const growSharedFingerprints = (table: SharedFingerprints): SharedFingerprints => {
|
|
101
|
+
const slots = new BigInt64Array(new SharedArrayBuffer(table.slots.byteLength * 2));
|
|
102
|
+
for (const key of table.slots) if (key !== 0n) insertSharedKey(slots, key);
|
|
103
|
+
return { slots, used: table.used };
|
|
104
|
+
};
|
package/src/lexer.ts
CHANGED
|
@@ -380,7 +380,12 @@ export const tokenize = (source: string): Token[] => {
|
|
|
380
380
|
}
|
|
381
381
|
};
|
|
382
382
|
const createError = (message: string) =>
|
|
383
|
-
new TlaSyntaxError(`${message} at line ${cursor.line}, column ${getColumn()}
|
|
383
|
+
new TlaSyntaxError(`${message} at line ${cursor.line}, column ${getColumn()}`, undefined, {
|
|
384
|
+
reason: message,
|
|
385
|
+
line: cursor.line,
|
|
386
|
+
column: getColumn(),
|
|
387
|
+
isEnd: cursor.index >= source.length,
|
|
388
|
+
});
|
|
384
389
|
|
|
385
390
|
while (cursor.index < source.length) {
|
|
386
391
|
const character = source[cursor.index];
|