Skip to content

Commit 41faafb

Browse files
authored
Test PlusCal translation in CI (#136)
Fail if translation is out of date or module has invalid PlusCal syntax Signed-off-by: Andrew Helwer <[email protected]>
1 parent 2445ae9 commit 41faafb

File tree

23 files changed

+189
-86
lines changed

23 files changed

+189
-86
lines changed

.github/scripts/translate_pluscal.py

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
"""
2+
Runs PlusCal translation on all PlusCal specs.
3+
"""
4+
5+
from argparse import ArgumentParser
6+
from concurrent.futures import ThreadPoolExecutor
7+
import logging
8+
from os import cpu_count
9+
from os.path import dirname, normpath
10+
import subprocess
11+
from subprocess import CompletedProcess
12+
import tla_utils
13+
14+
parser = ArgumentParser(description='Run PlusCal translation on all modules.')
15+
parser.add_argument('--tools_jar_path', help='Path to the tla2tools.jar file', required=True)
16+
parser.add_argument('--manifest_path', help='Path to the tlaplus/examples manifest.json file', required=True)
17+
parser.add_argument('--skip', nargs='+', help='Space-separated list of .tla modules to skip converting', required=False, default=[])
18+
parser.add_argument('--only', nargs='+', help='If provided, only convert models in this space-separated list', required=False, default=[])
19+
parser.add_argument('--verbose', help='Set logging output level to debug', action='store_true')
20+
args = parser.parse_args()
21+
22+
logging.basicConfig(level = logging.DEBUG if args.verbose else logging.INFO)
23+
24+
tools_path = normpath(args.tools_jar_path)
25+
manifest_path = normpath(args.manifest_path)
26+
examples_root = dirname(manifest_path)
27+
skip_modules = [normpath(path) for path in args.skip]
28+
only_modules = [normpath(path) for path in args.only]
29+
30+
manifest = tla_utils.load_json(manifest_path)
31+
32+
# List of all modules to translate
33+
modules = [
34+
tla_utils.from_cwd(examples_root, module['path'])
35+
for spec in manifest['specifications']
36+
for module in spec['modules']
37+
if 'pluscal' in module['features']
38+
and normpath(module['path']) not in skip_modules
39+
and (only_modules == [] or normpath(module['path']) in only_modules)
40+
]
41+
42+
for path in skip_modules:
43+
logging.info(f'Skipping {path}')
44+
45+
def translate_module(module_path):
46+
logging.info(f'Translating {module_path}')
47+
result = subprocess.run(
48+
['java', '-cp', tools_path, 'pcal.trans', '-nocfg', module_path],
49+
stdout=subprocess.PIPE,
50+
stderr=subprocess.STDOUT,
51+
text=True
52+
)
53+
match result:
54+
case CompletedProcess():
55+
if result.returncode == 0:
56+
return True
57+
else:
58+
logging.error(f'Module {module_path} conversion failed with return code {result.returncode}; output:\n{result.stdout}')
59+
return False
60+
case _:
61+
logging.error(f'Unhandled result type {type(result)}: {result.stdout}')
62+
return False
63+
64+
success = True
65+
thread_count = cpu_count() if not args.verbose else 1
66+
logging.info(f'Translating PlusCal using {thread_count} threads')
67+
with ThreadPoolExecutor(thread_count) as executor:
68+
results = executor.map(translate_module, modules)
69+
exit(0 if all(results) else 1)
70+

.github/scripts/unicode_conversion.py

+1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ def convert_module(module_path):
6060
return False
6161
case _:
6262
logging.error(f'Unhandled TLAUC result type {type(result)}: {result.stdout}')
63+
return False
6364

6465
success = True
6566
thread_count = cpu_count() if not args.verbose else 1

.github/workflows/CI.yml

+33-8
Original file line numberDiff line numberDiff line change
@@ -45,23 +45,23 @@ jobs:
4545
run: $SCRIPT_DIR/linux-setup.sh $SCRIPT_DIR $DEPS_DIR false
4646
- name: Check manifest.json format
4747
run: |
48-
python $SCRIPT_DIR/check_manifest_schema.py \
49-
--manifest_path manifest.json \
48+
python "$SCRIPT_DIR/check_manifest_schema.py" \
49+
--manifest_path manifest.json \
5050
--schema_path manifest-schema.json
5151
- name: Check manifest files
5252
run: |
53-
python $SCRIPT_DIR/check_manifest_files.py \
54-
--manifest_path manifest.json \
53+
python "$SCRIPT_DIR/check_manifest_files.py" \
54+
--manifest_path manifest.json \
5555
--ci_ignore_path .ciignore
5656
- name: Check manifest feature flags
5757
run: |
58-
python $SCRIPT_DIR/check_manifest_features.py \
59-
--manifest_path manifest.json \
58+
python "$SCRIPT_DIR/check_manifest_features.py" \
59+
--manifest_path manifest.json \
6060
--ts_path $DEPS_DIR/tree-sitter-tlaplus
6161
- name: Check README spec table
6262
run: |
63-
python $SCRIPT_DIR/check_markdown_table.py \
64-
--manifest_path manifest.json \
63+
python "$SCRIPT_DIR/check_markdown_table.py" \
64+
--manifest_path manifest.json \
6565
--readme_path README.md
6666
- name: Convert specs to unicode
6767
if: matrix.unicode
@@ -75,6 +75,31 @@ jobs:
7575
python "$SCRIPT_DIR/unicode_number_set_shim.py" \
7676
--ts_path "$DEPS_DIR/tree-sitter-tlaplus" \
7777
--manifest_path manifest.json
78+
- name: Translate PlusCal
79+
if: (!matrix.unicode) # PlusCal does not yet support unicode
80+
run: |
81+
# https://github.com/tlaplus/tlaplus/issues/906
82+
SKIP=(
83+
"specifications/byzpaxos/BPConProof.tla"
84+
"specifications/byzpaxos/PConProof.tla"
85+
"specifications/byzpaxos/VoteProof.tla"
86+
)
87+
if [[ "${{ matrix.os }}" == "windows-latest" ]]; then
88+
# Slush.tla includes unicode characters in comments,
89+
# and on Windows the PlusCal translator can't handle
90+
# this for some reason.
91+
SKIP+=("specifications/SlushProtocol/Slush.tla")
92+
fi
93+
python $SCRIPT_DIR/translate_pluscal.py \
94+
--tools_jar_path $DEPS_DIR/tools/tla2tools.jar \
95+
--manifest_path manifest.json \
96+
--skip "${SKIP[@]}"
97+
if [[ "${{ matrix.unicode }}" == "false" ]]; then
98+
git status
99+
git diff
100+
diff_count=$(git status --porcelain=v1 2>/dev/null | wc -l)
101+
exit $diff_count
102+
fi
78103
- name: Parse all modules
79104
run: |
80105
# Need to have a nonempty list to pass as a skip parameter

specifications/DiningPhilosophers/DiningPhilosophers.tla

+1-1
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ Think:
113113
end process;
114114

115115
end algorithm; *)
116-
\* BEGIN TRANSLATION (chksum(pcal) = "8d8268d4" /\ chksum(tla) = "16352822")
116+
\* BEGIN TRANSLATION (chksum(pcal) = "ea877089" /\ chksum(tla) = "16352822")
117117
VARIABLES forks, pc
118118

