773 lines
24 KiB
Rust
773 lines
24 KiB
Rust
use std::{io, io::Write};
|
||
|
||
#[derive(Debug, PartialEq)]
|
||
enum TokenizeError {
|
||
NumberParseError(String),
|
||
}
|
||
|
||
#[derive(Debug, PartialEq)]
|
||
enum InfixToPostfixError {
|
||
ExpectedOperator,
|
||
ExpectedLeftParenthesis,
|
||
}
|
||
|
||
#[derive(Debug, PartialEq)]
|
||
enum CalculateError {
|
||
PostfixExpectedNumbers,
|
||
EmptyStack,
|
||
}
|
||
|
||
#[derive(PartialEq)]
|
||
enum Associativity {
|
||
Right,
|
||
Left,
|
||
}
|
||
|
||
#[derive(Debug, PartialEq, Clone)]
|
||
enum Token {
|
||
LeftParenthesis,
|
||
RightParenthesis,
|
||
Multiply,
|
||
Divide,
|
||
Add,
|
||
Subtract,
|
||
Modulus,
|
||
Power,
|
||
Separator,
|
||
Number(f64),
|
||
Function(FunctionType),
|
||
}
|
||
|
||
#[derive(Clone, PartialEq, Debug)]
|
||
enum FunctionType {
|
||
Sine,
|
||
Cosine,
|
||
NaturalLog,
|
||
Log10,
|
||
Max,
|
||
Min,
|
||
SquareRoot,
|
||
}
|
||
|
||
fn main() {
|
||
if std::env::args().len() == 1 {
|
||
loop {
|
||
print!("> ");
|
||
io::stdout().flush().expect("failed to flush stdout");
|
||
|
||
let mut input = String::new();
|
||
io::stdin()
|
||
.read_line(&mut input)
|
||
.expect("Failed to read line");
|
||
|
||
let result = compute(&input);
|
||
print_result(result);
|
||
}
|
||
} else {
|
||
let arg = match std::env::args().nth(1) {
|
||
Some(v) => v,
|
||
None => {
|
||
eprintln!("missing argument, Usage: expr, Example: 1 + 1 * 3 (6 - 4)^2");
|
||
std::process::exit(1);
|
||
}
|
||
};
|
||
let result = compute(&arg);
|
||
print_result(result);
|
||
}
|
||
}
|
||
|
||
impl Token {
|
||
fn is_operator(&self) -> bool {
|
||
use Token::*;
|
||
match self {
|
||
Multiply | Divide | Add | Subtract | Modulus | Power => true,
|
||
LeftParenthesis | RightParenthesis | Separator | Number(_) | Function(_) => false,
|
||
}
|
||
}
|
||
}
|
||
|
||
fn print_result(result: f64) {
|
||
if result > 10_000. || (result < 0.0001 && result > -0.0001) && result != 0. {
|
||
println!("{:e}", result);
|
||
} else {
|
||
println!("{result}");
|
||
}
|
||
}
|
||
|
||
fn compute(input: &str) -> f64 {
|
||
let input = &input
|
||
.trim()
|
||
.replace(' ', "")
|
||
.replace("pi", "π")
|
||
.replace("tau", "τ");
|
||
|
||
let tokens = match tokenize(input) {
|
||
Ok(v) => v,
|
||
Err(e) => {
|
||
eprintln!("failed to parse tokens: {:?}", e);
|
||
std::process::exit(1);
|
||
}
|
||
};
|
||
|
||
let tokens = match implicit_operations(tokens) {
|
||
Some(v) => v,
|
||
None => {
|
||
eprintln!("expected at least two tokens");
|
||
std::process::exit(1);
|
||
}
|
||
};
|
||
|
||
let tokens = match infix_to_postfix(tokens) {
|
||
Ok(v) => v,
|
||
Err(e) => {
|
||
eprintln!("failed to convert infix to postfix: {:?}", e);
|
||
std::process::exit(1);
|
||
}
|
||
};
|
||
|
||
match calculate(tokens) {
|
||
Ok(v) => 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::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::NaturalLog => write!(f, "ln"),
|
||
FunctionType::Max => write!(f, "max"),
|
||
FunctionType::Min => write!(f, "min"),
|
||
FunctionType::SquareRoot => write!(f, "sqrt"),
|
||
FunctionType::Log10 => write!(f, "log"),
|
||
},
|
||
}
|
||
}
|
||
}
|
||
|
||
fn tokenize(input: &str) -> Result<Vec<Token>, TokenizeError> {
|
||
let mut num_buf = String::new();
|
||
let mut fun_buf = String::new();
|
||
let mut tokens = Vec::new();
|
||
let mut prev = None;
|
||
for c in input.chars() {
|
||
let token = match c {
|
||
'(' => Token::LeftParenthesis,
|
||
')' => Token::RightParenthesis,
|
||
'*' | '×' => Token::Multiply,
|
||
'/' | '÷' => Token::Divide,
|
||
'+' => Token::Add,
|
||
'-' | '−' => {
|
||
// exception for negative exponent for scientific notation
|
||
if let Some(p) = prev {
|
||
if p == 'E' {
|
||
num_buf.push(c);
|
||
continue;
|
||
}
|
||
}
|
||
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),
|
||
'0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | 'E' | '.' => {
|
||
push_buf(&mut tokens, &mut fun_buf)?;
|
||
num_buf.push(c);
|
||
prev = Some(c);
|
||
continue;
|
||
}
|
||
_ => {
|
||
push_buf(&mut tokens, &mut num_buf)?;
|
||
fun_buf.push(c);
|
||
prev = Some(c);
|
||
continue;
|
||
}
|
||
};
|
||
push_buf(&mut tokens, &mut num_buf)?;
|
||
push_buf(&mut tokens, &mut fun_buf)?;
|
||
tokens.push(token);
|
||
prev = Some(c);
|
||
}
|
||
|
||
push_buf(&mut tokens, &mut num_buf)?;
|
||
push_buf(&mut tokens, &mut fun_buf)?;
|
||
Ok(tokens)
|
||
}
|
||
|
||
fn push_buf(tokens: &mut Vec<Token>, buf: &mut String) -> Result<(), TokenizeError> {
|
||
if !buf.is_empty() {
|
||
tokens.push(parse_buffer(buf)?);
|
||
buf.clear();
|
||
}
|
||
Ok(())
|
||
}
|
||
|
||
fn parse_buffer(buf: &str) -> Result<Token, TokenizeError> {
|
||
match buf {
|
||
"sin" => Ok(Token::Function(FunctionType::Sine)),
|
||
"cos" => Ok(Token::Function(FunctionType::Cosine)),
|
||
"ln" => Ok(Token::Function(FunctionType::NaturalLog)),
|
||
"log" => Ok(Token::Function(FunctionType::Log10)),
|
||
"max" => Ok(Token::Function(FunctionType::Max)),
|
||
"min" => Ok(Token::Function(FunctionType::Min)),
|
||
"sqrt" => Ok(Token::Function(FunctionType::SquareRoot)),
|
||
_ => {
|
||
if let Ok(number) = buf.parse() {
|
||
Ok(Token::Number(number))
|
||
} else {
|
||
Err(TokenizeError::NumberParseError(buf.to_string()))
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
fn implicit_operations(tokens: Vec<Token>) -> Option<Vec<Token>> {
|
||
if tokens.len() < 2 {
|
||
return Some(tokens);
|
||
}
|
||
let mut new_tokens = vec![tokens[0].clone()];
|
||
let mut prev = tokens[0].clone();
|
||
for token in tokens.iter().skip(1) {
|
||
let mut token = token.clone();
|
||
match prev {
|
||
Token::Subtract => match token {
|
||
Token::Add => {
|
||
new_tokens.pop();
|
||
token = Token::Subtract
|
||
}
|
||
Token::Subtract => {
|
||
new_tokens.pop();
|
||
token = Token::Add;
|
||
}
|
||
_ => (),
|
||
},
|
||
Token::Number(_) => match token {
|
||
Token::LeftParenthesis | Token::Number(_) | Token::Function(_) => {
|
||
new_tokens.push(Token::Multiply)
|
||
}
|
||
_ => (),
|
||
},
|
||
Token::RightParenthesis => match token {
|
||
Token::LeftParenthesis | Token::Number(_) => new_tokens.push(Token::Multiply),
|
||
_ => (),
|
||
},
|
||
_ => (),
|
||
}
|
||
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()?, tokens.next()?);
|
||
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);
|
||
}
|
||
}
|
||
|
||
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 {
|
||
if a.is_operator() {
|
||
new_tokens.pop();
|
||
new_tokens.push(Token::Number(-n));
|
||
} else {
|
||
new_tokens.push(c.clone());
|
||
}
|
||
} else if *c == Token::LeftParenthesis {
|
||
if *a == 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());
|
||
}
|
||
}
|
||
} else {
|
||
new_tokens.push(c.clone());
|
||
}
|
||
}
|
||
}
|
||
a = b;
|
||
b = c;
|
||
}
|
||
Some(new_tokens)
|
||
}
|
||
|
||
fn associativity(token: &Token) -> Option<Associativity> {
|
||
match token {
|
||
Token::Power => Some(Associativity::Right),
|
||
Token::Number(_) => None,
|
||
_ => Some(Associativity::Left),
|
||
}
|
||
}
|
||
|
||
fn infix_to_postfix(tokens: Vec<Token>) -> Result<Vec<Token>, InfixToPostfixError> {
|
||
let mut output = Vec::new();
|
||
let mut op_stack: Vec<Token> = 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() != Some(&Token::LeftParenthesis) {
|
||
output.push(
|
||
op_stack
|
||
.pop()
|
||
.ok_or(InfixToPostfixError::ExpectedLeftParenthesis)?,
|
||
);
|
||
}
|
||
assert!(op_stack.last() == Some(&Token::LeftParenthesis));
|
||
op_stack.pop();
|
||
}
|
||
Token::Function(_) => op_stack.push(token.clone()),
|
||
Token::Separator => {
|
||
while op_stack.last() != Some(&Token::LeftParenthesis) {
|
||
output.push(
|
||
op_stack
|
||
.pop()
|
||
.ok_or(InfixToPostfixError::ExpectedLeftParenthesis)?,
|
||
);
|
||
}
|
||
}
|
||
op => {
|
||
while let Some(op2) = op_stack.last() {
|
||
if op2 != &Token::LeftParenthesis
|
||
&& (precedence(op2) > precedence(op)
|
||
|| (precedence(op) == precedence(op2)
|
||
&& associativity(op)
|
||
.ok_or(InfixToPostfixError::ExpectedOperator)?
|
||
== Associativity::Left))
|
||
{
|
||
output.push(
|
||
op_stack
|
||
.pop()
|
||
.ok_or(InfixToPostfixError::ExpectedLeftParenthesis)?,
|
||
);
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
op_stack.push(op.clone());
|
||
}
|
||
}
|
||
}
|
||
while let Some(item) = op_stack.pop() {
|
||
output.push(item);
|
||
}
|
||
Ok(output)
|
||
}
|
||
|
||
fn precedence(token: &Token) -> u8 {
|
||
match token {
|
||
Token::Add | Token::Subtract => 0,
|
||
Token::Multiply | Token::Divide | Token::Modulus => 1,
|
||
Token::Power => 2,
|
||
Token::LeftParenthesis | Token::RightParenthesis | Token::Function(_) => 3,
|
||
Token::Number(_) => unreachable!(),
|
||
Token::Separator => unreachable!(),
|
||
}
|
||
}
|
||
|
||
fn calculate(tokens: Vec<Token>) -> Result<f64, CalculateError> {
|
||
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 let Some(mut token) = tokens.next() {
|
||
while let Token::Number(_) = token {
|
||
stack.push(token.clone());
|
||
if let Some(t) = tokens.next() {
|
||
token = t;
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
|
||
let a = stack.pop().ok_or(CalculateError::PostfixExpectedNumbers)?;
|
||
|
||
if let Token::Function(f) = token {
|
||
if let Token::Number(n) = a {
|
||
let n = match f {
|
||
FunctionType::Sine => Some(n.sin()),
|
||
FunctionType::Cosine => Some(n.cos()),
|
||
FunctionType::NaturalLog => Some(n.ln()),
|
||
FunctionType::Log10 => Some(n.log10()),
|
||
FunctionType::SquareRoot => Some(n.sqrt()),
|
||
_ => None,
|
||
};
|
||
if let Some(n) = n {
|
||
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!(),
|
||
};
|
||
|
||
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));
|
||
}
|
||
|
||
if stack.is_empty() {
|
||
return Err(CalculateError::EmptyStack);
|
||
}
|
||
if let Token::Number(n) = stack[0] {
|
||
Ok(n)
|
||
} else {
|
||
Err(CalculateError::PostfixExpectedNumbers)
|
||
}
|
||
}
|
||
|
||
#[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::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("sqrt"),
|
||
Ok(vec![Token::Function(FunctionType::SquareRoot)])
|
||
);
|
||
assert_eq!(
|
||
tokenize("ln"),
|
||
Ok(vec![Token::Function(FunctionType::Log10)])
|
||
);
|
||
assert_eq!(
|
||
tokenize("log"),
|
||
Ok(vec![Token::Function(FunctionType::Log10)])
|
||
);
|
||
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(".".to_string()))
|
||
);
|
||
assert_eq!(
|
||
tokenize(".."),
|
||
Err(TokenizeError::NumberParseError("..".to_string()))
|
||
);
|
||
assert_eq!(
|
||
tokenize(".1."),
|
||
Err(TokenizeError::NumberParseError(".1.".to_string()))
|
||
);
|
||
assert_eq!(tokenize("1E3"), Ok(vec![Token::Number(1000.)]));
|
||
assert_eq!(
|
||
tokenize("E3"),
|
||
Err(TokenizeError::NumberParseError("E3".to_string()))
|
||
);
|
||
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() {
|
||
// (3)2 == (3)*2
|
||
assert_eq!(
|
||
implicit_operations(vec![
|
||
Token::LeftParenthesis,
|
||
Token::Number(3.),
|
||
Token::RightParenthesis,
|
||
Token::Number(2.),
|
||
]),
|
||
Some(vec![
|
||
Token::LeftParenthesis,
|
||
Token::Number(3.),
|
||
Token::RightParenthesis,
|
||
Token::Multiply,
|
||
Token::Number(2.),
|
||
])
|
||
);
|
||
|
||
// 3(2) == 3*(2)
|
||
assert_eq!(
|
||
implicit_operations(vec![
|
||
Token::Number(3.),
|
||
Token::LeftParenthesis,
|
||
Token::Number(2.),
|
||
Token::RightParenthesis,
|
||
]),
|
||
Some(vec![
|
||
Token::Number(3.),
|
||
Token::Multiply,
|
||
Token::LeftParenthesis,
|
||
Token::Number(2.),
|
||
Token::RightParenthesis,
|
||
])
|
||
);
|
||
|
||
// (3)(2) = (3)*(2)
|
||
assert_eq!(
|
||
implicit_operations(vec![
|
||
Token::LeftParenthesis,
|
||
Token::Number(3.),
|
||
Token::RightParenthesis,
|
||
Token::LeftParenthesis,
|
||
Token::Number(2.),
|
||
Token::RightParenthesis
|
||
]),
|
||
Some(vec![
|
||
Token::LeftParenthesis,
|
||
Token::Number(3.),
|
||
Token::RightParenthesis,
|
||
Token::Multiply,
|
||
Token::LeftParenthesis,
|
||
Token::Number(2.),
|
||
Token::RightParenthesis
|
||
])
|
||
);
|
||
|
||
// 3 -- 2 == 3 + 2
|
||
assert_eq!(
|
||
implicit_operations(vec![
|
||
Token::Number(3.),
|
||
Token::Subtract,
|
||
Token::Subtract,
|
||
Token::Number(2.),
|
||
]),
|
||
Some(vec![Token::Number(3.), Token::Add, Token::Number(2.),])
|
||
);
|
||
|
||
assert_eq!(
|
||
implicit_operations(vec![Token::Subtract, Token::Number(3.),]),
|
||
Some(vec![Token::Number(-3.)])
|
||
);
|
||
|
||
assert_eq!(
|
||
implicit_operations(vec![
|
||
Token::Subtract,
|
||
Token::LeftParenthesis,
|
||
Token::Number(3.),
|
||
Token::RightParenthesis,
|
||
]),
|
||
Some(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,
|
||
]),
|
||
Some(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,
|
||
]),
|
||
Some(vec![
|
||
Token::Number(-1.),
|
||
Token::Multiply,
|
||
Token::LeftParenthesis,
|
||
Token::Number(-1.),
|
||
Token::Multiply,
|
||
Token::LeftParenthesis,
|
||
Token::Number(3.),
|
||
Token::RightParenthesis,
|
||
Token::RightParenthesis,
|
||
])
|
||
);
|
||
}
|
||
#[test]
|
||
fn infix_to_postfix_test() {
|
||
assert_eq!(
|
||
infix_to_postfix(vec![Token::Number(3.), Token::Add, Token::Number(2.)]),
|
||
Ok(vec![Token::Number(3.), Token::Number(2.), Token::Add,])
|
||
);
|
||
assert_eq!(
|
||
infix_to_postfix(vec![Token::Number(3.), Token::Add, Token::Number(2.)]),
|
||
Ok(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 ^ ^ ÷ +
|
||
Ok(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]),
|
||
Ok(6.)
|
||
);
|
||
}
|
||
|
||
#[test]
|
||
fn test_compute() {
|
||
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.);
|
||
assert_eq!(compute("1E-3"), 0.001);
|
||
assert_eq!(compute("3sin(3)"), 3. * 3_f64.sin());
|
||
assert_eq!(compute("(1 + 2) - (1)"), 2.);
|
||
assert_eq!(compute("(1 + 2) - 1"), 2.);
|
||
assert_eq!(compute("sqrt(4)"), 2.);
|
||
}
|
||
}
|