From 53314ddaadf0328d86f061d43f77d8389873d3fd Mon Sep 17 00:00:00 2001 From: Vegard Matthey Date: Thu, 21 Mar 2024 12:36:17 +0100 Subject: [PATCH] initial --- .gitignore | 1 + Cargo.toml | 8 ++ src/main.rs | 405 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 414 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..e145edd --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "calc" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..e1eee82 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,405 @@ +#[derive(Debug, PartialEq)] +enum TokenizeError { + NumberParseError, +} + +#[derive(Debug)] +enum CalculateError { + PostfixExpectedNumbers, + EmptyStack, +} + +#[derive(PartialEq)] +enum Associativity { + Right, + Left, +} + +#[derive(Debug, PartialEq, Clone)] +enum Token { + LeftParenthesis, + RightParenthesis, + Multiply, + Divide, + Add, + Subtract, + Number(f64), + Power, +} + +fn main() { + let arg = match std::env::args().nth(1) { + Some(v) => v.replace(" ", ""), + None => { + eprintln!("missing argument, Usage: expr, Example: 1 + 1 * 3 (6 - 4)^2"); + std::process::exit(1); + } + }; + + println!("result: {}", compute(&arg)); +} + +fn compute(input: &str) -> f64 { + let tokens = match tokenize(input) { + Ok(v) => v, + Err(e) => { + eprintln!("failed to parse tokens: {:?}", e); + std::process::exit(1); + } + }; + + let tokens = implicit_operations(tokens); + let tokens = infix_to_postfix(tokens); + + // print!("postfix expression: "); + // tokens.iter().for_each(|t| print!("{t} ")); + // println!(); + match calculate(tokens) { + Ok(v) => return v, + Err(e) => { + eprintln!("failed to calculate result: {:?}", e); + std::process::exit(1); + } + } +} + +impl std::fmt::Display for Token { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Token::Number(n) => write!(f, "{n}"), + Token::Add => write!(f, "+"), + Token::Subtract => write!(f, "-"), + Token::Multiply => write!(f, "*"), + Token::Divide => write!(f, "/"), + Token::Power => write!(f, "^"), + Token::LeftParenthesis => write!(f, "("), + Token::RightParenthesis => write!(f, ")"), + } + } +} + +fn tokenize(input: &str) -> Result, TokenizeError> { + let mut buf = String::new(); + let mut tokens = Vec::new(); + for c in input.chars() { + let token = match c { + '(' => Token::LeftParenthesis, + ')' => Token::RightParenthesis, + '*' => Token::Multiply, + '/' => Token::Divide, + '+' => Token::Add, + '-' => Token::Subtract, + '^' => Token::Power, + _ => { + buf.push(c); + continue; + } + }; + if !buf.is_empty() { + if let Ok(number) = buf.parse() { + tokens.push(Token::Number(number)); + buf.clear(); + } else { + return Err(TokenizeError::NumberParseError); + } + } + tokens.push(token); + } + if !buf.is_empty() { + if let Ok(number) = buf.parse() { + tokens.push(Token::Number(number)); + buf.clear(); + } else { + return Err(TokenizeError::NumberParseError); + } + } + Ok(tokens) +} + +fn implicit_operations(tokens: Vec) -> Vec { + if tokens.is_empty() { + return tokens; + } + let mut new_tokens = vec![tokens[0].clone()]; + let mut prev = &tokens[0]; + for token in tokens.iter().skip(1) { + let /* mut */neg = false; + match prev { + // &Token::Number(_) => { + // match token { + // if prev == &Token::Subtract { + // new_tokens.pop(); + // neg = true; + // } + // } + // } + &Token::RightParenthesis | &Token::Number(_) => match token { + &Token::LeftParenthesis | &Token::Number(_) => new_tokens.push(Token::Multiply), + _ => (), + }, + _ => (), + } + if neg { + match token { + &Token::Number(a) => new_tokens.push(Token::Number(-a)), + _ => unreachable!(), + } + } else { + new_tokens.push(token.clone()); + } + prev = token; + } + new_tokens +} + +fn associativity(token: &Token) -> Associativity { + match token { + Token::Power => Associativity::Right, + Token::Number(_) => unreachable!(), + _ => Associativity::Left, + } +} + +fn infix_to_postfix(tokens: Vec) -> Vec { + let mut output = Vec::new(); + let mut op_stack: Vec = Vec::new(); + for token in tokens.iter() { + match token { + Token::Number(n) => output.push(Token::Number(*n)), + Token::LeftParenthesis => op_stack.push(Token::LeftParenthesis), + Token::RightParenthesis => { + while op_stack.last().unwrap() != &Token::LeftParenthesis { + output.push(op_stack.pop().unwrap()); + } + assert!(op_stack.last().unwrap() == &Token::LeftParenthesis); + op_stack.pop(); + } + op => { + while let Some(op2) = op_stack.last() { + if op2 != &Token::LeftParenthesis + && (precedence(&op2) > precedence(&op) + || (precedence(&op) == precedence(&op2) + && associativity(&op) == Associativity::Left)) + { + output.push(op_stack.pop().unwrap()); + } else { + break; + } + } + op_stack.push(op.clone()); + } + } + } + while !op_stack.is_empty() { + assert!(op_stack.last().unwrap() != &Token::LeftParenthesis); + output.push(op_stack.pop().unwrap()); + } + output +} + +fn precedence(token: &Token) -> u8 { + match token { + Token::Add | Token::Subtract => 0, + Token::Multiply | Token::Divide => 1, + Token::Power => 2, + Token::LeftParenthesis | Token::RightParenthesis => 3, + Token::Number(_) => unreachable!(), + } +} + +fn calculate(tokens: Vec) -> Result { + let mut stack = Vec::new(); + let mut tokens = tokens.iter(); + if tokens.len() == 1 { + match tokens.next() { + Some(Token::Number(n)) => return Ok(*n), + Some(_) => return Err(CalculateError::PostfixExpectedNumbers), + None => return Err(CalculateError::EmptyStack), + } + } + while stack.len() != 1 && tokens.len() != 0 { + let mut token = tokens.next().unwrap(); + loop { + match token { + Token::Number(_) => stack.push(token.clone()), + _ => break, + } + token = tokens.next().unwrap(); + } + let a = stack.pop().ok_or(CalculateError::PostfixExpectedNumbers)?; + let b = stack.pop().ok_or(CalculateError::PostfixExpectedNumbers)?; + let (a, b) = match (a, b) { + (Token::Number(a), Token::Number(b)) => (b, a), + _ => unreachable!(), + }; + match token { + Token::Add => stack.push(Token::Number(a + b)), + Token::Subtract => stack.push(Token::Number(a - b)), + Token::Multiply => stack.push(Token::Number(a * b)), + Token::Divide => stack.push(Token::Number(a / b)), + Token::Power => stack.push(Token::Number(a.powf(b))), + _ => unreachable!(), + } + } + if let Token::Number(n) = stack[0] { + return Ok(n); + } else { + return Err(CalculateError::EmptyStack); + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn token_test() { + assert_eq!(tokenize("("), Ok(vec![Token::LeftParenthesis])); + assert_eq!(tokenize(")"), Ok(vec![Token::RightParenthesis])); + assert_eq!(tokenize("*"), Ok(vec![Token::Multiply])); + assert_eq!(tokenize("/"), Ok(vec![Token::Divide])); + assert_eq!(tokenize("+"), Ok(vec![Token::Add])); + assert_eq!(tokenize("-"), Ok(vec![Token::Subtract])); + assert_eq!(tokenize("3"), Ok(vec![Token::Number(3.)])); + assert_eq!( + tokenize("3.14159265358979323"), + Ok(vec![Token::Number(3.14159265358979323)]) + ); + assert_eq!(tokenize("0."), Ok(vec![Token::Number(0.)])); + assert_eq!(tokenize("."), Err(TokenizeError::NumberParseError)); + assert_eq!(tokenize(".."), Err(TokenizeError::NumberParseError)); + assert_eq!(tokenize(".1."), Err(TokenizeError::NumberParseError)); + assert_eq!(tokenize("1e3"), Ok(vec![Token::Number(1000.)])); + assert_eq!(tokenize("e3"), Err(TokenizeError::NumberParseError)); + assert_eq!( + tokenize("1+1"), + Ok(vec![Token::Number(1.), Token::Add, Token::Number(1.)]) + ); + assert_eq!( + tokenize("1*1"), + Ok(vec![Token::Number(1.), Token::Multiply, Token::Number(1.)]) + ); + assert_eq!( + tokenize("(1*1)"), + Ok(vec![ + Token::LeftParenthesis, + Token::Number(1.), + Token::Multiply, + Token::Number(1.), + Token::RightParenthesis, + ]) + ); + } + #[test] + fn implicit_test() { + assert_eq!( + implicit_operations(vec![ + Token::LeftParenthesis, + Token::Number(3.), + Token::RightParenthesis, + Token::Number(2.), + ]), + vec![ + Token::LeftParenthesis, + Token::Number(3.), + Token::RightParenthesis, + Token::Multiply, + Token::Number(2.), + ] + ); + assert_eq!( + implicit_operations(vec![ + Token::Number(3.), + Token::LeftParenthesis, + Token::Number(2.), + Token::RightParenthesis, + ]), + vec![ + Token::Number(3.), + Token::Multiply, + Token::LeftParenthesis, + Token::Number(2.), + Token::RightParenthesis, + ] + ); + assert_eq!( + implicit_operations(vec![ + Token::LeftParenthesis, + Token::Number(3.), + Token::RightParenthesis, + Token::LeftParenthesis, + Token::Number(2.), + Token::RightParenthesis + ]), + vec![ + Token::LeftParenthesis, + Token::Number(3.), + Token::RightParenthesis, + Token::Multiply, + Token::LeftParenthesis, + Token::Number(2.), + Token::RightParenthesis + ] + ); + } + #[test] + fn post_to_infix_test() { + assert_eq!( + infix_to_postfix(vec![Token::Number(3.), Token::Add, Token::Number(2.)]), + vec![Token::Number(3.), Token::Number(2.), Token::Add,] + ); + assert_eq!( + infix_to_postfix(vec![Token::Number(3.), Token::Add, Token::Number(2.)]), + vec![Token::Number(3.), Token::Number(2.), Token::Add,] + ); + assert_eq!( + // 3 + 4 × 2 ÷ ( 1 − 5 ) ^ 2 ^ 3 + infix_to_postfix(vec![ + Token::Number(3.), + Token::Add, + Token::Number(4.), + Token::Multiply, + Token::Number(2.), + Token::Divide, + Token::LeftParenthesis, + Token::Number(1.), + Token::Subtract, + Token::Number(5.), + Token::RightParenthesis, + Token::Power, + Token::Number(2.), + Token::Power, + Token::Number(3.), + ]), + // 3 4 2 × 1 5 − 2 3 ^ ^ ÷ + + vec![ + Token::Number(3.), + Token::Number(4.), + Token::Number(2.), + Token::Multiply, + Token::Number(1.), + Token::Number(5.), + Token::Subtract, + Token::Number(2.), + Token::Number(3.), + Token::Power, + Token::Power, + Token::Divide, + Token::Add, + ] + ); + } + + #[test] + fn test_calculate() { + assert_eq!( + calculate(vec![Token::Number(3.), Token::Number(2.), Token::Multiply]).unwrap(), + 6. + ); + } + + #[test] + fn test_compute() { + assert_eq!(compute(&"(6 - 2)".replace(" ", "")), 4.); + // assert_eq!(compute(&"(6 - -2)".replace(" ", "")), 8.); + } +}