119119
(* define statement *)

specifications/LoopInvariance/BinarySearch.tla

+5-3
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,11 @@ a == /\ pc = "a"
6767
/\ UNCHANGED << low, high, result >>
6868
/\ UNCHANGED << seq, val >>
6969

70+
(* Allow infinite stuttering to prevent deadlock on termination. *)
71+
Terminating == pc = "Done" /\ UNCHANGED vars
72+
7073
Next == a
71-
\/ (* Disjunct to prevent deadlock on termination *)
72-
(pc = "Done" /\ UNCHANGED vars)
74+
\/ Terminating
7375

7476
Spec == /\ Init /\ [][Next]_vars
7577
/\ WF_vars(Next)
@@ -245,7 +247,7 @@ THEOREM Spec => []resultCorrect
245247
<2>2. CASE UNCHANGED vars
246248
BY <2>2 DEF Inv, TypeOK, vars
247249
<2>3. QED
248-
BY <2>1, <2>2 DEF Next
250+
BY <2>1, <2>2 DEF Next, Terminating
249251
<1>3. Inv => resultCorrect
250252
BY DEF resultCorrect, Inv, TypeOK
251253
<1>4. QED

specifications/LoopInvariance/Quicksort.tla

+4-2
Original file line numberDiff line numberDiff line change
@@ -152,9 +152,11 @@ a == /\ pc = "a"
152152
/\ UNCHANGED << seq, U >>
153153
/\ seq0' = seq0
154154

