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)); } }