added functions and fixed negative numbers

This commit is contained in:
2024-03-22 01:16:41 +01:00
parent 53314ddaad
commit 5505d1218b
2 changed files with 281 additions and 67 deletions

1
.gitignore vendored
View File

@@ -1 +1,2 @@
/target
Cargo.lock

View File

@@ -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.);
}
}