155+
(* Allow infinite stuttering to prevent deadlock on termination. *)
156+
Terminating == pc = "Done" /\ UNCHANGED vars
157+
155158
Next == a
156-
\/ (* Disjunct to prevent deadlock on termination *)
157-
(pc = "Done" /\ UNCHANGED vars)
159+
\/ Terminating
158160

159161
Spec == /\ Init /\ [][Next]_vars
160162
/\ WF_vars(Next)

specifications/LoopInvariance/SumSequence.tla

+4-2
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,11 @@ a == /\ pc = "a"
7474
/\ UNCHANGED << sum, n >>
7575
/\ seq' = seq
7676

77+
(* Allow infinite stuttering to prevent deadlock on termination. *)
78+
Terminating == pc = "Done" /\ UNCHANGED vars
79+
7780
Next == a
78-
\/ (* Disjunct to prevent deadlock on termination *)
79-
(pc = "Done" /\ UNCHANGED vars)
81+
\/ Terminating
8082

8183
Spec == /\ Init /\ [][Next]_vars
8284
/\ WF_vars(Next)

specifications/MisraReachability/ParReach.tla

+5-2
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,12 @@ c(self) == /\ pc[self] = "c"
137137

138138
p(self) == a(self) \/ b(self) \/ c(self)
139139

140+
(* Allow infinite stuttering to prevent deadlock on termination. *)
141+
Terminating == /\ \A self \in ProcSet: pc[self] = "Done"
142+
/\ UNCHANGED vars
143+
140144
Next == (\E self \in Procs: p(self))
141-
\/ (* Disjunct to prevent deadlock on termination *)
142-
((\A self \in ProcSet: pc[self] = "Done") /\ UNCHANGED vars)
145+
\/ Terminating
143146

144147
Spec == /\ Init /\ [][Next]_vars
145148
/\ \A self \in Procs : WF_vars(p(self))

specifications/MisraReachability/ParReachProofs.tla

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ LEMMA TypeInvariant == Spec => []Inv
1111
<1>1. Init => Inv
1212
BY RootAssump DEF Init, Inv, ProcSet
1313
<1>2. Inv /\ [Next]_vars => Inv'
14-
BY SuccAssump DEF Inv, Next, vars, ProcSet, p, a, b, c
14+
BY SuccAssump DEF Inv, Next, Terminating, vars, ProcSet, p, a, b, c
1515
<1>3. QED
1616
BY <1>1, <1>2, PTL DEF Spec
1717

@@ -23,7 +23,7 @@ THEOREM Spec => R!Init /\ [][R!Next]_R!vars
2323
[Next]_vars
2424
PROVE [R!Next]_R!vars
2525
OBVIOUS
26-
<2> USE DEF Inv, Next, vars, R!Next, R!vars, vrootBar, pcBar
26+
<2> USE DEF Inv, Next, Terminating, vars, R!Next, R!vars, vrootBar, pcBar
2727
<2>1. ASSUME NEW self \in Procs,
2828
a(self)
2929
PROVE [R!Next]_R!vars
@@ -58,7 +58,7 @@ THEOREM Spec => R!Init /\ [][R!Next]_R!vars
5858
<2>4. CASE UNCHANGED vars
5959
BY <2>4
6060
<2>5. QED
61-
BY <2>1, <2>2, <2>3, <2>4 DEF Next, p
61+
BY <2>1, <2>2, <2>3, <2>4 DEF Next, Terminating, p
6262
<1>3. QED
6363
BY <1>1, <1>2, TypeInvariant, PTL DEF Spec
6464

