Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
94a2cc8
Translate top-level module
ahelwer Jul 6, 2025
d1c56c9
Use OUnit to control test execution
ahelwer Jul 9, 2025
215fc5e
Print sexpr diff and silence parser err output
ahelwer Jul 10, 2025
1191aab
Skip all test files to track progress
ahelwer Jul 10, 2025
a09fb1d
Translate basic operator definition
ahelwer Jul 10, 2025
013644b
Implemented jlist translation
ahelwer Jul 10, 2025
2a0f603
fixed another test
ahelwer Jul 10, 2025
66e1917
Translate functions and assumptions
ahelwer Jul 10, 2025
8b9fdc5
All tests in conjlist.txt pass
ahelwer Jul 11, 2025
3a82509
All tests in disjlist.txt pass
ahelwer Jul 11, 2025
684865b
All tuples.txt tests pass
ahelwer Jul 11, 2025
591f2af
Tests in if_then_else.txt pass
ahelwer Jul 11, 2025
2546d21
String tests now pass
ahelwer Jul 11, 2025
185c70e
Case.txt tests now pass
ahelwer Jul 12, 2025
37b10e8
jlist.txt tests pass; implemented label translation
ahelwer Jul 12, 2025
90fe4e0
modules.txt tests pass
ahelwer Jul 12, 2025
ffcdf21
number.txt tests pass; basic subexpression translation implemented
ahelwer Jul 12, 2025
3075ca9
Operators.txt tests pass
ahelwer Jul 12, 2025
f3f4a5d
Mostly finished set quantification translation
ahelwer Jul 13, 2025
1f644b6
All sets.txt tests now pass; un-ditto'd bound identifiers
ahelwer Jul 25, 2025
3b46588
Mostly translate quantification tests
ahelwer Jul 29, 2025
8089b5c
Quantification.txt tests now all pass
ahelwer Sep 3, 2025
eb75f71
records.txt tests all pass
ahelwer Sep 3, 2025
8cc2549
let_in.txt tests pass
ahelwer Sep 3, 2025
c30d3b8
recursive.txt tests pass
ahelwer Sep 3, 2025
b72c53f
step_expressions.txt tests pass
ahelwer Sep 3, 2025
8070dd6
Functions.txt: most tests pass
ahelwer Sep 3, 2025
f43460c
functions.txt tests all pass
ahelwer Sep 4, 2025
1ae92d7
fairness.txt tests all pass
ahelwer Sep 4, 2025
9d910f6
except.txt tests all pass
ahelwer Sep 4, 2025
a29f796
prefix_op.txt tests all pass
ahelwer Sep 4, 2025
8430f22
postfix_op.txt test work
ahelwer Sep 4, 2025
dbedb74
postfix_op.txt tests all pass
ahelwer Sep 5, 2025
c0a3ac5
infix_op.txt work
ahelwer Sep 6, 2025
b54c0c6
Infix op tests all pass
ahelwer Oct 23, 2025
32df665
Subexpressions (excepting proof IDs) now translated
ahelwer Oct 24, 2025
68c1307
Use-or-hide translation complete
ahelwer Oct 24, 2025
d81a5fc
Translated ASSUME/PROVE constructs
ahelwer Oct 28, 2025
98161f1
Proof translation progress
ahelwer Oct 30, 2025
8f8f6c9
Proof step translation progress
ahelwer Oct 30, 2025
5a709f0
Only one proof test remains
ahelwer Oct 30, 2025
abb48bf
All viable syntax tests now pass
ahelwer Oct 31, 2025
e459b6c
Added some more ASSUME/PROVE tests
ahelwer Oct 31, 2025
e53a31d
Added additional proof test cases
ahelwer Nov 1, 2025
b7fbeda
Reverted parser output changes
ahelwer Nov 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dune-project
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
ocaml
dune-site
dune-build-info
sexp_diff
sexplib
cmdliner
camlzip
Expand Down
2 changes: 1 addition & 1 deletion src/tlapm_lib.ml
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,7 @@ let modctx_of_string ~(content : string) ~(filename : string) ~loader_paths ~pre
| None, None -> Error (None, Printexc.to_string e))

let module_of_string module_str =
let hparse = Tla_parser.P.use M_parser.parse in
let hparse = Tla_parser.P.use Module.Parser.parse in
let (flex, _) = Alexer.lex_string module_str in
Tla_parser.P.run hparse ~init:Tla_parser.init ~source:flex

