This solution gives similar performance results than the tree based one, mostly because of the intermediate vectors.
187 lines
4.7 KiB
Rust
187 lines
4.7 KiB
Rust
use enum_map::Enum;
|
|
use std::fmt;
|
|
|
|
/// A single digit that can be wrapped in parentheses.
|
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
|
pub struct Digit {
|
|
pub value: i8,
|
|
pub has_left_parenthesis: bool,
|
|
pub has_right_parenthesis: bool,
|
|
}
|
|
|
|
impl Digit {
|
|
pub fn new(value: i8) -> Self {
|
|
Self {
|
|
value,
|
|
has_left_parenthesis: false,
|
|
has_right_parenthesis: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Digit {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
if self.has_left_parenthesis {
|
|
write!(f, "(")?;
|
|
}
|
|
write!(f, "{}", self.value)?;
|
|
if self.has_right_parenthesis {
|
|
write!(f, ")")?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl TryFrom<&str> for Digit {
|
|
type Error = ();
|
|
|
|
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
|
let mut res = Digit {
|
|
value: 0,
|
|
has_left_parenthesis: false,
|
|
has_right_parenthesis: false,
|
|
};
|
|
let mut it = value.chars();
|
|
let c = it.next().ok_or(())?;
|
|
if c == '(' {
|
|
res.has_left_parenthesis = true;
|
|
res.value = it.next().ok_or(())?.to_digit(10).ok_or(())? as i8;
|
|
} else {
|
|
res.value = c.to_digit(10).ok_or(())? as i8;
|
|
}
|
|
if let Some(c) = it.next() {
|
|
if c != ')' {
|
|
return Err(());
|
|
}
|
|
res.has_right_parenthesis = true;
|
|
}
|
|
Ok(res)
|
|
}
|
|
}
|
|
|
|
/// An operator that can be applied between two terms.
|
|
#[derive(Debug, PartialEq, Eq, Copy, Clone, Enum)]
|
|
pub enum Operator {
|
|
Add,
|
|
Subtract,
|
|
Multiply,
|
|
Divide,
|
|
}
|
|
|
|
impl Operator {
|
|
pub fn precedence(&self) -> u8 {
|
|
match self {
|
|
Operator::Add | Operator::Subtract => 1,
|
|
Operator::Multiply | Operator::Divide => 2,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Operator {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Operator::Add => write!(f, "+"),
|
|
Operator::Subtract => write!(f, "-"),
|
|
Operator::Multiply => write!(f, "*"),
|
|
Operator::Divide => write!(f, "/"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TryFrom<char> for Operator {
|
|
type Error = ();
|
|
|
|
fn try_from(value: char) -> Result<Self, Self::Error> {
|
|
match value {
|
|
'+' => Ok(Operator::Add),
|
|
'-' => Ok(Operator::Subtract),
|
|
'*' => Ok(Operator::Multiply),
|
|
'/' => Ok(Operator::Divide),
|
|
_ => Err(()),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A single piece of a mathematical expression.
|
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
|
pub enum Tile {
|
|
Digit(Digit),
|
|
Operator(Operator),
|
|
Equals,
|
|
}
|
|
|
|
impl TryFrom<&str> for Tile {
|
|
type Error = ();
|
|
|
|
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
|
if let Ok(digit) = Digit::try_from(value) {
|
|
return Ok(Tile::Digit(digit));
|
|
}
|
|
match value {
|
|
"=" => Ok(Tile::Equals),
|
|
_ => Ok(Tile::Operator(value.chars().next().ok_or(())?.try_into()?)),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Tile {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Tile::Digit(digit) => write!(f, "{digit}"),
|
|
Tile::Operator(operator) => write!(f, "{operator}"),
|
|
Tile::Equals => write!(f, "="),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn digit_from_str() {
|
|
assert_eq!(Digit::try_from("1"), Ok(Digit::new(1)));
|
|
assert_eq!(
|
|
Digit::try_from("(5"),
|
|
Ok(Digit {
|
|
value: 5,
|
|
has_left_parenthesis: true,
|
|
has_right_parenthesis: false,
|
|
})
|
|
);
|
|
assert_eq!(
|
|
Digit::try_from("8)"),
|
|
Ok(Digit {
|
|
value: 8,
|
|
has_left_parenthesis: false,
|
|
has_right_parenthesis: true,
|
|
})
|
|
);
|
|
assert_eq!(Digit::try_from("+"), Err(()));
|
|
assert_eq!(Digit::try_from("1("), Err(()));
|
|
assert_eq!(Digit::try_from(""), Err(()));
|
|
}
|
|
|
|
#[test]
|
|
fn operator_from_str() {
|
|
assert_eq!(Operator::try_from('+'), Ok(Operator::Add));
|
|
assert_eq!(Operator::try_from('-'), Ok(Operator::Subtract));
|
|
assert_eq!(Operator::try_from('²'), Err(()));
|
|
}
|
|
|
|
#[test]
|
|
fn piece_from_str() {
|
|
assert_eq!(Tile::try_from("+"), Ok(Tile::Operator(Operator::Add)));
|
|
assert_eq!(
|
|
Tile::try_from("(7)"),
|
|
Ok(Tile::Digit(Digit {
|
|
value: 7,
|
|
has_left_parenthesis: true,
|
|
has_right_parenthesis: true,
|
|
}))
|
|
);
|
|
assert_eq!(Tile::try_from("="), Ok(Tile::Equals));
|
|
assert_eq!(Tile::try_from(""), Err(()));
|
|
}
|
|
}
|