specifications/MisraReachability/Reachable.tla

+4-3
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,6 @@ node and does the following:
8181
\* BEGIN TRANSLATION Here is the TLA+ translation of the PlusCal code.
8282
VARIABLES marked, vroot, pc
8383

84-
\*Reachable == ReachableFrom(marked) (* added for a test *)
8584
vars == << marked, vroot, pc >>
8685

8786
Init == (* Global variables *)
@@ -101,9 +100,11 @@ a == /\ pc = "a"
101100
ELSE /\ pc' = "Done"
102101
/\ UNCHANGED << marked, vroot >>
103102

103+
(* Allow infinite stuttering to prevent deadlock on termination. *)
104+
Terminating == pc = "Done" /\ UNCHANGED vars
105+
104106
Next == a
105-
\/ (* Disjunct to prevent deadlock on termination *)
106-
(pc = "Done" /\ UNCHANGED vars)
107+
\/ Terminating
107108

108109
Spec == /\ Init /\ [][Next]_vars
109110
/\ WF_vars(Next)

specifications/MisraReachability/ReachableProofs.tla

+2-2
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ THEOREM Thm1 == Spec => []Inv1
7474
<2>2. CASE UNCHANGED vars
7575
BY <2>2 DEF Inv1, TypeOK, vars
7676
<2>3. QED
77-
BY <2>1, <2>2 DEF Next
77+
BY <2>1, <2>2 DEF Next, Terminating
7878
<1>3. QED
7979
BY <1>1, <1>2, PTL DEF Spec
8080

@@ -189,7 +189,7 @@ THEOREM Thm3 == Spec => []Inv3
189189
(*********************************************************************)
190190
BY <2>1, <2>3 DEF Inv3, TypeOK, vars
191191
<2>4. QED
192-
BY <2>2, <2>3 DEF Next
192+
BY <2>2, <2>3 DEF Next, Terminating
193193
<1>3. QED
194194
BY <1>1, <1>2, Thm2, PTL DEF Spec
195195

specifications/MultiPaxos-SMR/MultiPaxos.tla

+1-1
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ end algorithm; *)
424424

425425
----------
426426

427-
\* BEGIN TRANSLATION (chksum(pcal) = "d7327cb9" /\ chksum(tla) = "bfbfd945")
427+
\* BEGIN TRANSLATION (chksum(pcal) = "2be53042" /\ chksum(tla) = "bfbfd945")
428428
VARIABLES msgs, node, pending, observed, pc
429429

430430
(* define statement *)

specifications/N-Queens/QueensPluscal.tla

+5-3
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,16 @@ nxtQ == /\ pc = "nxtQ"
8686
THEN /\ todo' = todo \ {queens}
8787
/\ sols' = (sols \union exts)
8888
ELSE /\ todo' = ((todo \ {queens}) \union exts)
89-
/\ UNCHANGED sols
89+
/\ sols' = sols
9090
/\ pc' = "nxtQ"
9191
ELSE /\ pc' = "Done"
9292
/\ UNCHANGED << todo, sols >>
9393

94+
(* Allow infinite stuttering to prevent deadlock on termination. *)
95+
Terminating == pc = "Done" /\ UNCHANGED vars
96+
9497
Next == nxtQ
95-
\/ (* Disjunct to prevent deadlock on termination *)
96-
(pc = "Done" /\ UNCHANGED vars)
98+
\/ Terminating
9799

98100
Spec == Init /\ [][Next]_vars
99101

specifications/N-Queens/QueensPluscal.toolbox/FourQueens/QueensPluscal.tla