Expand Down
2 changes: 1 addition & 1 deletion src/tlapm_lib.mli
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ val modctx_of_string :
from a specified string, assume it is located in the
specified path. *)

val module_of_string : string -> M_t.mule option
val module_of_string : string -> Module.T.mule option
(** Parse the specified string as a module. No dependencies
are considered, nor proof obligations are elaborated. *)

Expand Down
2 changes: 1 addition & 1 deletion test/parser/dune
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
(test
(name parser_tests)
(modes exe)
(libraries tlapm_lib ounit2 sexplib)
(libraries tlapm_lib ounit2 sexplib sexp_diff)
(deps (glob_files_rec syntax_corpus/*))
(preprocess (pps ppx_deriving.show))
)
192 changes: 100 additions & 92 deletions test/parser/parser_tests.ml
Original file line number Diff line number Diff line change
@@ -1,93 +1,28 @@
(** This test runs a battery of TLA+ syntax fragments against TLAPM's syntax
parser. In the future it will check the actual parse tree emitted by
TLAPM, but for now it just checks whether TLAPM parses without error all
the syntax it is expected to parse. Tests sourced from:
(** This file runs a battery of TLA+ syntax fragments against TLAPM's syntax
parser. It then takes the resulting parse tree and translates it into a
normalized S-expression form to compare with the associated expected AST.
Tests are sourced from the standardized syntax test corpus at:
https://github.com/tlaplus/rfcs/tree/2a772d9dd11acec5d7dedf30abfab91a49de48b8/language_standard/tests/tlaplus_syntax
*)

open Tlapm_lib;;

open Syntax_corpus_file_parser;;
open OUnit2;;

(** Calls TLAPM's parser with the given input. Catches all exceptions and
treats them as parse failures.
@param input The TLA+ fragment to parse.
@return None if parse failure, syntax tree root if successful.
*)
let parse (input : string) : Tlapm_lib__M_t.mule option =
try Tlapm_lib.module_of_string input
let parse (input : string) : Module.T.mule option =
try module_of_string input
with _ -> None

(** Datatype summarizing a run of all the syntax tests. *)
type test_run_summary = {
total : int;
succeeded : int;
failed : int;
skipped : int;
failures : syntax_test_info list;
} [@@deriving show]

(** A blank test summary. *)
let test_summary_init = {
total = 0;
succeeded = 0;
failed = 0;
skipped = 0;
failures = [];
}

(** Runs a given syntax test by determining its type then sending the input
into the TLAPM parser.
@param expect_failure Whether this test should fail due to a TLAPM bug.
@param test Information about the test itself.
@return Whether the test succeeded.
*)
let run_test (test : syntax_test) : bool =
match test.test with
| Error_test input -> parse input |> Option.is_none
| Expected_test (input, _) -> parse input |> Option.is_some

(** Controls run of a given syntax test. Checks whether test should be
skipped and whether it is expected to fail, then runs test and returns
summary.
@param expect_failure Whether this test should fail due to a TLAPM bug.
@param acc Accumulation variable for test summarization.
@param test Information about the test itself.
@return Test run summary.
*)
let control_test_run
(expect_failure : syntax_test -> bool)
(acc : test_run_summary)
(test : syntax_test)
: test_run_summary =
let acc = {acc with total = acc.total + 1} in
if test.skip then {acc with skipped = acc.skipped + 1} else
if run_test test = expect_failure test
then {acc with failed = acc.failed + 1; failures = test.info :: acc.failures}
else {acc with succeeded = acc.succeeded + 1}

(** Given a path to a directory containing a corpus of syntax tests, get all
the tests encoded in those files, filter them as appropriate, then run
them all and collect the results.
@param path Path to the directory containing the corpus of syntax tests.
@param expect_failure Whether a test should fail due to a TLAPM bug.
@param filter_predicate Whether to actually execute a test.
@return Accumulated summary of all test executions.
*)
let run_test_corpus
(path : string)
(expect_failure : syntax_test -> bool)
(filter_pred : syntax_test -> bool)
: test_run_summary =
path
|> get_all_tests_under
|> List.filter filter_pred
|> List.fold_left (control_test_run expect_failure) test_summary_init

(** Names of tests that are known to fail due to TLAPM parser bugs.
@param test Information about the test.
@return Whether the test is expected to fail.
*)
let expect_failure (test : syntax_test) : bool =
let expect_parse_failure (test : syntax_test) : bool =
List.mem test.info.name [

(* https://github.com/tlaplus/tlapm/issues/54#issuecomment-2435515180 *)
Expand Down Expand Up @@ -142,24 +77,97 @@ let expect_failure (test : syntax_test) : bool =
"Nonfix Double Exclamation Operator (GH TSTLA #GH97, GH tlaplus/tlaplus #884)";
]

(** Filter predicate to control which tests to run.
@param name Optional; a test name to filter on.
@return Predicate matching all tests or tests with given name.
(** Names of tests that are unable to match the expected output tree, but not
because of a bug; instead, the TLAPM syntax tree doesn't contain the
(usually extraneous) necessary information to fully populate the output
tree with the expected children.
@param test Information about the test.
@return Whether the test should skip the tree comparison phase.
*)
let should_skip_tree_comparison (test : syntax_test) : bool =
List.mem test.info.name [
(* In TLAPM's ASSUME/PROVE parsing, NEW identifiers with unspecified
level are by default Constant instead of Unknown *)
"Assume/Prove With New Identifier of Unspecified Level";

(* Jlist terminated by single line comment omitted in TLAPM AST *)
"Keyword-Unit-Terminated Conjlist";
"Keyword-Unit-Terminated Disjlist";

(* Unnecessary parentheses omitted in TLAPM AST *)
"Nested Parentheses";

(* TLAPM AST does not distinguish between nonfix and infix ops *)
"Lexically-Conflicting Nonfix Operators";
"Minus and Negative";
"Nonfix Minus (GH tlaplus/tlaplus #GH884)";
"Nonfix Prefix Operators";
"Nonfix Infix Operators";
"Nonfix Postfix Operators";

(* TLAPM uses function literals for function definitions *)
(* See: https://github.com/tlaplus/tlapm/issues/237 *)
"Function Literal";

(* TLAPM makes multi-parameter EXCEPT update statements into tuples *)
"Record Update with Multiple Parameters";
"Record Update with Tuple and Non-Tuple Parameters";

(* TLAPM does not distinguish between <=> and \equiv *)
"IFF Disambiguation"
]

(** Names of tests that are expected to fail the tree comparison phase due to
bugs in TLAPM's syntax parser.
@param test Information about the test.
@return Whether the test is expected to fail the tree comparison phase.
*)
let should_run ?name test =
match name with
| Some name -> String.equal test.info.name name
| None -> true
let expect_tree_comparison_failure (test : syntax_test) : bool =
List.mem test.info.name [
(* TLAPM appears to simply return an empty set here? *)
(* https://github.com/tlaplus/tlapm/issues/235 *)
"Mistaken Set Filter Test";
"Mistaken Set Filter Tuples Test";
]

open OUnit2;;

(** The top-level test; runs all syntax tests, prints summary, then fails
with an assertion if any tests failed.
(** Gathers all syntax test files, parses them, then runs the cases they
contain as tests against TLAPM's syntax parser, skipping or expecting
failure as appropriate.
*)
let () =
let test_results =
run_test_corpus
"syntax_corpus"
expect_failure
(should_run (*~name:"Proof Containing Jlist"*))
in
print_endline (show_test_run_summary test_results);
assert_equal 0 test_results.failed;
let tests = "Standardized syntax test corpus" >::: (
get_all_tests_under "syntax_corpus"
|> List.map (fun test ->
Format.sprintf "[%s] %s" test.info.path test.info.name >::
(fun _ ->
skip_if test.skip "Test has skip attribute";
match test.test with
| Error_test input -> (
match parse input with
| None -> assert_bool "Expected error test to fail" (not (expect_parse_failure test))
| Some _ -> assert_bool "Expected parse failure" (expect_parse_failure test)
)
| Expected_test (input, expected) -> (
match parse input with
| None -> assert_bool "Expected parse success" (expect_parse_failure test)
| Some tlapm_output ->
skip_if (should_skip_tree_comparison test) "Skipping parse tree comparison";
let open Translate_syntax_tree in
let open Sexplib in
let actual = tlapm_output |> translate_tla_source_file |> ts_node_to_sexpr in
if Sexp.equal expected actual
then assert_bool "Expected parse test to fail" (not (expect_tree_comparison_failure test))
else
let open Sexp_diff in
let diff = Algo.diff ~original:expected ~updated:actual () in
let options = Display.Display_options.(create Layout.Single_column) in
let text = Display.display_with_ansi_colors options diff in
assert_bool text (expect_tree_comparison_failure test)
)
)
)
)

(** The OUnit2 test entrypoint. *)
let () = run_test_tt_main tests
Loading