Evaluation of plus and minus

This commit is contained in:
Kasper 2019-12-22 21:35:58 +01:00
parent c247b6c899
commit 8fcd76a098
5 changed files with 199 additions and 49 deletions

79
src/evaluator.rs Normal file
View File

@ -0,0 +1,79 @@
use decimal::d128;
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};
#[derive(Clone, Debug)]
pub struct Answer {
value: d128,
unit: Unit,
}
impl Answer {
pub fn new(value: d128, unit: Unit) -> Answer {
Answer {
value: value,
unit: unit,
}
}
}
pub fn evaluate(ast: &AstNode) -> Result<Answer, String> {
let answer = evaluate_node(ast)?;
Ok(answer)
}
fn evaluate_node(ast_node: &AstNode) -> Result<Answer, String> {
let token = &ast_node.token;
let children = &ast_node.children;
match token {
Token::Number(number) => {
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_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]");
return evaluate_node(child_node)
},
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());
let left = evaluate_node(left_child)?;
let right = evaluate_node(right_child)?;
match operator {
Plus => {
if left.unit == right.unit {
Ok(Answer::new(left.value + right.value, left.unit))
} else if left.unit.category() == right.unit.category() {
let result = left.value * left.unit.weight() + right.value * right.unit.weight();
Ok(Answer::new(result, Unit::Millimeter))
} else {
return Err(format!("Cannot add {:?} and {:?}", left.unit, right.unit))
}
},
Minus => {
if left.unit == right.unit {
Ok(Answer::new(left.value - right.value, left.unit))
} else if left.unit.category() == right.unit.category() {
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))
}
},
_ => {
Err(format!("Unexpected operator {:?}", operator))
}
}
},
_ => {
Err(format!("Unexpected ast node {:?}", token))
},
}
}

View File

