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::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))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
|
|||||||
13
src/lexer.rs
13
src/lexer.rs
@ -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);
|
||||||
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
11
src/main.rs
11
src/main.rs
@ -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),
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user