From 13fe33c6ed08623ed5b1284f3a1c86346cf983e4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 9 Nov 2023 05:45:31 -0500 Subject: [PATCH] more thorough testing around associativity --- crates/formality-core/src/parse.rs | 2 +- .../parser-torture-tests/left_associative.rs | 159 ++++++++++++++++++ tests/parser-torture-tests/main.rs | 24 ++- .../parser-torture-tests/none_associative.rs | 138 +++++++++++++++ tests/parser-torture-tests/precedence.rs | 60 ------- .../parser-torture-tests/right_associative.rs | 159 ++++++++++++++++++ 6 files changed, 479 insertions(+), 63 deletions(-) create mode 100644 tests/parser-torture-tests/left_associative.rs create mode 100644 tests/parser-torture-tests/none_associative.rs delete mode 100644 tests/parser-torture-tests/precedence.rs create mode 100644 tests/parser-torture-tests/right_associative.rs diff --git a/crates/formality-core/src/parse.rs b/crates/formality-core/src/parse.rs index a410cb5c..598af984 100644 --- a/crates/formality-core/src/parse.rs +++ b/crates/formality-core/src/parse.rs @@ -168,7 +168,7 @@ pub type ParseResult<'t, T> = Result, Set> pub type TokenResult<'t, T> = Result<(T, &'t str), Set>>; /// Tracks the variables in scope at this point in parsing. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub struct Scope { bindings: Vec<(String, CoreParameter)>, } diff --git a/tests/parser-torture-tests/left_associative.rs b/tests/parser-torture-tests/left_associative.rs new file mode 100644 index 00000000..da96a83a --- /dev/null +++ b/tests/parser-torture-tests/left_associative.rs @@ -0,0 +1,159 @@ +use formality_core::{term, test}; +use std::sync::Arc; + +#[term] +pub enum Expr { + #[cast] + Id(Id), + + #[grammar($v0 + $v1)] + #[precedence(1)] + Add(Arc, Arc), + + #[grammar($v0 * $v1)] + #[precedence(2)] + Mul(Arc, Arc), +} + +formality_core::id!(Id); + +#[test] +fn add_mul() { + let term: Expr = crate::ptt::term("a + b * c"); + expect_test::expect![[r#" + Add( + Id( + a, + ), + Mul( + Id( + b, + ), + Id( + c, + ), + ), + ) + "#]] + .assert_debug_eq(&term); +} + +#[test] +fn mul_add() { + let term: Expr = crate::ptt::term("a * b + c"); + expect_test::expect![[r#" + Add( + Mul( + Id( + a, + ), + Id( + b, + ), + ), + Id( + c, + ), + ) + "#]] + .assert_debug_eq(&term); +} + +#[test] +fn add_add() { + let term: Expr = crate::ptt::term("a + b + c"); + expect_test::expect![[r#" + Add( + Add( + Id( + a, + ), + Id( + b, + ), + ), + Id( + c, + ), + ) + "#]] + .assert_debug_eq(&term); +} + +#[test] +fn mul_mul() { + let term: Expr = crate::ptt::term("a * b * c"); + expect_test::expect![[r#" + Mul( + Mul( + Id( + a, + ), + Id( + b, + ), + ), + Id( + c, + ), + ) + "#]] + .assert_debug_eq(&term); +} + +#[test] +fn mul_mul_mul() { + let term: Expr = crate::ptt::term("a * b * c * d"); + expect_test::expect![[r#" + Mul( + Mul( + Mul( + Id( + a, + ), + Id( + b, + ), + ), + Id( + c, + ), + ), + Id( + d, + ), + ) + "#]] + .assert_debug_eq(&term); +} + +#[test] +fn add_add_mul_add() { + let term: Expr = crate::ptt::term("a + b + c * d + e"); + expect_test::expect![[r#" + Add( + Add( + Add( + Id( + a, + ), + Id( + b, + ), + ), + Mul( + Id( + c, + ), + Id( + d, + ), + ), + ), + Id( + e, + ), + ) + "#]] + .assert_debug_eq(&term); +} diff --git a/tests/parser-torture-tests/main.rs b/tests/parser-torture-tests/main.rs index 39836bd1..c903ef45 100644 --- a/tests/parser-torture-tests/main.rs +++ b/tests/parser-torture-tests/main.rs @@ -1,7 +1,9 @@ mod ambiguity; mod grammar; +mod left_associative; +mod none_associative; mod path; -mod precedence; +mod right_associative; formality_core::declare_language! { mod ptt { @@ -20,8 +22,26 @@ formality_core::declare_language! { } } +/// Used to parse `text` when we expect some remainder +fn expect_remainder(text: &str) -> (T, &str) +where + T: CoreParse, +{ + match T::parse(&Default::default(), text) { + Ok(parse) => { + let (value, remainder) = parse.finish(); + assert!( + !remainder.is_empty(), + "expected to have remainder text, but parsed entire term `{text:?}`" + ); + (value, remainder) + } + Err(errs) => panic!("encountered unexpected parse error: {errs:#?}"), + } +} + // Default language for our crate -use formality_core::Fallible; +use formality_core::{parse::CoreParse, Fallible}; use ptt::FormalityLang; fn main() -> Fallible<()> { diff --git a/tests/parser-torture-tests/none_associative.rs b/tests/parser-torture-tests/none_associative.rs new file mode 100644 index 00000000..3b751e4e --- /dev/null +++ b/tests/parser-torture-tests/none_associative.rs @@ -0,0 +1,138 @@ +use formality_core::{term, test}; +use std::sync::Arc; + +use crate::expect_remainder; + +#[term] +pub enum Expr { + #[cast] + Id(Id), + + #[grammar($v0 + $v1)] + #[precedence(1, none)] + Add(Arc, Arc), + + #[grammar($v0 * $v1)] + #[precedence(2, none)] + Mul(Arc, Arc), +} + +formality_core::id!(Id); + +#[test] +fn add_mul() { + let term: Expr = crate::ptt::term("a + b * c"); + expect_test::expect![[r#" + Add( + Id( + a, + ), + Mul( + Id( + b, + ), + Id( + c, + ), + ), + ) + "#]] + .assert_debug_eq(&term); +} + +#[test] +fn mul_add() { + let term: Expr = crate::ptt::term("a * b + c"); + expect_test::expect![[r#" + Add( + Mul( + Id( + a, + ), + Id( + b, + ), + ), + Id( + c, + ), + ) + "#]] + .assert_debug_eq(&term); +} + +#[test] +fn add_add() { + let term = expect_remainder::("a + b + c"); + expect_test::expect![[r#" + ( + Add( + Id( + a, + ), + Id( + b, + ), + ), + " + c", + ) + "#]] + .assert_debug_eq(&term); +} + +#[test] +fn mul_mul() { + let term = expect_remainder::("a * b * c"); + expect_test::expect![[r#" + ( + Mul( + Id( + a, + ), + Id( + b, + ), + ), + " * c", + ) + "#]] + .assert_debug_eq(&term); +} + +#[test] +fn mul_mul_mul() { + let term = expect_remainder::("a * b * c * d"); + expect_test::expect![[r#" + ( + Mul( + Id( + a, + ), + Id( + b, + ), + ), + " * c * d", + ) + "#]] + .assert_debug_eq(&term); +} + +#[test] +fn add_add_mul_add() { + let term = expect_remainder::("a + b + c * d + e"); + expect_test::expect![[r#" + ( + Add( + Id( + a, + ), + Id( + b, + ), + ), + " + c * d + e", + ) + "#]] + .assert_debug_eq(&term); +} diff --git a/tests/parser-torture-tests/precedence.rs b/tests/parser-torture-tests/precedence.rs deleted file mode 100644 index d40931e2..00000000 --- a/tests/parser-torture-tests/precedence.rs +++ /dev/null @@ -1,60 +0,0 @@ -use formality_core::{term, test}; -use std::sync::Arc; - -#[term] -pub enum Expr { - #[cast] - Id(Id), - - #[grammar($v0 + $v1)] - #[precedence(1)] - Add(Arc, Arc), - - #[grammar($v0 * $v1)] - #[precedence(2)] - Mul(Arc, Arc), -} - -formality_core::id!(Id); - -#[test] -fn mul_is_higher_precedence() { - let term: Expr = crate::ptt::term("a + b * c"); - expect_test::expect![[r#" - Add( - Id( - a, - ), - Mul( - Id( - b, - ), - Id( - c, - ), - ), - ) - "#]] - .assert_debug_eq(&term); -} - -#[test] -fn left_associative() { - let term: Expr = crate::ptt::term("a + b + c"); - expect_test::expect![[r#" - Add( - Add( - Id( - a, - ), - Id( - b, - ), - ), - Id( - c, - ), - ) - "#]] - .assert_debug_eq(&term); -} diff --git a/tests/parser-torture-tests/right_associative.rs b/tests/parser-torture-tests/right_associative.rs new file mode 100644 index 00000000..966f3370 --- /dev/null +++ b/tests/parser-torture-tests/right_associative.rs @@ -0,0 +1,159 @@ +use formality_core::{term, test}; +use std::sync::Arc; + +#[term] +pub enum Expr { + #[cast] + Id(Id), + + #[grammar($v0 + $v1)] + #[precedence(1, right)] + Add(Arc, Arc), + + #[grammar($v0 * $v1)] + #[precedence(2, right)] + Mul(Arc, Arc), +} + +formality_core::id!(Id); + +#[test] +fn add_mul() { + let term: Expr = crate::ptt::term("a + b * c"); + expect_test::expect![[r#" + Add( + Id( + a, + ), + Mul( + Id( + b, + ), + Id( + c, + ), + ), + ) + "#]] + .assert_debug_eq(&term); +} + +#[test] +fn mul_add() { + let term: Expr = crate::ptt::term("a * b + c"); + expect_test::expect![[r#" + Add( + Mul( + Id( + a, + ), + Id( + b, + ), + ), + Id( + c, + ), + ) + "#]] + .assert_debug_eq(&term); +} + +#[test] +fn add_add() { + let term: Expr = crate::ptt::term("a + b + c"); + expect_test::expect![[r#" + Add( + Id( + a, + ), + Add( + Id( + b, + ), + Id( + c, + ), + ), + ) + "#]] + .assert_debug_eq(&term); +} + +#[test] +fn mul_mul() { + let term: Expr = crate::ptt::term("a * b * c"); + expect_test::expect![[r#" + Mul( + Id( + a, + ), + Mul( + Id( + b, + ), + Id( + c, + ), + ), + ) + "#]] + .assert_debug_eq(&term); +} + +#[test] +fn mul_mul_mul() { + let term: Expr = crate::ptt::term("a * b * c * d"); + expect_test::expect![[r#" + Mul( + Id( + a, + ), + Mul( + Id( + b, + ), + Mul( + Id( + c, + ), + Id( + d, + ), + ), + ), + ) + "#]] + .assert_debug_eq(&term); +} + +#[test] +fn add_add_mul_add() { + let term: Expr = crate::ptt::term("a + b + c * d + e"); + expect_test::expect![[r#" + Add( + Id( + a, + ), + Add( + Id( + b, + ), + Add( + Mul( + Id( + c, + ), + Id( + d, + ), + ), + Id( + e, + ), + ), + ), + ) + "#]] + .assert_debug_eq(&term); +}