tla-rs 0.0.1 → 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 chenglong
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,192 @@
1
+ <h1 align="center">tla-rs</h1>
2
+
3
+ <p align="center">
4
+ <b>A fast TLA+ model checker in Rust that gives the same answers as TLC, state for state.</b>
5
+ </p>
6
+
7
+ <p align="center">
8
+ <a href="LICENSE"><img alt="License" src="https://img.shields.io/badge/license-MIT-blue"></a>
9
+ <img alt="Rust" src="https://img.shields.io/badge/rust-1.85%2B-orange">
10
+ <img alt="Conformance" src="https://img.shields.io/badge/TLC%20conformance-766%2F772%20match%2C%200%20mismatch-brightgreen">
11
+ </p>
12
+
13
+ <p align="center">
14
+ English | <a href="README.zh-CN.md">简体中文</a>
15
+ </p>
16
+
17
+ ---
18
+
19
+ ```console
20
+ $ tla-rs MCLamportMutex.tla -workers 4
21
+ Running breadth-first search Model-Checking with fp 0 and seed 0 with 4 workers on 11 cores ...
22
+ Computing initial states...
23
+ Finished computing initial states: 1 distinct state generated at 2026-11-05 14:35:25.
24
+ Model checking completed. No error has been found.
25
+ 2729079 states generated, 724274 distinct states found, 0 states left on queue.
26
+ The depth of the complete state graph search is 61.
27
+ Finished in 04s at (2026-11-05 14:35:30)
28
+ ```
29
+
30
+ ## Highlights
31
+
32
+ - 🎯 **TLC's answers, exactly.** tla-rs gives the same verdict, violation name, states generated, distinct states, depth and trace length as TLC. It was checked against 823 recorded TLC runs: no model disagrees, and every model with a TLC verdict matches unless it hits the time limit.
33
+ - ⚡ **No JVM, fast from the first state.** A small spec is checked in a few milliseconds; TLC's median time on the 823 recorded models is about a second, most of it JVM startup. Large models are 2 to 3× faster than TLC on one worker.
34
+ - 🧵 **Parallel and deterministic.** `-workers N` gives the same counts as one worker, on every run. That holds even when `SYMMETRY` or `VIEW` makes which state is kept matter.
35
+ - 🔁 **A drop-in TLC command line.** tla-rs takes TLC's options and configuration files, prints TLC's messages and counterexample traces, and returns TLC's exit codes. Existing scripts and CI jobs keep working.
36
+ - 🧩 **The full language, not a subset.** It covers `INSTANCE`, recursion, `ENABLED`, `CHOOSE`, liveness and fairness, `SYMMETRY`, `VIEW` and constraints. It includes the standard modules and the Java-backed operators of TLC, TLCExt and CommunityModules.
37
+ - 🪶 **Small and self-contained.** tla-rs is one 8 MB binary. A state costs about 30 bytes besides the queue, and states are told apart by 128-bit fingerprints, twice the width of TLC's.
38
+
39
+ ## Performance
40
+
41
+ The TLC times below are TLC's own times recorded with `-workers 1`. tla-rs was timed on a local machine, not the one TLC ran on, with a 60 s limit per model.
42
+
43
+ | Model size (TLC time) | Models | Median speedup, 1 worker |
44
+ | --- | ---: | ---: |
45
+ | < 2 s | 624 | 73× |
46
+ | 2 – 10 s | 91 | 17× |
47
+ | 10 – 60 s | 33 | 3.1× |
48
+ | > 60 s | 9 | 2.8× |
49
+
50
+ Speedups on small models come mostly from not starting a JVM. Speedups on large models come from the checker itself.
51
+
52
+ Some large models:
53
+
54
+ | Model | Distinct states | TLC | tla-rs, 1 worker | tla-rs, 4 workers |
55
+ | --- | ---: | ---: | ---: | ---: |
56
+ | Detlefs | 952,912 | 82.3 s | 31.7 s | **9.4 s** |
57
+ | Crossword_MC | 485,988 | 120.1 s | 50.8 s | **16.8 s** |
58
+ | TLCSailfish1 | 109,604 | 77.2 s | 32.2 s | **10.0 s** |
59
+ | MCLamportMutex | 724,274 | 33.1 s | 14.4 s | **4.7 s** |
60
+ | MCbtree | 374,727 | 40.2 s | 20.7 s | **6.7 s** |
61
+ | GameOfLife | 65,536 | 63.6 s | 12.9 s | **4.1 s** |
62
+
63
+ Across all 757 models that match on one worker, tla-rs is faster on 750. It takes 670 s in total, against TLC's 2,441 s.
64
+
65
+ ## How it compares
66
+
67
+ | | TLC | tla-rs |
68
+ | --- | :---: | :---: |
69
+ | State counts | reference | identical on 766 / 772 models, 0 mismatches |
70
+ | Runtime | JVM | native binary, no runtime |
71
+ | Liveness and fairness | ✅ | ✅ |
72
+ | Parallel search | ✅ | ✅, counts identical to one worker |
73
+ | Command line, messages, exit codes | ✅ | ✅ TLC-compatible |
74
+ | Fingerprint width | 64-bit | 128-bit |
75
+
76
+ Other Rust TLA+ checkers either target a subset of the language (`fabracht/tla-rs`) or compile a spec into a dedicated checker (`ddalton/tla-mc`). tla-rs interprets the full language and aims for conformance with TLC.
77
+
78
+ ## Installation
79
+
80
+ | Channel | Package | Status |
81
+ | --- | --- | --- |
82
+ | crates.io | [`tla-tools`](https://crates.io/crates/tla-tools) | planned: `cargo install tla-tools` |
83
+ | npm | [`tla-rs`](https://www.npmjs.com/package/tla-rs) | planned: `npx tla-rs Spec.tla`, with prebuilt binaries |
84
+
85
+ Both install the same `tla-rs` command. Until the first release, build from source. This needs Rust 1.85 or newer:
86
+
87
+ ```shell
88
+ git clone https://github.com/bbcvc/tla-rs.git && cd tla-rs
89
+ cargo build --release -p tla-tools
90
+ # the binary is target/release/tla-rs
91
+ ```
92
+
93
+ ## Usage
94
+
95
+ ```shell
96
+ tla-rs Spec.tla # check Spec.tla with Spec.cfg
97
+ tla-rs Spec.tla -config Other.cfg # use another model configuration
98
+ tla-rs Spec.tla -workers auto # one worker per core
99
+ tla-rs Spec.tla -deadlock # don't check for deadlock
100
+ tla-rs Spec.tla -library path/to/CommunityModules
101
+ ```
102
+
103
+ Exit codes follow TLC:
104
+
105
+ | Code | Meaning |
106
+ | ---: | --- |
107
+ | `0` | no error |
108
+ | `10` | an assumption is false |
109
+ | `11` | deadlock |
110
+ | `12` | an invariant or safety property is violated |
111
+ | `13` | a liveness property is violated |
112
+ | `75` | evaluation failed |
113
+ | `150` | parse error |
114
+
115
+ See [crates/tla-tools/README.md](crates/tla-tools/README.md) for every option and exit code.
116
+
117
+ ## Architecture
118
+
119
+ ```
120
+ source ─► tla-tools-parser ─► tla-tools-resolve ─► tla-tools-eval ─► tla-tools-checker ─► tla-tools
121
+ ▲
122
+ tla-tools-value
123
+ ```
124
+
125
+ | Crate | Role |
126
+ | --- | --- |
127
+ | `tla-tools-intern` | Process-wide interner with TLC's string order |
128
+ | `tla-tools-ast` | Syntax tree for TLA+ modules |
129
+ | `tla-tools-parser` | TLA+ modules (via tree-sitter-tlaplus) and TLC configuration files |
130
+ | `tla-tools-resolve` | Name resolution, `INSTANCE`, level checking |
131
+ | `tla-tools-value` | Values with TLC's equality, ordering, printing and fingerprints |
132
+ | `tla-tools-eval` | Expressions, initial and next states, `ENABLED`, standard modules |
133
+ | `tla-tools-checker` | Spec processing, BFS (single and parallel), liveness, symmetry |
134
+ | `tla-tools` | The `tla-rs` binary (the crate published to crates.io) |
135
+
136
+ Matching TLC state for state takes more than a correct evaluator. tla-rs reproduces TLC's ordering of strings and values, which decides what `CHOOSE` returns. It also reproduces TLC's order of variables, the way TLC enumerates successors, its 32-bit integer overflow, and the order in which its liveness checker explores the behavior graph. [EVALUATION.md](EVALUATION.md) records each of these choices.
137
+
138
+ ## Testing
139
+
140
+ ```shell
141
+ cargo test --workspace
142
+
143
+ # the full conformance suite (needs the tla-ts repository next to this one)
144
+ C=../tla-ts/packages/tla-ts/tests/conformance
145
+ cargo run --release -p tla-tools-checker --example conformance -- $C/.corpora \
146
+ --library $C/.corpora/tlapm/library --library $C/.corpora/community/modules \
147
+ --expected $C/expected.json --timeout 60 --jobs 4 [--workers 4] [--filter NAME]
148
+ ```
149
+
150
+ The conformance runner checks each model in its own process and compares the result with TLC's recorded outcome. The 823 models come from 16 corpora, including TLC's own test models, tlaplus/Examples, Spectacle, CCF, MongoDB and CockroachDB.
151
+
152
+ ## Roadmap
153
+
154
+ **Done**
155
+
156
+ - [x] Parser, name resolution and level checking
157
+ - [x] Values with TLC's ordering, equality and fingerprints
158
+ - [x] Evaluator, initial and next states, `ENABLED`, standard modules and Java-backed operators
159
+ - [x] Safety checking: invariants, action properties, deadlock, constraints, `VIEW`, `SYMMETRY`
160
+ - [x] Liveness checking with fairness, as TLC's `-lncheck final`
161
+ - [x] Parallel breadth-first search with deterministic counts
162
+ - [x] TLC-compatible command line
163
+
164
+ **Next**
165
+
166
+ - [ ] Show the output of `Print` and `PrintT`
167
+ - [ ] Support `ALIAS` in counterexample traces
168
+ - [ ] Print SANY's parse messages and warnings exactly, and TLC's stack of nested expressions in evaluation errors
169
+ - [ ] Cut per-call evaluation overhead; about ten large models (VR_*, S2C, MoveRange) are still around 2× slower than TLC
170
+ - [ ] Bring the 15 models that time out at 60 s on one worker under the limit
171
+ - [ ] Parallel liveness checking (temporal properties run on one worker today)
172
+ - [ ] Parallel search for counterexamples (today a violation falls back to one worker for TLC's shortest trace)
173
+ - [ ] Persistent worker threads and a lock-free fingerprint set
174
+ - [ ] Disk-backed state queue and fingerprint set for state spaces larger than memory
175
+ - [ ] Simulation mode (`-simulate`), `-lncheck` during the search, `-dump`, `-continue`
176
+ - [ ] Publish `tla-tools` to crates.io and `tla-rs` to npm with prebuilt binaries, and set up CI
177
+
178
+ ## Documents
179
+
180
+ - [DESIGN.md](DESIGN.md): the original design
181
+ - [EVALUATION.md](EVALUATION.md): the revisions needed to match TLC, and results for each phase
182
+ - [crates/tla-tools/README.md](crates/tla-tools/README.md): command-line options, output, and remaining differences from TLC
183
+
184
+ ## Acknowledgements
185
+
186
+ - [TLC](https://github.com/tlaplus/tlaplus) is the reference that tla-rs follows.
187
+ - [tla-ts](https://github.com/aidenybai/tla-ts) provided the conformance suite and the ports of TLC's algorithms that tla-rs builds on.
188
+ - [tree-sitter-tlaplus](https://github.com/tlaplus-community/tree-sitter-tlaplus) is the parser.
189
+
190
+ ## License
191
+
192
+ [MIT](LICENSE)
package/bin/tla-rs.js ADDED
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env node
2
+ // Runs the prebuilt tla-rs binary for this platform, installed as an optional dependency.
3
+ "use strict";
4
+
5
+ const { spawnSync } = require("node:child_process");
6
+ const path = require("node:path");
7
+
8
+ function binaryPath() {
9
+ if (process.env.TLA_RS_BINARY) return process.env.TLA_RS_BINARY;
10
+ const pkg = `tla-rs-${process.platform}-${process.arch}`;
11
+ const exe = process.platform === "win32" ? "tla-rs.exe" : "tla-rs";
12
+ try {
13
+ return require.resolve(`${pkg}/bin/${exe}`);
14
+ } catch {
15
+ console.error(
16
+ `tla-rs: no prebuilt binary for ${process.platform}-${process.arch} (package ${pkg} is not installed).\n` +
17
+ "If your package manager skipped optional dependencies, reinstall without --no-optional.\n" +
18
+ "Otherwise install from source with `cargo install tla-tools`, " +
19
+ "or set TLA_RS_BINARY to the path of a tla-rs binary."
20
+ );
21
+ process.exit(1);
22
+ }
23
+ }
24
+
25
+ const result = spawnSync(binaryPath(), process.argv.slice(2), { stdio: "inherit" });
26
+ if (result.error) {
27
+ console.error(`tla-rs: ${result.error.message}`);
28
+ process.exit(1);
29
+ }
30
+ if (result.signal) {
31
+ process.kill(process.pid, result.signal);
32
+ } else {
33
+ process.exit(result.status);
34
+ }
package/package.json CHANGED
@@ -1,13 +1,37 @@
1
1
  {
2
2
  "name": "tla-rs",
3
- "version": "0.0.1",
4
- "description": "",
5
- "main": "index.js",
6
- "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
3
+ "version": "0.1.0",
4
+ "description": "A fast TLA+ model checker in Rust that gives the same answers as TLC, state for state",
5
+ "keywords": [
6
+ "tla",
7
+ "tlaplus",
8
+ "tla+",
9
+ "tlc",
10
+ "model-checker",
11
+ "formal-methods"
12
+ ],
13
+ "homepage": "https://github.com/bbcvc/tla-rs",
14
+ "bugs": "https://github.com/bbcvc/tla-rs/issues",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/bbcvc/tla-rs.git",
18
+ "directory": "npm/tla-rs"
8
19
  },
9
- "keywords": [],
10
- "author": "",
11
- "license": "ISC",
12
- "type": "commonjs"
20
+ "license": "MIT",
21
+ "bin": {
22
+ "tla-rs": "bin/tla-rs.js"
23
+ },
24
+ "files": [
25
+ "bin"
26
+ ],
27
+ "engines": {
28
+ "node": ">=16"
29
+ },
30
+ "optionalDependencies": {
31
+ "tla-rs-darwin-arm64": "0.1.0",
32
+ "tla-rs-darwin-x64": "0.1.0",
33
+ "tla-rs-linux-arm64": "0.1.0",
34
+ "tla-rs-linux-x64": "0.1.0",
35
+ "tla-rs-win32-x64": "0.1.0"
36
+ }
13
37
  }
package/mise.toml DELETED
@@ -1,2 +0,0 @@
1
- [tools]
2
- node = "latest"