+5-3
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,16 @@ nxtQ == /\ pc = "nxtQ"
8686
THEN /\ todo' = todo \ {queens}
8787
/\ sols' = (sols \union exts)
8888
ELSE /\ todo' = ((todo \ {queens}) \union exts)
89-
/\ UNCHANGED sols
89+
/\ sols' = sols
9090
/\ pc' = "nxtQ"
9191
ELSE /\ pc' = "Done"
9292
/\ UNCHANGED << todo, sols >>
9393

94+
(* Allow infinite stuttering to prevent deadlock on termination. *)
95+
Terminating == pc = "Done" /\ UNCHANGED vars
96+
9497
Next == nxtQ
95-
\/ (* Disjunct to prevent deadlock on termination *)
96-
(pc = "Done" /\ UNCHANGED vars)
98+
\/ Terminating
9799

98100
Spec == Init /\ [][Next]_vars
99101

specifications/TLC/TLCMC.tla

+4-2
Original file line numberDiff line numberDiff line change
@@ -258,9 +258,11 @@ trc == /\ pc = "trc"
258258
/\ UNCHANGED counterexample
259259
/\ UNCHANGED << S, C, state, successors, i, T >>
260260

261+
(* Allow infinite stuttering to prevent deadlock on termination. *)
262+
Terminating == pc = "Done" /\ UNCHANGED vars
263+
261264
Next == init \/ initPost \/ scsr \/ each \/ trc
262-
\/ (* Disjunct to prevent deadlock on termination *)
263-
(pc = "Done" /\ UNCHANGED vars)
265+
\/ Terminating
264266

265267
Spec == /\ Init /\ [][Next]_vars
266268
/\ WF_vars(Next)

specifications/TeachingConcurrency/Simple.tla

+6-3
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,12 @@ b(self) == /\ pc[self] = "b"
5757

5858
proc(self) == a(self) \/ b(self)
5959

60+
(* Allow infinite stuttering to prevent deadlock on termination. *)
61+
Terminating == /\ \A self \in ProcSet: pc[self] = "Done"
62+
/\ UNCHANGED vars
63+
6064
Next == (\E self \in 0..N-1: proc(self))
61-
\/ (* Disjunct to prevent deadlock on termination *)
62-
((\A self \in ProcSet: pc[self] = "Done") /\ UNCHANGED vars)
65+
\/ Terminating
6366

6467
Spec == Init /\ [][Next]_vars
6568

@@ -148,7 +151,7 @@ THEOREM Spec => []PCorrect
148151
<2>3. CASE UNCHANGED vars
149152
BY <2>3 DEF TypeOK, Inv, vars
150153
<2>4. QED
151-
BY <2>1, <2>2, <2>3 DEF Next, proc
154+
BY <2>1, <2>2, <2>3 DEF Next, Terminating, proc
152155
<1>3. Inv => PCorrect
153156
BY DEF Inv, TypeOK, PCorrect
154157
<1>4. QED

specifications/TeachingConcurrency/SimpleRegular.tla

+6-3
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,12 @@ b(self) == /\ pc[self] = "b"
9898

9999
proc(self) == a1(self) \/ a2(self) \/ b(self)
100100

101+
(* Allow infinite stuttering to prevent deadlock on termination. *)
102+
Terminating == /\ \A self \in ProcSet: pc[self] = "Done"
103+
/\ UNCHANGED vars
104+
101105
Next == (\E self \in 0..N-1: proc(self))
102-
\/ (* Disjunct to prevent deadlock on termination *)
103-
((\A self \in ProcSet: pc[self] = "Done") /\ UNCHANGED vars)
106+
\/ Terminating
104107

105108
Spec == Init /\ [][Next]_vars
106109

@@ -166,7 +169,7 @@ THEOREM Spec => []PCorrect
166169
<2>4. CASE UNCHANGED vars
167170
BY <2>4 DEF TypeOK, Inv, vars
168171
<2>5. QED
169-
BY <2>1, <2>2, <2>3, <2>4 DEF Next, proc
172+
BY <2>1, <2>2, <2>3, <2>4 DEF Next, Terminating, proc
170173
<1>3. Inv => PCorrect
171174
BY DEF Inv, TypeOK, PCorrect
172175
<1>4. QED

0 commit comments

Comments
 (0)