From 26871aa9aed93b5e35a5824ab9eba9f1906e41e9 Mon Sep 17 00:00:00 2001 From: John Doty Date: Thu, 4 Jan 2024 19:37:58 -0800 Subject: [PATCH] [fine] Automatic rebase support for CST --- fine/build.rs | 3 +- fine/tests/example_tests.rs | 80 ++++++++++++++++++- .../{expressions.fine => arithmetic.fine} | 5 -- fine/tests/expression/boolean.fine | 22 +++++ fine/tests/expression/number.fine | 8 ++ fine/tests/expression/strings.fine | 12 +++ 6 files changed, 122 insertions(+), 8 deletions(-) rename fine/tests/expression/{expressions.fine => arithmetic.fine} (83%) create mode 100644 fine/tests/expression/boolean.fine create mode 100644 fine/tests/expression/number.fine create mode 100644 fine/tests/expression/strings.fine diff --git a/fine/build.rs b/fine/build.rs index 100b5229..69f39cb9 100644 --- a/fine/build.rs +++ b/fine/build.rs @@ -33,8 +33,9 @@ fn generate_test_for_file(path: PathBuf) -> String { } let concrete_comparison = if let Some(concrete) = concrete_stuff { + let display_path = path.display().to_string(); quote! { - crate::assert_concrete(&_tree, #concrete) + crate::assert_concrete(&_tree, #concrete, #display_path) } } else { quote! {} diff --git a/fine/tests/example_tests.rs b/fine/tests/example_tests.rs index c61f26c6..e07a84d7 100644 --- a/fine/tests/example_tests.rs +++ b/fine/tests/example_tests.rs @@ -1,8 +1,84 @@ use fine::parser::concrete::Tree; use pretty_assertions::assert_eq; -fn assert_concrete(tree: &Tree, expected: &str) { - assert_eq!(tree.dump(), expected, "concrete syntax trees did not match"); +fn rebase_concrete(source_path: &str, dump: &str) { + // RE-BASE + let contents = std::fs::read_to_string(source_path) + .expect(&format!("unable to read input file {}", source_path)); + + let mut result = String::new(); + let mut lines = contents.lines(); + + let mut found_concrete_section = false; + while let Some(line) = lines.next() { + result.push_str(line); + result.push_str("\n"); + + if line == "// concrete:" { + found_concrete_section = true; + break; + } + } + if !found_concrete_section { + panic!( + "unable to locate the concrete section in {}. Is there a line that starts with '// concrete:'?", + source_path + ); + } + + let mut replaced_output = false; + while let Some(line) = lines.next() { + if line.starts_with("// | ") { + // Skip copying lines here, because we're skipping + // the existing concrete syntax tree. + } else { + // OK we're out of concrete syntax tree; copy in the + // new CST. (We do this inline so we don't lose + // `line`.) + for expected_line in dump.lines() { + result.push_str("// | "); + result.push_str(expected_line); + result.push_str("\n"); + } + + // (Make sure not to drop this line.) + result.push_str(line); + result.push_str("\n"); + + replaced_output = true; + break; + } + } + if !replaced_output { + panic!( + "didn't actually replace the output section in {}", + source_path + ); + } + + // Now just copy the rest of the lines. + while let Some(line) = lines.next() { + result.push_str(line); + result.push_str("\n"); + } + + // ... and re-write the file. + std::fs::write(source_path, result).expect("unable to write the new file!"); +} + +fn assert_concrete(tree: &Tree, expected: &str, source_path: &str) { + let dump = tree.dump(); + let rebase = std::env::var("FINE_TEST_REBASE") + .unwrap_or(String::new()) + .to_lowercase(); + match rebase.as_str() { + "1" | "true" | "yes" | "y" => { + if dump != expected { + rebase_concrete(source_path, &dump) + } + } + _ => assert_eq!(expected, dump, "concrete syntax trees did not match (set FINE_TEST_REBASE=1 to auto-rebase if the diff is expected)"), + } } include!(concat!(env!("OUT_DIR"), "/generated_tests.rs")); diff --git a/fine/tests/expression/expressions.fine b/fine/tests/expression/arithmetic.fine similarity index 83% rename from fine/tests/expression/expressions.fine rename to fine/tests/expression/arithmetic.fine index b353d91a..fe61663a 100644 --- a/fine/tests/expression/expressions.fine +++ b/fine/tests/expression/arithmetic.fine @@ -1,10 +1,6 @@ // concrete: // | File // | ExpressionStatement -// | LiteralExpression -// | Number:'"42"' -// | Semicolon:'";"' -// | ExpressionStatement // | BinaryExpression // | BinaryExpression // | LiteralExpression @@ -23,5 +19,4 @@ // | Number:'"4"' // | Semicolon:'";"' // -42; 1 * 2 + -3 * 4; diff --git a/fine/tests/expression/boolean.fine b/fine/tests/expression/boolean.fine new file mode 100644 index 00000000..340b9e65 --- /dev/null +++ b/fine/tests/expression/boolean.fine @@ -0,0 +1,22 @@ +// concrete: +// | File +// | ExpressionStatement +// | BinaryExpression +// | BinaryExpression +// | LiteralExpression +// | True:'"true"' +// | And:'"and"' +// | LiteralExpression +// | False:'"false"' +// | Or:'"or"' +// | BinaryExpression +// | LiteralExpression +// | False:'"false"' +// | And:'"and"' +// | UnaryExpression +// | Bang:'"!"' +// | LiteralExpression +// | True:'"true"' +// | Semicolon:'";"' +// +true and false or false and !true; diff --git a/fine/tests/expression/number.fine b/fine/tests/expression/number.fine new file mode 100644 index 00000000..b153d3f2 --- /dev/null +++ b/fine/tests/expression/number.fine @@ -0,0 +1,8 @@ +// concrete: +// | File +// | ExpressionStatement +// | LiteralExpression +// | Number:'"42"' +// | Semicolon:'";"' +// +42; diff --git a/fine/tests/expression/strings.fine b/fine/tests/expression/strings.fine new file mode 100644 index 00000000..eab2d5ea --- /dev/null +++ b/fine/tests/expression/strings.fine @@ -0,0 +1,12 @@ +// concrete: +// | File +// | ExpressionStatement +// | BinaryExpression +// | LiteralExpression +// | String:'"\"Hello \""' +// | Plus:'"+"' +// | LiteralExpression +// | String:'"'world!'"' +// | Semicolon:'";"' +// +"Hello " + 'world!';