Evaluation of multiply, divide, modulo, caret, percent, factorial

This commit is contained in:
Kasper 2019-12-23 01:24:15 +01:00
parent 8fcd76a098
commit 5286bec0dd
4 changed files with 99 additions and 12 deletions

View File

@ -3,7 +3,8 @@ use crate::Token;
use crate::units::Unit; use crate::units::Unit;
use crate::parser::AstNode; use crate::parser::AstNode;
#[allow(unused_imports)] #[allow(unused_imports)]
use crate::Operator::{Percent, Caret, Divide, Factorial, LeftParen, Minus, Modulo, Multiply, Plus, RightParen}; use crate::Operator::{Caret, Divide, LeftParen, Minus, Modulo, Multiply, Plus, RightParen};
use crate::UnaryOperator::{Percent, Factorial};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Answer { pub struct Answer {
@ -25,6 +26,14 @@ pub fn evaluate(ast: &AstNode) -> Result<Answer, String> {
Ok(answer) Ok(answer)
} }
fn factorial(n: d128) -> d128 {
if n < d128!(2) {
d128!(1)
} else {
n * factorial(n - d128!(1))
}
}
fn evaluate_node(ast_node: &AstNode) -> Result<Answer, String> { fn evaluate_node(ast_node: &AstNode) -> Result<Answer, String> {
let token = &ast_node.token; let token = &ast_node.token;
let children = &ast_node.children; let children = &ast_node.children;
@ -33,14 +42,32 @@ fn evaluate_node(ast_node: &AstNode) -> Result<Answer, String> {
Ok(Answer::new(number.clone(), Unit::NoUnit)) Ok(Answer::new(number.clone(), Unit::NoUnit))
}, },
Token::Unit(unit) => { Token::Unit(unit) => {
let child_node = ast_node.children.get(0).expect("Unit has no child[0]"); let child_node = children.get(0).expect("Unit has no child[0]");
let child_answer = evaluate_node(child_node)?; let child_answer = evaluate_node(child_node)?;
Ok(Answer::new(child_answer.value, unit.clone())) Ok(Answer::new(child_answer.value, unit.clone()))
}, },
Token::Paren => { Token::Paren => {
let child_node = ast_node.children.get(0).expect("Paren has no child[0]"); let child_node = children.get(0).expect("Paren has no child[0]");
return evaluate_node(child_node) return evaluate_node(child_node)
}, },
Token::UnaryOperator(operator) => {
let child_node = children.get(0).expect(format!("Token {:?} has no child[0]", token).as_str());
let child_answer = evaluate_node(child_node)?;
match operator {
Percent => {
Ok(Answer::new(child_answer.value / d128!(100), child_answer.unit))
},
Factorial => {
if child_answer.value.is_negative() || child_answer.value.is_signed() {
Err("Cannot perform factorial of floats or negative".to_string())
} else if child_answer.value > d128!(1000) {
Err("Cannot perform factorial of numbers above 1000".to_string())
} else {
Ok(Answer::new(factorial(child_answer.value), child_answer.unit))
}
},
}
},
Token::Operator(operator) => { Token::Operator(operator) => {
let left_child = children.get(0).expect(format!("Token {:?} has no child[0]", token).as_str()); let left_child = children.get(0).expect(format!("Token {:?} has no child[0]", token).as_str());
let right_child = children.get(1).expect(format!("Token {:?} has no child[1]", token).as_str()); let right_child = children.get(1).expect(format!("Token {:?} has no child[1]", token).as_str());
@ -64,7 +91,54 @@ fn evaluate_node(ast_node: &AstNode) -> Result<Answer, String> {
let result = left.value * left.unit.weight() - right.value * right.unit.weight(); let result = left.value * left.unit.weight() - right.value * right.unit.weight();
Ok(Answer::new(result, Unit::Millimeter)) Ok(Answer::new(result, Unit::Millimeter))
} else { } else {
return Err(format!("Cannot subtract {:?} and {:?}", left.unit, right.unit)) return Err(format!("Cannot subtract {:?} by {:?}", left.unit, right.unit))
}
},
Multiply => {
if left.unit == Unit::NoUnit && right.unit == Unit::NoUnit {
// 3 * 2
return Ok(Answer::new(left.value * right.value, left.unit))
} else if left.unit == Unit::NoUnit && right.unit != Unit::NoUnit {
// 3 * 1 km
return Ok(Answer::new(left.value * right.value, right.unit))
} else if right.unit == Unit::NoUnit && left.unit != Unit::NoUnit {
// 1 km * 3
return Ok(Answer::new(left.value * right.value, left.unit))
} else {
return Err(format!("Cannot multiply {:?} and {:?}", left.unit, right.unit))
}
},
Divide => {
if left.unit == Unit::NoUnit && right.unit == Unit::NoUnit {
// 3 / 2
return Ok(Answer::new(left.value / right.value, left.unit))
} else if left.unit != Unit::NoUnit && right.unit == Unit::NoUnit {
// 1 km / 2
return Ok(Answer::new(left.value / right.value, right.unit))
} else {
return Err(format!("Cannot divide {:?} by {:?}", left.unit, right.unit))
}
},
Modulo => {
if left.unit == Unit::NoUnit && right.unit == Unit::NoUnit {
// 3 / 2
return Ok(Answer::new(left.value % right.value, left.unit))
} else if left.unit != Unit::NoUnit && right.unit == Unit::NoUnit {
// 1 km / 2
return Ok(Answer::new(left.value % right.value, right.unit))
} else {
return Err(format!("Cannot modulo {:?} by {:?}", left.unit, right.unit))
}
},
Caret => {
if left.unit == Unit::NoUnit && right.unit == Unit::NoUnit {
// 3 ^ 2
return Ok(Answer::new(left.value ^ right.value, left.unit))
} else if right.unit == Unit::NoUnit && left.unit != Unit::NoUnit {
// 1 km ^ 3
return Ok(Answer::new(left.value ^ right.value, left.unit))
} else {
return Err(format!("Cannot multiply {:?} and {:?}", left.unit, right.unit))
} }
}, },
_ => { _ => {

View File

@ -1,7 +1,8 @@
use std::str::FromStr; use std::str::FromStr;
use decimal::d128; use decimal::d128;
use crate::{Token, TokenVector}; use crate::{Token, TokenVector};
use crate::Operator::{Percent, Caret, Divide, Factorial, LeftParen, Minus, Modulo, Multiply, Plus, RightParen}; use crate::Operator::{Caret, Divide, LeftParen, Minus, Modulo, Multiply, Plus, RightParen};
use crate::UnaryOperator::{Percent, Factorial};
use crate::TextOperator::{Of, To}; use crate::TextOperator::{Of, To};
use crate::Constant::{E, Pi}; use crate::Constant::{E, Pi};
use crate::FunctionIdentifier::{Acos, Acosh, Asin, Asinh, Atan, Atanh, Cbrt, Ceil, Cos, Cosh, Exp, Fabs, Floor, Ln, Log, Round, Sin, Sinh, Sqrt, Tan, Tanh}; use crate::FunctionIdentifier::{Acos, Acosh, Asin, Asinh, Atan, Atanh, Cbrt, Ceil, Cos, Cosh, Exp, Fabs, Floor, Ln, Log, Round, Sin, Sinh, Sqrt, Tan, Tanh};
@ -25,7 +26,7 @@ pub fn lex(input: &str) -> Result<TokenVector, String> {
'/' => tokens.push(Token::Operator(Divide)), '/' => tokens.push(Token::Operator(Divide)),
'%' => tokens.push(Token::Operator(Modulo)), '%' => tokens.push(Token::Operator(Modulo)),
'^' => tokens.push(Token::Operator(Caret)), '^' => tokens.push(Token::Operator(Caret)),
'!' => tokens.push(Token::Operator(Factorial)), '!' => tokens.push(Token::UnaryOperator(Factorial)),
'(' => { '(' => {
left_paren_count += 1; left_paren_count += 1;
tokens.push(Token::Operator(LeftParen)); tokens.push(Token::Operator(LeftParen));
@ -233,17 +234,21 @@ pub fn lex(input: &str) -> Result<TokenVector, String> {
match &tokens[token_index + 1] { match &tokens[token_index + 1] {
Token::TextOperator(Of) => { Token::TextOperator(Of) => {
// for example "10% of 1km" should be a percentage, not modulo // for example "10% of 1km" should be a percentage, not modulo
tokens[token_index] = Token::Operator(Percent); tokens[token_index] = Token::UnaryOperator(Percent);
}, },
Token::Operator(operator) => { Token::Operator(operator) => {
match operator { match operator {
LeftParen => {}, LeftParen => {},
_ => { _ => {
// for example "10%*2" should be a percentage, but "10%(2)" should be modulo // for example "10%*2" should be a percentage, but "10%(2)" should be modulo
tokens[token_index] = Token::Operator(Percent); tokens[token_index] = Token::UnaryOperator(Percent);
} }
} }
}, },
Token::UnaryOperator(_operator) => {
// for example "10%!" should be a percentage, but "10%(2)" should be modulo
tokens[token_index] = Token::UnaryOperator(Percent);
},
_ => {}, _ => {},
} }
} }

View File

@ -9,12 +9,18 @@ pub enum Operator {
Divide, Divide,
Modulo, Modulo,
Caret, Caret,
Percent, // Percent,
Factorial, // Factorial,
LeftParen, // lexer only LeftParen, // lexer only
RightParen, // lexer only RightParen, // lexer only
} }
#[derive(Clone, Debug)]
pub enum UnaryOperator {
Percent,
Factorial,
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum TextOperator { pub enum TextOperator {
To, To,
@ -60,6 +66,7 @@ mod units;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum Token { pub enum Token {
Operator(Operator), Operator(Operator),
UnaryOperator(UnaryOperator),
Number(d128), Number(d128),
FunctionIdentifier(FunctionIdentifier), FunctionIdentifier(FunctionIdentifier),
Constant(Constant), Constant(Constant),

View File

@ -1,5 +1,6 @@
use crate::{Token, TokenVector}; use crate::{Token, TokenVector};
use crate::Operator::{Percent, Caret, Divide, Factorial, LeftParen, Minus, Modulo, Multiply, Plus, RightParen}; use crate::Operator::{Caret, Divide, LeftParen, Minus, Modulo, Multiply, Plus, RightParen};
use crate::UnaryOperator::{Percent, Factorial};
use crate::TextOperator::{To, Of}; use crate::TextOperator::{To, Of};
#[derive(Debug)] #[derive(Debug)]
@ -121,7 +122,7 @@ fn parse_level_5(tokens: &TokenVector, pos: usize) -> Result<(AstNode, usize), S
loop { loop {
let token = tokens.get(pos); let token = tokens.get(pos);
match token { match token {
Some(&Token::Operator(Factorial)) | Some(&Token::Operator(Percent)) => { Some(&Token::UnaryOperator(Factorial)) | Some(&Token::UnaryOperator(Percent)) => {
// Here we are handling unary operators, aka stuff written as // Here we are handling unary operators, aka stuff written as
// "Number Operator" (3!) instead of "Number Operator Number" (3+3). // "Number Operator" (3!) instead of "Number Operator Number" (3+3).
// Therefore, if we find a match, we don't parse what comes after it. // Therefore, if we find a match, we don't parse what comes after it.