added functions and fixed negative numbers
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
||||
/target
|
||||
Cargo.lock
|
||||
|
||||
347
src/main.rs
347
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<Vec<Token>, 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<Token, TokenizeError> {
|
||||
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<Token>) -> Vec<Token> {
|
||||
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<Token>) -> Vec<Token> {
|
||||
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<Token>) -> Vec<Token> {
|
||||
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<Token>) -> Result<f64, CalculateError> {
|
||||
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.);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user