From 80aacc649357d873d428bfb9b1094135d8cffb60 Mon Sep 17 00:00:00 2001 From: Kasper Date: Thu, 9 Jan 2020 20:58:45 +0100 Subject: [PATCH] Made the "Answer" struct a generic struct "Number" --- src/evaluator.rs | 93 ++++++++++++++++++++---------------------------- src/units.rs | 60 ++++++++++++++++++++----------- 2 files changed, 78 insertions(+), 75 deletions(-) diff --git a/src/evaluator.rs b/src/evaluator.rs index a7d35b6..a09d98e 100644 --- a/src/evaluator.rs +++ b/src/evaluator.rs @@ -1,6 +1,6 @@ use decimal::d128; use crate::Token; -use crate::units::{Unit, UnitType}; +use crate::units::{Unit, UnitType, Number, convert}; use crate::parser::AstNode; use crate::Operator::{Caret, Divide, Minus, Modulo, Multiply, Plus}; use crate::Constant::{Pi, E}; @@ -9,22 +9,7 @@ use crate::TextOperator::{To, Of}; use crate::FunctionIdentifier::*; use crate::lookup::lookup_factorial; -#[derive(Clone, Debug)] -pub struct Answer { - pub value: d128, - pub unit: Unit, -} - -impl Answer { - pub fn new(value: d128, unit: Unit) -> Answer { - Answer { - value: value, - unit: unit, - } - } -} - -pub fn evaluate(ast: &AstNode) -> Result { +pub fn evaluate(ast: &AstNode) -> Result { let answer = evaluate_node(ast)?; Ok(answer) } @@ -91,20 +76,20 @@ fn tan(input: d128) -> d128 { return sin(input) / cos(input); } -fn evaluate_node(ast_node: &AstNode) -> Result { +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)) + Ok(Number::new(number.clone(), Unit::NoUnit)) }, Token::Constant(constant) => { match constant { Pi => { - Ok(Answer::new(d128!(3.141592653589793238462643383279503), Unit::NoUnit)) + Ok(Number::new(d128!(3.141592653589793238462643383279503), Unit::NoUnit)) }, E => { - Ok(Answer::new(d128!(2.718281828459045235360287471352662), Unit::NoUnit)) + Ok(Number::new(d128!(2.718281828459045235360287471352662), Unit::NoUnit)) }, } }, @@ -115,7 +100,7 @@ fn evaluate_node(ast_node: &AstNode) -> Result { Cbrt => { if child_answer.unit.category() == UnitType::NoUnit { let result = cbrt(child_answer.value); - return Ok(Answer::new(result, child_answer.unit)) + return Ok(Number::new(result, child_answer.unit)) } else { return Err(format!("log() only accepts UnitType::NoUnit").to_string()) } @@ -123,7 +108,7 @@ fn evaluate_node(ast_node: &AstNode) -> Result { Sqrt => { if child_answer.unit.category() == UnitType::NoUnit { let result = sqrt(child_answer.value); - return Ok(Answer::new(result, child_answer.unit)) + return Ok(Number::new(result, child_answer.unit)) } else { return Err(format!("log() only accepts UnitType::NoUnit").to_string()) } @@ -131,7 +116,7 @@ fn evaluate_node(ast_node: &AstNode) -> Result { Log => { if child_answer.unit.category() == UnitType::NoUnit { let result = child_answer.value.log10(); - return Ok(Answer::new(result, child_answer.unit)) + return Ok(Number::new(result, child_answer.unit)) } else { return Err(format!("log() only accepts UnitType::NoUnit").to_string()) } @@ -139,7 +124,7 @@ fn evaluate_node(ast_node: &AstNode) -> Result { Ln => { if child_answer.unit.category() == UnitType::NoUnit { let result = child_answer.value.ln(); - return Ok(Answer::new(result, child_answer.unit)) + return Ok(Number::new(result, child_answer.unit)) } else { return Err(format!("ln() only accepts UnitType::NoUnit").to_string()) } @@ -147,7 +132,7 @@ fn evaluate_node(ast_node: &AstNode) -> Result { Exp => { if child_answer.unit.category() == UnitType::NoUnit { let result = child_answer.value.exp(child_answer.value); - return Ok(Answer::new(result, child_answer.unit)) + return Ok(Number::new(result, child_answer.unit)) } else { return Err(format!("exp() only accepts UnitType::NoUnit").to_string()) } @@ -158,49 +143,49 @@ fn evaluate_node(ast_node: &AstNode) -> Result { let rounding_change = result - child_answer.value; // If the result was rounded down by 0.5, correct by +1 if rounding_change == d128!(-0.5) { result += d128!(1); } - return Ok(Answer::new(result, child_answer.unit)) + return Ok(Number::new(result, child_answer.unit)) }, Ceil => { let mut result = child_answer.value.quantize(d128!(1)); let rounding_change = result - child_answer.value; if rounding_change.is_negative() { result += d128!(1); } - return Ok(Answer::new(result, child_answer.unit)) + return Ok(Number::new(result, child_answer.unit)) }, Floor => { let mut result = child_answer.value.quantize(d128!(1)); let rounding_change = result - child_answer.value; if !rounding_change.is_negative() { result -= d128!(1); } - return Ok(Answer::new(result, child_answer.unit)) + return Ok(Number::new(result, child_answer.unit)) }, Abs => { let mut result = child_answer.value.abs(); let rounding_change = result - child_answer.value; if rounding_change == d128!(-0.5) { result += d128!(1); } - return Ok(Answer::new(result, child_answer.unit)) + return Ok(Number::new(result, child_answer.unit)) }, Sin => { let result = sin(child_answer.value); - return Ok(Answer::new(result, child_answer.unit)) + return Ok(Number::new(result, child_answer.unit)) }, Cos => { let result = cos(child_answer.value); - return Ok(Answer::new(result, child_answer.unit)) + return Ok(Number::new(result, child_answer.unit)) }, Tan => { let result = tan(child_answer.value); - return Ok(Answer::new(result, child_answer.unit)) + return Ok(Number::new(result, child_answer.unit)) }, } } Token::Unit(unit) => { 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())) + Ok(Number::new(child_answer.value, unit.clone())) }, Token::Negative => { let child_node = children.get(0).expect("Negative has no child[0]"); let child_answer = evaluate_node(child_node)?; - Ok(Answer::new(-child_answer.value, child_answer.unit)) + Ok(Number::new(-child_answer.value, child_answer.unit)) }, Token::Paren => { let child_node = children.get(0).expect("Paren has no child[0]"); @@ -211,14 +196,14 @@ fn evaluate_node(ast_node: &AstNode) -> Result { let child_answer = evaluate_node(child_node)?; match operator { Percent => { - Ok(Answer::new(child_answer.value / d128!(100), child_answer.unit)) + Ok(Number::new(child_answer.value / d128!(100), child_answer.unit)) }, Factorial => { let result = factorial(child_answer.value); if result.is_nan() { return Err("Can only perform factorial on integers from 0 to 1000".to_string()); } - Ok(Answer::new(result, child_answer.unit)) + Ok(Number::new(result, child_answer.unit)) }, } }, @@ -231,10 +216,8 @@ fn evaluate_node(ast_node: &AstNode) -> Result { if let Token::Unit(right_unit) = right_child.token { let left = evaluate_node(left_child)?; if left.unit.category() == right_unit.category() { - let left_weight = left.unit.weight(); - let right_weight = right_unit.weight(); - let result = left.value * left_weight / right_weight; - return Ok(Answer::new(result, right_unit)) + let result = convert(left, right_unit)?; + return Ok(result) } else { return Err(format!("Cannot convert from {:?} to {:?}", left.unit, right_unit)) } @@ -246,7 +229,7 @@ fn evaluate_node(ast_node: &AstNode) -> Result { let left = evaluate_node(left_child)?; let right = evaluate_node(right_child)?; if left.unit == Unit::NoUnit { - return Ok(Answer::new(left.value * right.value, right.unit)) + return Ok(Number::new(left.value * right.value, right.unit)) } else { return Err("child[0] of the Of operator must be NoUnit".to_string()) } @@ -261,20 +244,20 @@ fn evaluate_node(ast_node: &AstNode) -> Result { match operator { Plus => { if left.unit == right.unit { - Ok(Answer::new(left.value + right.value, left.unit)) + Ok(Number::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)) + Ok(Number::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)) + Ok(Number::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)) + Ok(Number::new(result, Unit::Millimeter)) } else { return Err(format!("Cannot subtract {:?} by {:?}", left.unit, right.unit)) } @@ -282,13 +265,13 @@ fn evaluate_node(ast_node: &AstNode) -> Result { Multiply => { if left.unit == Unit::NoUnit && right.unit == Unit::NoUnit { // 3 * 2 - return Ok(Answer::new(left.value * right.value, left.unit)) + return Ok(Number::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)) + return Ok(Number::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)) + return Ok(Number::new(left.value * right.value, left.unit)) } else { return Err(format!("Cannot multiply {:?} and {:?}", left.unit, right.unit)) } @@ -296,10 +279,10 @@ fn evaluate_node(ast_node: &AstNode) -> Result { Divide => { if left.unit == Unit::NoUnit && right.unit == Unit::NoUnit { // 3 / 2 - return Ok(Answer::new(left.value / right.value, left.unit)) + return Ok(Number::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)) + return Ok(Number::new(left.value / right.value, right.unit)) } else { return Err(format!("Cannot divide {:?} by {:?}", left.unit, right.unit)) } @@ -307,10 +290,10 @@ fn evaluate_node(ast_node: &AstNode) -> Result { Modulo => { if left.unit == Unit::NoUnit && right.unit == Unit::NoUnit { // 3 / 2 - return Ok(Answer::new(left.value % right.value, left.unit)) + return Ok(Number::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)) + return Ok(Number::new(left.value % right.value, right.unit)) } else { return Err(format!("Cannot modulo {:?} by {:?}", left.unit, right.unit)) } @@ -318,10 +301,10 @@ fn evaluate_node(ast_node: &AstNode) -> Result { Caret => { if left.unit == Unit::NoUnit && right.unit == Unit::NoUnit { // 3 ^ 2 - return Ok(Answer::new(left.value.pow(right.value), left.unit)) + return Ok(Number::new(left.value.pow(right.value), left.unit)) } else if right.unit == Unit::NoUnit && left.unit != Unit::NoUnit { // 1 km ^ 3 - return Ok(Answer::new(left.value.pow(right.value), left.unit)) + return Ok(Number::new(left.value.pow(right.value), left.unit)) } else { return Err(format!("Cannot multiply {:?} and {:?}", left.unit, right.unit)) } diff --git a/src/units.rs b/src/units.rs index 9b778c5..faf6770 100644 --- a/src/units.rs +++ b/src/units.rs @@ -13,17 +13,17 @@ pub enum UnitType { use UnitType::*; macro_rules! create_units { - ( $( $x:ident : $y:expr ),*, ) => { + ( $( $variant:ident : $properties:expr ),*, ) => { #[derive(Clone, Copy, PartialEq, Debug)] pub enum Unit { - $($x),* + $($variant),* } use Unit::*; fn get_info(unit: &Unit) -> (UnitType, d128) { match unit { $( - Unit::$x => $y + Unit::$variant => $properties ),* } } @@ -119,27 +119,46 @@ impl Unit { } } +#[derive(Clone, Debug)] +pub struct Number { + pub value: d128, + pub unit: Unit, +} + +impl Number { + pub fn new(value: d128, unit: Unit) -> Number { + Number { + value: value, + unit: unit, + } + } +} + fn get_convertion_factor(unit: Unit, to_unit: Unit) -> d128 { return unit.weight() / to_unit.weight(); } -pub fn convert(value: d128, unit: Unit, to_unit: Unit) -> Result { - if unit.category() == UnitType::Temperature { - match (unit, to_unit) { - (Kelvin, Kelvin) => Ok(value), - (Kelvin, Celcius) => Ok(value-d128!(273.15)), - (Kelvin, Fahrenheit) => Ok(value*d128!(1.8)-d128!(459.67)), - (Celcius, Celcius) => Ok(value), - (Celcius, Kelvin) => Ok(value+d128!(273.15)), - (Celcius, Fahrenheit) => Ok(value*d128!(1.8)+d128!(32)), - (Fahrenheit, Fahrenheit) => Ok(value), - (Fahrenheit, Kelvin) => Ok((value+d128!(459.67))*d128!(5)/d128!(9)), - (Fahrenheit, Celcius) => Ok((value-d128!(32))/d128!(1.8)), - _ => Err(format!("Error converting temperature {:?} to {:?}", unit, to_unit)), +pub fn convert(number: Number, to_unit: Unit) -> Result { + let value = number.value; + let ok = |new_value| { + Ok(Number::new(new_value, to_unit)) + }; + if number.unit.category() == UnitType::Temperature { + match (number.unit, to_unit) { + (Kelvin, Kelvin) => ok(value), + (Kelvin, Celcius) => ok(value-d128!(273.15)), + (Kelvin, Fahrenheit) => ok(value*d128!(1.8)-d128!(459.67)), + (Celcius, Celcius) => ok(value), + (Celcius, Kelvin) => ok(value+d128!(273.15)), + (Celcius, Fahrenheit) => ok(value*d128!(1.8)+d128!(32)), + (Fahrenheit, Fahrenheit) => ok(value), + (Fahrenheit, Kelvin) => ok((value+d128!(459.67))*d128!(5)/d128!(9)), + (Fahrenheit, Celcius) => ok((value-d128!(32))/d128!(1.8)), + _ => Err(format!("Error converting temperature {:?} to {:?}", number.unit, to_unit)), } } else { - let convertion_factor = get_convertion_factor(unit, to_unit); - Ok(value * convertion_factor) + let convertion_factor = get_convertion_factor(number.unit, to_unit); + ok(number.value * convertion_factor) } } @@ -154,9 +173,10 @@ mod tests { let value_string = &value.to_string(); let value_d128 = d128::from_str(value_string).unwrap(); + let number = Number::new(value_d128, unit); - let result = convert(value_d128, unit, to_unit); - let string_result = &result.unwrap().to_string(); + let result = convert(number, to_unit); + let string_result = &result.unwrap().value.to_string(); let float_result = f64::from_str(string_result).unwrap(); return float_result;