@ -5,7 +5,7 @@ use crate::Operator::{Percent, Caret, Divide, Factorial, LeftParen, Minus, Modul
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};
use crate::Unit::*; use crate::units::Unit::*;
pub fn lex(input: &str) -> Result<TokenVector, String> { pub fn lex(input: &str) -> Result<TokenVector, String> {
@ -106,7 +106,7 @@ pub fn lex(input: &str) -> Result<TokenVector, String> {
"yr" | "year" | "years" => tokens.push(Token::Unit(Year)), "yr" | "year" | "years" => tokens.push(Token::Unit(Year)),
"decade" | "decades" => tokens.push(Token::Unit(Decade)), "decade" | "decades" => tokens.push(Token::Unit(Decade)),
"century" | "centuries" => tokens.push(Token::Unit(Century)), "century" | "centuries" => tokens.push(Token::Unit(Century)),
"millenium" | "milleniums" => tokens.push(Token::Unit(Milleniums)), "millenium" | "millenia" | "milleniums" => tokens.push(Token::Unit(Millenium)),
"mm" | "millimeter" | "millimeters" => tokens.push(Token::Unit(Millimeter)), "mm" | "millimeter" | "millimeters" => tokens.push(Token::Unit(Millimeter)),
"cm" | "centimeter" | "centimeters" => tokens.push(Token::Unit(Centimeter)), "cm" | "centimeter" | "centimeters" => tokens.push(Token::Unit(Centimeter)),

View File

@ -8,8 +8,8 @@ pub enum Operator {
Multiply, Multiply,
Divide, Divide,
Modulo, Modulo,
Percent,
Caret, Caret,
Percent,
Factorial, Factorial,
LeftParen, // lexer only LeftParen, // lexer only
RightParen, // lexer only RightParen, // lexer only
@ -55,42 +55,7 @@ pub enum FunctionIdentifier {
Atanh, Atanh,
} }
#[derive(Clone, Copy, Debug)] mod units;
pub enum Unit {
Nanosecond,
Microsecond,
Millisecond,
Second,
Minute,
Hour,
Day,
Week,
Month,
Quarter,
Year,
Decade,
Century,
Milleniums,
Millimeter,
Centimeter,
Decimeter,
Meter,
Kilometer,
Inch,
Foot,
Yard,
Mile,
NauticalMile,
SquareMeter,
// etc
CubicMeter,
//etc
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum Token { pub enum Token {
@ -101,13 +66,14 @@ pub enum Token {
Paren, // parser only Paren, // parser only
TextOperator(TextOperator), TextOperator(TextOperator),
Negative, // parser only Negative, // parser only
Unit(Unit), Unit(units::Unit),
} }
pub type TokenVector = Vec<Token>; pub type TokenVector = Vec<Token>;
mod lexer; mod lexer;
mod parser; mod parser;
mod evaluator;
fn main() { fn main() {
let lex_start = Instant::now(); let lex_start = Instant::now();
@ -119,20 +85,32 @@ fn main() {
match lexer::lex(s) { match lexer::lex(s) {
Ok(tokens) => { Ok(tokens) => {
let lex_time = Instant::now().duration_since(lex_start).as_nanos() as f32; let lex_time = Instant::now().duration_since(lex_start).as_nanos() as f32;
println!("Lexed TokenVector: {:?}", tokens); // println!("Lexed TokenVector: {:?}", tokens);
let parse_start = Instant::now(); let parse_start = Instant::now();
match parser::parse(&tokens) { match parser::parse(&tokens) {
Ok(ast) => { Ok(ast) => {
let parse_time = Instant::now().duration_since(parse_start).as_nanos() as f32; let parse_time = Instant::now().duration_since(parse_start).as_nanos() as f32;
println!("Parsed AstNode: {:#?}", ast); // println!("Parsed AstNode: {:#?}", ast);
let eval_start = Instant::now();
match evaluator::evaluate(&ast) {
Ok(answer) => {
let eval_time = Instant::now().duration_since(eval_start).as_nanos() as f32;
println!("Evaluated answer: {:#?}", answer);
println!("\u{23f1} {:.3}ms lexing", lex_time/1000.0/1000.0); println!("\u{23f1} {:.3}ms lexing", lex_time/1000.0/1000.0);
println!("\u{23f1} {:.3}ms parsing", parse_time/1000.0/1000.0); println!("\u{23f1} {:.3}ms parsing", parse_time/1000.0/1000.0);
println!("\u{23f1} {:.3}ms evaluation", eval_time/1000.0/1000.0);
}, },
Err(e) => println!("parsing error: {}", e), Err(e) => println!("Eval error: {}", e),
}
},
Err(e) => println!("Parsing error: {}", e),
} }
}, },
Err(e) => println!("lexing error: {}", e), Err(e) => println!("Lexing error: {}", e),
} }
} }

View File

@ -1,12 +1,11 @@
use crate::{Token, TokenVector}; use crate::{Token, TokenVector};
#[allow(unused_imports)]
use crate::Operator::{Percent, Caret, Divide, Factorial, LeftParen, Minus, Modulo, Multiply, Plus, RightParen}; use crate::Operator::{Percent, Caret, Divide, Factorial, LeftParen, Minus, Modulo, Multiply, Plus, RightParen};
use crate::TextOperator::{To, Of}; use crate::TextOperator::{To, Of};
#[derive(Debug)] #[derive(Debug)]
pub struct AstNode { pub struct AstNode {
children: Vec<AstNode>, pub children: Vec<AstNode>,
token: Token, pub token: Token,
} }
impl AstNode { impl AstNode {

94
src/units.rs Normal file
View File

@ -0,0 +1,94 @@
use decimal::d128;
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum UnitType {
NoUnit,
Time,
Length,
Area,
Volume,
}
use UnitType::*;
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum Unit {
NoUnit,
Nanosecond,
Microsecond,
Millisecond,
Second,
Minute,
Hour,
Day,
Week,
Month,
Quarter,
Year,
Decade,
Century,
Millenium,
Millimeter,
Centimeter,
Decimeter,
Meter,
Kilometer,
Inch,
Foot,
Yard,
Mile,
NauticalMile,
SquareMeter,
// etc
CubicMeter,
// etc
}
use Unit::*;
fn get_info(unit: &Unit) -> (UnitType, d128) {
match unit {
Unit::NoUnit => (UnitType::NoUnit, d128!(1)),
Nanosecond => (Time, d128!(1)),
Microsecond => (Time, d128!(1000)),
Millisecond => (Time, d128!(1000000)),
Second => (Time, d128!(1000000000)),
Minute => (Time, d128!(60000000000)),
Hour => (Time, d128!(3600000000000)),
Day => (Time, d128!(86400000000000)),
Week => (Time, d128!(604800000000000)),
Month => (Time, d128!(2629746000000000)),
Quarter => (Time, d128!(7889238000000000)),
Year => (Time, d128!(31556952000000000)),
Decade => (Time, d128!(315569520000000000)),
Century => (Time, d128!(3155695200000000000)),
Millenium => (Time, d128!(31556952000000000000)),
Millimeter => (Length, d128!(10)),
Centimeter => (Length, d128!(100)),
Decimeter => (Length, d128!(1000)),
Meter => (Length, d128!(10000)),
Kilometer => (Length, d128!(10000000)),
Inch => (Length, d128!(254)),
Foot => (Length, d128!(3048)),
Yard => (Length, d128!(9144)),
Mile => (Length, d128!(16090000)),
NauticalMile => (Length, d128!(18520000)),
SquareMeter => (UnitType::Area, d128!(1)),
CubicMeter => (UnitType::Volume, d128!(1)),
}
}
impl Unit {
pub fn category(&self) -> UnitType {
return get_info(self).0
}
pub fn weight(&self) -> d128 {
return get_info(self).1
}
}