diff --git a/src/evaluator.rs b/src/evaluator.rs new file mode 100644 index 0000000..03ffbed --- /dev/null +++ b/src/evaluator.rs @@ -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 { + let answer = evaluate_node(ast)?; + Ok(answer) +} + +fn evaluate_node(ast_node: &AstNode) -> Result { + 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)) + }, + } +} diff --git a/src/lexer.rs b/src/lexer.rs index ad2cfcd..20fb96c 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -5,7 +5,7 @@ use crate::Operator::{Percent, Caret, Divide, Factorial, LeftParen, Minus, Modul 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}; -use crate::Unit::*; +use crate::units::Unit::*; pub fn lex(input: &str) -> Result { @@ -106,7 +106,7 @@ pub fn lex(input: &str) -> Result { "yr" | "year" | "years" => tokens.push(Token::Unit(Year)), "decade" | "decades" => tokens.push(Token::Unit(Decade)), "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)), "cm" | "centimeter" | "centimeters" => tokens.push(Token::Unit(Centimeter)), diff --git a/src/main.rs b/src/main.rs index dd4b4a0..458d2f0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,8 +8,8 @@ pub enum Operator { Multiply, Divide, Modulo, - Percent, Caret, + Percent, Factorial, LeftParen, // lexer only RightParen, // lexer only @@ -55,42 +55,7 @@ pub enum FunctionIdentifier { Atanh, } -#[derive(Clone, Copy, Debug)] -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 - - -} +mod units; #[derive(Clone, Debug)] pub enum Token { @@ -101,13 +66,14 @@ pub enum Token { Paren, // parser only TextOperator(TextOperator), Negative, // parser only - Unit(Unit), + Unit(units::Unit), } pub type TokenVector = Vec; mod lexer; mod parser; +mod evaluator; fn main() { let lex_start = Instant::now(); @@ -119,20 +85,32 @@ fn main() { match lexer::lex(s) { Ok(tokens) => { 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(); match parser::parse(&tokens) { Ok(ast) => { let parse_time = Instant::now().duration_since(parse_start).as_nanos() as f32; - println!("Parsed AstNode: {:#?}", ast); - 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!("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 parsing", parse_time/1000.0/1000.0); + println!("\u{23f1} {:.3}ms evaluation", eval_time/1000.0/1000.0); + }, + Err(e) => println!("Eval error: {}", e), + } + }, - Err(e) => println!("parsing error: {}", e), + Err(e) => println!("Parsing error: {}", e), } }, - Err(e) => println!("lexing error: {}", e), + Err(e) => println!("Lexing error: {}", e), } } diff --git a/src/parser.rs b/src/parser.rs index d175801..aa1dd2f 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,12 +1,11 @@ use crate::{Token, TokenVector}; -#[allow(unused_imports)] use crate::Operator::{Percent, Caret, Divide, Factorial, LeftParen, Minus, Modulo, Multiply, Plus, RightParen}; use crate::TextOperator::{To, Of}; #[derive(Debug)] pub struct AstNode { - children: Vec, - token: Token, + pub children: Vec, + pub token: Token, } impl AstNode { diff --git a/src/units.rs b/src/units.rs new file mode 100644 index 0000000..3066b86 --- /dev/null +++ b/src/units.rs @@ -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 + } +}