Skip to content

Instantly share code, notes, and snippets.

@hilios
Created February 1, 2024 17:25
Show Gist options
  • Save hilios/4cd9c89afe2140505225c87a5c7e267b to your computer and use it in GitHub Desktop.
Save hilios/4cd9c89afe2140505225c87a5c7e267b to your computer and use it in GitHub Desktop.
RPN Calculator in RUST
use std::collections::VecDeque;
use crate::expr::Expr::*;
#[derive(Debug, Clone)]
enum Expr {
Number(f64),
Add(Box<Expr>, Box<Expr>),
Subtract(Box<Expr>, Box<Expr>),
Divide(Box<Expr>, Box<Expr>),
Multiply(Box<Expr>, Box<Expr>),
Sqrt(Box<Expr>),
}
impl Expr {
pub fn eval(&self) -> f64 {
match self {
Number(value) => *value,
Add(x, y) => x.eval() + y.eval(),
Subtract(x, y) => x.eval() - y.eval(),
Divide(x, y) => x.eval() / y.eval(),
Multiply(x, y) => x.eval() * y.eval(),
Sqrt(x) => x.eval().sqrt(),
}
}
pub fn parse_exprs(tokens: &str) -> Result<VecDeque<Expr>, String> {
let mut memory = VecDeque::with_capacity(100);
for token in tokens.split_ascii_whitespace() {
let expr = Expr::parse_expr(token, &mut memory)?;
memory.push_front(expr);
}
Ok(memory)
}
fn parse_expr(token: &str, memory: &mut VecDeque<Expr>) -> Result<Expr, String> {
match token {
"+" => {
let x = memory.pop_back().ok_or("Missing right operand")?;
let y = memory.pop_back().ok_or("Missing left operand")?;
Ok(Add(Box::from(x), Box::from(y)))
},
"-" => {
let x = memory.pop_back().ok_or("Missing right operand")?;
let y = memory.pop_back().ok_or("Missing left operand")?;
Ok(Subtract(Box::from(x), Box::from(y)))
},
"/" => {
let x = memory.pop_back().ok_or("Missing right operand")?;
let y = memory.pop_back().ok_or("Missing left operand")?;
Ok(Divide(Box::from(x), Box::from(y)))
},
"*" => {
let x = memory.pop_back().ok_or("Missing right operand")?;
let y = memory.pop_back().ok_or("Missing left operand")?;
Ok(Multiply(Box::from(x), Box::from(y)))
},
"sqrt" => {
let x = memory.pop_back().ok_or("Missing right operand")?;
Ok(Sqrt(Box::from(x)))
},
_ => {
let n = token.parse::<f64>()
.map_err(|_| format!("Invalid number: {}", token))?;
Ok(Number(n))
}
}
}
}
#[cfg(test)]
mod tests {
use std::collections::VecDeque;
use crate::expr::Expr;
use crate::expr::Expr::*;
#[test]
fn add() {
let expr = Add(Box::from(Number(2.0)), Box::from(Number(2.0)));
assert_eq!(expr.eval(), 4.0);
}
#[test]
fn subtract() {
let expr = Subtract(Box::from(Number(2.0)), Box::from(Number(1.0)));
assert_eq!(expr.eval(), 1.0);
}
#[test]
fn multiply() {
let expr = Multiply(Box::from(Number(2.0)), Box::from(Number(4.0)));
assert_eq!(expr.eval(), 8.0);
}
#[test]
fn divide() {
let expr = Divide(Box::from(Number(4.0)), Box::from(Number(2.0)));
assert_eq!(expr.eval(), 2.0);
}
#[test]
fn sqrt() {
let expr = Sqrt(Box::from(Number(25.0)));
assert_eq!(expr.eval(), 5.0);
}
#[test]
fn parse_number() {
let mut memory: VecDeque<Expr> = VecDeque::with_capacity(100);
let expr = Expr::parse_expr("1", &mut memory).unwrap();
assert_eq!(expr.eval(), 1.0);
}
#[test]
fn parse_add() {
let mut mem = Expr::parse_exprs("1 1 +").unwrap();
let expr = mem.pop_front().unwrap();
assert_eq!(expr.eval(), 2.0);
}
#[test]
fn parse_sub() {
let mut mem = Expr::parse_exprs("2 1 -").unwrap();
let expr = mem.pop_front().unwrap();
assert_eq!(expr.eval(), 1.0);
}
#[test]
fn parse_mult() {
let mut mem = Expr::parse_exprs("2 2 *").unwrap();
let expr = mem.pop_front().unwrap();
assert_eq!(expr.eval(), 4.0);
}
#[test]
fn parse_div() {
let mut mem = Expr::parse_exprs("4 2 /").unwrap();
let expr = mem.pop_front().unwrap();
assert_eq!(expr.eval(), 2.0);
}
#[test]
fn parse_sqrt() {
let mut mem = Expr::parse_exprs("25 sqrt").unwrap();
let expr = mem.pop_front().unwrap();
assert_eq!(expr.eval(), 5.0);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment