Create binding power algebra handling associativity

This commit is contained in:
cdo committed 2026-09-26 09:41:44 +01:00
1 parent fbab9c521d
commit aad169c24f
2 files changed
+188

No files matched your search

+187
View File
@@ -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<std::cmp::Ordering>
{
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));
}
}
+1
View File
@@ -1,4 +1,5 @@
pub mod token;
pub mod expression;
pub mod binding;
pub mod lex;