diff --git a/.gitignore b/.gitignore index ea8c4bf..96ef6c0 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +Cargo.lock diff --git a/src/main.rs b/src/main.rs index e1eee82..212577f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,13 +23,24 @@ enum Token { Divide, Add, Subtract, - Number(f64), + Modulus, Power, + Separator, + Number(f64), + Function(FunctionType), +} + +#[derive(Clone, PartialEq, Debug)] +enum FunctionType { + Sine, + Cosine, + Max, + Min, } fn main() { let arg = match std::env::args().nth(1) { - Some(v) => v.replace(" ", ""), + Some(v) => v, None => { eprintln!("missing argument, Usage: expr, Example: 1 + 1 * 3 (6 - 4)^2"); std::process::exit(1); @@ -40,6 +51,7 @@ fn main() { } fn compute(input: &str) -> f64 { + let input = &input.replace(' ', ""); let tokens = match tokenize(input) { Ok(v) => v, Err(e) => { @@ -50,12 +62,10 @@ fn compute(input: &str) -> f64 { let tokens = implicit_operations(tokens); let tokens = infix_to_postfix(tokens); + println!("tokens: {:?}", tokens); - // print!("postfix expression: "); - // tokens.iter().for_each(|t| print!("{t} ")); - // println!(); match calculate(tokens) { - Ok(v) => return v, + Ok(v) => v, Err(e) => { eprintln!("failed to calculate result: {:?}", e); std::process::exit(1); @@ -72,8 +82,16 @@ impl std::fmt::Display for Token { Token::Multiply => write!(f, "*"), Token::Divide => write!(f, "/"), Token::Power => write!(f, "^"), + Token::Modulus => write!(f, "%"), Token::LeftParenthesis => write!(f, "("), Token::RightParenthesis => write!(f, ")"), + Token::Separator => write!(f, ","), + Token::Function(function) => match function { + FunctionType::Sine => write!(f, "sin"), + FunctionType::Cosine => write!(f, "cos"), + FunctionType::Max => write!(f, "max"), + FunctionType::Min => write!(f, "min"), + }, } } } @@ -85,69 +103,121 @@ fn tokenize(input: &str) -> Result, TokenizeError> { let token = match c { '(' => Token::LeftParenthesis, ')' => Token::RightParenthesis, - '*' => Token::Multiply, - '/' => Token::Divide, + '*' | '×' => Token::Multiply, + '/' | '÷' => Token::Divide, '+' => Token::Add, - '-' => Token::Subtract, + '-' | '−' => Token::Subtract, '^' => Token::Power, + '%' => Token::Modulus, + ',' => Token::Separator, + 'π' => Token::Number(std::f64::consts::PI), + 'e' => Token::Number(std::f64::consts::E), + 'τ' => Token::Number(std::f64::consts::TAU), _ => { 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(parse_buffer(&buf)?); + buf.clear(); } 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); - } + tokens.push(parse_buffer(&buf)?); + buf.clear(); } Ok(tokens) } +fn parse_buffer(buf: &str) -> Result { + match buf { + "sin" => Ok(Token::Function(FunctionType::Sine)), + "cos" => Ok(Token::Function(FunctionType::Cosine)), + "max" => Ok(Token::Function(FunctionType::Max)), + "min" => Ok(Token::Function(FunctionType::Min)), + _ => { + if let Ok(number) = buf.parse() { + Ok(Token::Number(number)) + } else { + Err(TokenizeError::NumberParseError) + } + } + } +} + fn implicit_operations(tokens: Vec) -> Vec { - if tokens.is_empty() { + if tokens.len() < 2 { return tokens; } let mut new_tokens = vec![tokens[0].clone()]; - let mut prev = &tokens[0]; + let mut prev = tokens[0].clone(); for token in tokens.iter().skip(1) { - let /* mut */neg = false; + let mut token = token.clone(); 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), + Token::Subtract => match token { + Token::Add => { + new_tokens.pop(); + token = Token::Subtract + } + Token::Subtract => { + new_tokens.pop(); + token = Token::Add; + } + _ => (), + }, + 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()); + new_tokens.push(token.clone()); + prev = token.clone(); + } + let mut tokens = new_tokens.iter(); + let mut new_tokens = Vec::new(); + let (mut a, mut b) = (tokens.next().unwrap(), tokens.next().unwrap()); + new_tokens.push(a.clone()); + new_tokens.push(b.clone()); + + if new_tokens[0] == Token::Subtract { + if let Token::Number(n) = new_tokens[1] { + new_tokens.pop(); + new_tokens.pop(); + new_tokens.push(Token::Number(-n)); + } else if new_tokens[1] == Token::LeftParenthesis { + new_tokens.pop(); + new_tokens.pop(); + new_tokens.push(Token::Number(-1.)); + new_tokens.push(Token::Multiply); + new_tokens.push(Token::LeftParenthesis); } - prev = token; + } + + for t in tokens { + let c = t; + match a { + Token::Number(_) => new_tokens.push(c.clone()), + _ => { + if b == &Token::Subtract { + if let Token::Number(n) = c { + new_tokens.pop(); + new_tokens.push(Token::Number(-n)); + } else if c == &Token::LeftParenthesis { + new_tokens.pop(); + new_tokens.push(Token::Number(-1.)); + new_tokens.push(Token::Multiply); + new_tokens.push(Token::LeftParenthesis); + } + } else { + new_tokens.push(c.clone()); + } + } + } + a = b; + b = c; } new_tokens } @@ -174,12 +244,18 @@ fn infix_to_postfix(tokens: Vec) -> Vec { assert!(op_stack.last().unwrap() == &Token::LeftParenthesis); op_stack.pop(); } + Token::Function(_) => op_stack.push(token.clone()), + Token::Separator => { + while op_stack.last().unwrap() != &Token::LeftParenthesis { + output.push(op_stack.pop().unwrap()); + } + } op => { while let Some(op2) = op_stack.last() { if op2 != &Token::LeftParenthesis - && (precedence(&op2) > precedence(&op) - || (precedence(&op) == precedence(&op2) - && associativity(&op) == Associativity::Left)) + && (precedence(op2) > precedence(op) + || (precedence(op) == precedence(op2) + && associativity(op) == Associativity::Left)) { output.push(op_stack.pop().unwrap()); } else { @@ -200,10 +276,11 @@ fn infix_to_postfix(tokens: Vec) -> Vec { fn precedence(token: &Token) -> u8 { match token { Token::Add | Token::Subtract => 0, - Token::Multiply | Token::Divide => 1, + Token::Multiply | Token::Divide | Token::Modulus => 1, Token::Power => 2, - Token::LeftParenthesis | Token::RightParenthesis => 3, + Token::LeftParenthesis | Token::RightParenthesis | Token::Function(_) => 3, Token::Number(_) => unreachable!(), + Token::Separator => unreachable!(), } } @@ -217,34 +294,58 @@ fn calculate(tokens: Vec) -> Result { None => return Err(CalculateError::EmptyStack), } } - while stack.len() != 1 && tokens.len() != 0 { + while tokens.len() != 0 { let mut token = tokens.next().unwrap(); - loop { - match token { - Token::Number(_) => stack.push(token.clone()), - _ => break, - } + while let Token::Number(_) = token { + stack.push(token.clone()); token = tokens.next().unwrap(); } + let a = stack.pop().ok_or(CalculateError::PostfixExpectedNumbers)?; + + if stack.is_empty() { + if let Token::Function(f) = token { + if let Token::Number(n) = a { + let n = match f { + FunctionType::Sine => n.sin(), + FunctionType::Cosine => n.cos(), + + _ => unreachable!(), + }; + stack.push(Token::Number(n)); + continue; + } else { + return Err(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))), + + let n = match token { + Token::Add => a + b, + Token::Subtract => a - b, + Token::Multiply => a * b, + Token::Divide => a / b, + Token::Power => a.powf(b), + Token::Modulus => a % b, + Token::Function(FunctionType::Max) => a.max(b), + Token::Function(FunctionType::Min) => a.min(b), _ => unreachable!(), - } + }; + stack.push(Token::Number(n)); } + + assert!(stack.len() == 1); + if let Token::Number(n) = stack[0] { - return Ok(n); + Ok(n) } else { - return Err(CalculateError::EmptyStack); + Err(CalculateError::EmptyStack) } } @@ -256,10 +357,25 @@ mod tests { 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::Modulus])); 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("e"), Ok(vec![Token::Number(std::f64::consts::E)])); + assert_eq!(tokenize("π"), Ok(vec![Token::Number(std::f64::consts::PI)])); + assert_eq!( + tokenize("max"), + Ok(vec![Token::Function(FunctionType::Max)]) + ); + assert_eq!( + tokenize("sin"), + Ok(vec![Token::Function(FunctionType::Sine)]) + ); + assert_eq!( + tokenize("cos"), + Ok(vec![Token::Function(FunctionType::Cosine)]) + ); assert_eq!( tokenize("3.14159265358979323"), Ok(vec![Token::Number(3.14159265358979323)]) @@ -268,8 +384,8 @@ mod tests { 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("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.)]) @@ -291,6 +407,7 @@ mod tests { } #[test] fn implicit_test() { + // (3)2 == (3)*2 assert_eq!( implicit_operations(vec![ Token::LeftParenthesis, @@ -306,6 +423,8 @@ mod tests { Token::Number(2.), ] ); + + // 3(2) == 3*(2) assert_eq!( implicit_operations(vec![ Token::Number(3.), @@ -321,6 +440,8 @@ mod tests { Token::RightParenthesis, ] ); + + // (3)(2) = (3)*(2) assert_eq!( implicit_operations(vec![ Token::LeftParenthesis, @@ -340,9 +461,84 @@ mod tests { Token::RightParenthesis ] ); + + // 3 -- 2 == 3 + 2 + assert_eq!( + implicit_operations(vec![ + Token::Number(3.), + Token::Subtract, + Token::Subtract, + Token::Number(2.), + ]), + vec![Token::Number(3.), Token::Add, Token::Number(2.),] + ); + + assert_eq!( + implicit_operations(vec![Token::Subtract, Token::Number(3.),]), + vec![Token::Number(-3.)] + ); + + assert_eq!( + implicit_operations(vec![ + Token::Subtract, + Token::LeftParenthesis, + Token::Number(3.), + Token::RightParenthesis, + ]), + vec![ + Token::Number(-1.), + Token::Multiply, + Token::LeftParenthesis, + Token::Number(3.), + Token::RightParenthesis, + ] + ); + + assert_eq!( + implicit_operations(vec![ + Token::LeftParenthesis, + Token::Subtract, + Token::LeftParenthesis, + Token::Number(3.), + Token::RightParenthesis, + Token::RightParenthesis, + ]), + vec![ + Token::LeftParenthesis, + Token::Number(-1.), + Token::Multiply, + Token::LeftParenthesis, + Token::Number(3.), + Token::RightParenthesis, + Token::RightParenthesis, + ] + ); + + assert_eq!( + implicit_operations(vec![ + Token::Subtract, + Token::LeftParenthesis, + Token::Subtract, + Token::LeftParenthesis, + Token::Number(3.), + Token::RightParenthesis, + Token::RightParenthesis, + ]), + vec![ + Token::Number(-1.), + Token::Multiply, + Token::LeftParenthesis, + Token::Number(-1.), + Token::Multiply, + Token::LeftParenthesis, + Token::Number(3.), + Token::RightParenthesis, + Token::RightParenthesis, + ] + ); } #[test] - fn post_to_infix_test() { + fn infix_to_postfix_test() { assert_eq!( infix_to_postfix(vec![Token::Number(3.), Token::Add, Token::Number(2.)]), vec![Token::Number(3.), Token::Number(2.), Token::Add,] @@ -399,7 +595,24 @@ mod tests { #[test] fn test_compute() { - assert_eq!(compute(&"(6 - 2)".replace(" ", "")), 4.); - // assert_eq!(compute(&"(6 - -2)".replace(" ", "")), 8.); + assert_eq!(compute("(6 - 2)"), 4.); + assert_eq!(compute("3E2"), 300.); + assert_eq!(compute("4(3 - 1)^2"), 16.); + assert_eq!(compute("(6 - -2)"), 8.); + assert_eq!(compute("sin(3)"), 3_f64.sin()); + assert_eq!(compute("sin(3 - 1)"), 2_f64.sin()); + assert_eq!(compute("cos(3 - 1)"), 2_f64.cos()); + assert_eq!( + compute("3 + 4 × 2 ÷ ( 1 − 5 ) ^ 2 ^ 3"), + 3. + (4. * 2.) / (1_f64 - 5.).powf((2_f64).powf(3.)) + ); + assert_eq!( + compute("sin ( max ( 2, 3 ) ÷ 3 × π )"), + std::f64::consts::PI.sin() + ); + assert_eq!(compute("min(2, 3)"), 2.); + assert_eq!(compute("max(2, 3)"), 3.); + assert_eq!(compute("3 +- 2"), 1.); + assert_eq!(compute("3 -+ 2"), 1.); } }