Evaluation of multiply, divide, modulo, caret, percent, factorial
This commit is contained in:
parent
8fcd76a098
commit
5286bec0dd
@ -3,7 +3,8 @@ use crate::Token;
|
||||
use crate::units::Unit;
|
||||
use crate::parser::AstNode;
|
||||
#[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)]
|
||||
pub struct Answer {
|
||||
@ -25,6 +26,14 @@ pub fn evaluate(ast: &AstNode) -> Result<Answer, String> {
|
||||
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> {
|
||||
let token = &ast_node.token;
|
||||
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))
|
||||
},
|
||||
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)?;
|
||||
Ok(Answer::new(child_answer.value, unit.clone()))
|
||||
},
|
||||
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)
|
||||
},
|
||||
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) => {
|
||||
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());
|
||||
@ -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();
|
||||
Ok(Answer::new(result, Unit::Millimeter))
|
||||
} 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))
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
|
||||
13
src/lexer.rs
13
src/lexer.rs
@ -1,7 +1,8 @@
|
||||
use std::str::FromStr;
|
||||
use decimal::d128;
|
||||
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::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};
|
||||
@ -25,7 +26,7 @@ pub fn lex(input: &str) -> Result<TokenVector, String> {
|
||||
'/' => tokens.push(Token::Operator(Divide)),
|
||||
'%' => tokens.push(Token::Operator(Modulo)),
|
||||
'^' => tokens.push(Token::Operator(Caret)),
|
||||
'!' => tokens.push(Token::Operator(Factorial)),
|
||||
'!' => tokens.push(Token::UnaryOperator(Factorial)),
|
||||
'(' => {
|
||||
left_paren_count += 1;
|
||||
tokens.push(Token::Operator(LeftParen));
|
||||
@ -233,17 +234,21 @@ pub fn lex(input: &str) -> Result<TokenVector, String> {
|
||||
match &tokens[token_index + 1] {
|
||||
Token::TextOperator(Of) => {
|
||||
// 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) => {
|
||||
match operator {
|
||||
LeftParen => {},
|
||||
_ => {
|
||||
// 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);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
11
src/main.rs
11
src/main.rs
@ -9,12 +9,18 @@ pub enum Operator {
|
||||
Divide,
|
||||
Modulo,
|
||||
Caret,
|
||||
Percent,
|
||||
Factorial,
|
||||
// Percent,
|
||||
// Factorial,
|
||||
LeftParen, // lexer only
|
||||
RightParen, // lexer only
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum UnaryOperator {
|
||||
Percent,
|
||||
Factorial,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum TextOperator {
|
||||
To,
|
||||
@ -60,6 +66,7 @@ mod units;
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Token {
|
||||
Operator(Operator),
|
||||
UnaryOperator(UnaryOperator),
|
||||
Number(d128),
|
||||
FunctionIdentifier(FunctionIdentifier),
|
||||
Constant(Constant),
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
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};
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -121,7 +122,7 @@ fn parse_level_5(tokens: &TokenVector, pos: usize) -> Result<(AstNode, usize), S
|
||||
loop {
|
||||
let token = tokens.get(pos);
|
||||
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
|
||||
// "Number Operator" (3!) instead of "Number Operator Number" (3+3).
|
||||
// Therefore, if we find a match, we don't parse what comes after it.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user