From aad169c24f2d4d1a9401dbc1f6737de5e876c6fb Mon Sep 17 00:00:00 2001 From: Camden Dixie O'Brien Date: Sat, 26 Sep 2026 09:41:44 +0100 Subject: [PATCH] Create binding power algebra handling associativity --- src/binding.rs | 187 +++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 2 files changed, 188 insertions(+) create mode 100644 src/binding.rs diff --git a/src/binding.rs b/src/binding.rs new file mode 100644 index 0000000..fc00e4a --- /dev/null +++ b/src/binding.rs @@ -0,0 +1,187 @@ +use crate::token::Token; +use crate::expression::Expression; + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum Associativity { + Left, + Right, + Full, +} + +#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)] +pub enum Power { + Minimum, + Addition, + Multiplication, + Exponentiation, + Maximum, +} + +#[derive(Copy, Clone, Debug)] +pub struct Binding { + power: Power, + associativity: Associativity, +} + +impl Binding { + pub fn left(power: Power) -> Self { + Self { power, associativity: Associativity::Left } + } + + pub fn right(power: Power) -> Self { + Self { power, associativity: Associativity::Right } + } + + pub fn full(power: Power) -> Self { + Self { power, associativity: Associativity::Full } + } + + pub fn of_operator(token: &Token) -> Self { + if token.starts_expression() { + Binding::full(Power::Multiplication) + } else { + match token { + Token::Plus => Binding::full(Power::Addition), + Token::Minus => Binding::left(Power::Addition), + Token::Times => Binding::full(Power::Multiplication), + Token::Divide => Binding::left(Power::Multiplication), + Token::Caret => Binding::right(Power::Exponentiation), + Token::CloseParen => Binding::left(Power::Minimum), + _ => unreachable!(), + } + } + } + + + pub fn of_expression(expr: &Expression) -> Self { + match expr { + Expression::Sum(_, _) => Self::full(Power::Addition), + Expression::Difference(_, _) => Self::left(Power::Addition), + Expression::Product(_, _) => Self::full(Power::Multiplication), + Expression::Quotient(_, _) => Self::left(Power::Multiplication), + Expression::Exponent(_, _) => Self::right(Power::Exponentiation), + Expression::Integer(_) => Self::left(Power::Maximum), + Expression::Symbol(_) => Self::left(Power::Maximum), + } + } +} + +impl std::cmp::PartialEq for Binding { + fn eq(&self, other: &Self) -> bool + { + self.associativity == Associativity::Full + && self.power == other.power + } +} + +impl std::cmp::PartialOrd for Binding { + fn partial_cmp(&self, rhs: &Self) -> Option + { + Some(if self.power < rhs.power { + std::cmp::Ordering::Less + } else if self.power > rhs.power { + std::cmp::Ordering::Greater + } else { + match self.associativity { + Associativity::Left => std::cmp::Ordering::Less, + Associativity::Right => std::cmp::Ordering::Greater, + Associativity::Full => std::cmp::Ordering::Equal, + } + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn left_assoc_min_is_less_than_left_assoc_add() { + assert_eq!( + Binding::left(Power::Minimum).partial_cmp( + &Binding::left(Power::Addition)), + Some(std::cmp::Ordering::Less)); + } + + #[test] + fn left_assoc_add_is_less_than_left_assoc_add() { + assert_eq!( + Binding::left(Power::Addition).partial_cmp( + &Binding::left(Power::Addition)), + Some(std::cmp::Ordering::Less)); + } + + #[test] + fn left_assoc_mult_is_greater_than_left_assoc_add() { + assert_eq!( + Binding::left(Power::Multiplication).partial_cmp( + &Binding::left(Power::Addition)), + Some(std::cmp::Ordering::Greater)); + } + + #[test] + fn right_assoc_min_is_less_than_right_assoc_add() { + assert_eq!( + Binding::right(Power::Minimum).partial_cmp( + &Binding::right(Power::Addition)), + Some(std::cmp::Ordering::Less)); + } + + #[test] + fn right_assoc_add_is_greater_than_right_assoc_add() { + assert_eq!( + Binding::right(Power::Addition).partial_cmp( + &Binding::right(Power::Addition)), + Some(std::cmp::Ordering::Greater)); + } + + #[test] + fn right_assoc_mul_is_greater_than_right_assoc_add() { + assert_eq!( + Binding::right(Power::Multiplication).partial_cmp( + &Binding::right(Power::Addition)), + Some(std::cmp::Ordering::Greater)); + } + + #[test] + fn full_assoc_min_is_less_than_full_assoc_add() { + assert_eq!( + Binding::full(Power::Minimum).partial_cmp( + &Binding::full(Power::Addition)), + Some(std::cmp::Ordering::Less)); + } + + #[test] + fn full_assoc_add_is_equal_to_full_assoc_add() { + assert_eq!( + Binding::full(Power::Addition).partial_cmp( + &Binding::full(Power::Addition)), + Some(std::cmp::Ordering::Equal)); + } + + #[test] + fn full_assoc_mul_is_greater_than_full_assoc_add() { + assert_eq!( + Binding::full(Power::Multiplication).partial_cmp( + &Binding::full(Power::Addition)), + Some(std::cmp::Ordering::Greater)); + } + + #[test] + fn left_assoc_min_is_not_equal_to_left_assoc_min() { + assert_ne!( + Binding::left(Power::Minimum), Binding::left(Power::Minimum)); + } + + #[test] + fn right_assoc_min_is_not_equal_to_right_assoc_min() { + assert_ne!( + Binding::right(Power::Minimum), Binding::right(Power::Minimum)); + } + + #[test] + fn full_assoc_min_is_equal_to_full_assoc_min() { + assert_eq!( + Binding::full(Power::Minimum), Binding::left(Power::Minimum)); + } +} diff --git a/src/lib.rs b/src/lib.rs index 6769acd..4139ad5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ pub mod token; pub mod expression; +pub mod binding; pub mod lex;