use super::Arity;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ArithmeticOperator {
Add,
Subtract,
Multiply,
Divide,
Modulo,
ModuloWord,
Increment,
Decrement,
}
impl ArithmeticOperator {
pub const ALL: [Self; 8] = [
Self::Add,
Self::Subtract,
Self::Multiply,
Self::Divide,
Self::Modulo,
Self::ModuloWord,
Self::Increment,
Self::Decrement,
];
#[must_use]
pub const fn symbol(self) -> &'static str {
match self {
Self::Add => "+",
Self::Subtract => "-",
Self::Multiply => "*",
Self::Divide => "/",
Self::Modulo => "%",
Self::ModuloWord => "MOD",
Self::Increment => "++",
Self::Decrement => "--",
}
}
#[must_use]
pub const fn arity(self) -> Arity {
match self {
Self::Increment | Self::Decrement => Arity::Unary,
_ => Arity::Binary,
}
}
#[must_use]
pub const fn precedence(self) -> u8 {
match self {
Self::Increment | Self::Decrement => 1,
Self::Multiply | Self::Divide | Self::Modulo | Self::ModuloWord => 2,
Self::Add | Self::Subtract => 3,
}
}
#[must_use]
pub const fn alternate_spelling(self) -> Option<Self> {
Some(match self {
Self::Modulo => Self::ModuloWord,
Self::ModuloWord => Self::Modulo,
_ => return None,
})
}
}
#[cfg(test)]
mod tests {
use super::ArithmeticOperator;
use crate::expression::Arity;
#[test]
fn multiplication_binds_tighter_than_addition() {
assert!(ArithmeticOperator::Multiply.precedence() < ArithmeticOperator::Add.precedence());
}
#[test]
fn the_two_remainder_spellings_agree() {
let symbol = ArithmeticOperator::Modulo;
let word = ArithmeticOperator::ModuloWord;
assert_eq!(symbol.precedence(), word.precedence());
assert_eq!(symbol.arity(), word.arity());
assert_eq!(symbol.alternate_spelling(), Some(word));
assert_eq!(word.alternate_spelling(), Some(symbol));
}
#[test]
fn increment_and_decrement_take_one_operand() {
assert_eq!(ArithmeticOperator::Increment.arity(), Arity::Unary);
assert_eq!(ArithmeticOperator::Decrement.arity(), Arity::Unary);
assert_eq!(ArithmeticOperator::Add.arity(), Arity::Binary);
}
#[test]
fn no_operator_spells_exponentiation() {
assert!(ArithmeticOperator::ALL.iter().all(|op| op.symbol() != "^"));
}
}