Made the "Answer" struct a generic struct "Number"

This commit is contained in:
Kasper 2020-01-09 20:58:45 +01:00
parent d428546ecf
commit 80aacc6493
2 changed files with 78 additions and 75 deletions

View File

@ -1,6 +1,6 @@
use decimal::d128; use decimal::d128;
use crate::Token; use crate::Token;
use crate::units::{Unit, UnitType}; use crate::units::{Unit, UnitType, Number, convert};
use crate::parser::AstNode; use crate::parser::AstNode;
use crate::Operator::{Caret, Divide, Minus, Modulo, Multiply, Plus}; use crate::Operator::{Caret, Divide, Minus, Modulo, Multiply, Plus};
use crate::Constant::{Pi, E}; use crate::Constant::{Pi, E};
@ -9,22 +9,7 @@ use crate::TextOperator::{To, Of};
use crate::FunctionIdentifier::*; use crate::FunctionIdentifier::*;
use crate::lookup::lookup_factorial; use crate::lookup::lookup_factorial;
#[derive(Clone, Debug)] pub fn evaluate(ast: &AstNode) -> Result<Number, String> {
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<Answer, String> {
let answer = evaluate_node(ast)?; let answer = evaluate_node(ast)?;
Ok(answer) Ok(answer)
} }
@ -91,20 +76,20 @@ fn tan(input: d128) -> d128 {
return sin(input) / cos(input); return sin(input) / cos(input);
} }
fn evaluate_node(ast_node: &AstNode) -> Result<Answer, String> { fn evaluate_node(ast_node: &AstNode) -> Result<Number, String> {
let token = &ast_node.token; let token = &ast_node.token;
let children = &ast_node.children; let children = &ast_node.children;
match token { match token {
Token::Number(number) => { Token::Number(number) => {
Ok(Answer::new(number.clone(), Unit::NoUnit)) Ok(Number::new(number.clone(), Unit::NoUnit))
}, },
Token::Constant(constant) => { Token::Constant(constant) => {
match constant { match constant {
Pi => { Pi => {
Ok(Answer::new(d128!(3.141592653589793238462643383279503), Unit::NoUnit)) Ok(Number::new(d128!(3.141592653589793238462643383279503), Unit::NoUnit))
}, },
E => { 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<Answer, String> {
Cbrt => { Cbrt => {
if child_answer.unit.category() == UnitType::NoUnit { if child_answer.unit.category() == UnitType::NoUnit {
let result = cbrt(child_answer.value); let result = cbrt(child_answer.value);
return Ok(Answer::new(result, child_answer.unit)) return Ok(Number::new(result, child_answer.unit))
} else { } else {
return Err(format!("log() only accepts UnitType::NoUnit").to_string()) return Err(format!("log() only accepts UnitType::NoUnit").to_string())
} }
@ -123,7 +108,7 @@ fn evaluate_node(ast_node: &AstNode) -> Result<Answer, String> {
Sqrt => { Sqrt => {
if child_answer.unit.category() == UnitType::NoUnit { if child_answer.unit.category() == UnitType::NoUnit {
let result = sqrt(child_answer.value); let result = sqrt(child_answer.value);
return Ok(Answer::new(result, child_answer.unit)) return Ok(Number::new(result, child_answer.unit))
} else { } else {
return Err(format!("log() only accepts UnitType::NoUnit").to_string()) return Err(format!("log() only accepts UnitType::NoUnit").to_string())
} }
@ -131,7 +116,7 @@ fn evaluate_node(ast_node: &AstNode) -> Result<Answer, String> {
Log => { Log => {
if child_answer.unit.category() == UnitType::NoUnit { if child_answer.unit.category() == UnitType::NoUnit {
let result = child_answer.value.log10(); let result = child_answer.value.log10();
return Ok(Answer::new(result, child_answer.unit)) return Ok(Number::new(result, child_answer.unit))
} else { } else {
return Err(format!("log() only accepts UnitType::NoUnit").to_string()) return Err(format!("log() only accepts UnitType::NoUnit").to_string())
} }
@ -139,7 +124,7 @@ fn evaluate_node(ast_node: &AstNode) -> Result<Answer, String> {
Ln => { Ln => {
if child_answer.unit.category() == UnitType::NoUnit { if child_answer.unit.category() == UnitType::NoUnit {
let result = child_answer.value.ln(); let result = child_answer.value.ln();
return Ok(Answer::new(result, child_answer.unit)) return Ok(Number::new(result, child_answer.unit))
} else { } else {
return Err(format!("ln() only accepts UnitType::NoUnit").to_string()) return Err(format!("ln() only accepts UnitType::NoUnit").to_string())
} }
@ -147,7 +132,7 @@ fn evaluate_node(ast_node: &AstNode) -> Result<Answer, String> {
Exp => { Exp => {
if child_answer.unit.category() == UnitType::NoUnit { if child_answer.unit.category() == UnitType::NoUnit {
let result = child_answer.value.exp(child_answer.value); 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 { } else {
return Err(format!("exp() only accepts UnitType::NoUnit").to_string()) return Err(format!("exp() only accepts UnitType::NoUnit").to_string())
} }
@ -158,49 +143,49 @@ fn evaluate_node(ast_node: &AstNode) -> Result<Answer, String> {
let rounding_change = result - child_answer.value; let rounding_change = result - child_answer.value;
// If the result was rounded down by 0.5, correct by +1 // If the result was rounded down by 0.5, correct by +1
if rounding_change == d128!(-0.5) { result += d128!(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 => { Ceil => {
let mut result = child_answer.value.quantize(d128!(1)); let mut result = child_answer.value.quantize(d128!(1));
let rounding_change = result - child_answer.value; let rounding_change = result - child_answer.value;
if rounding_change.is_negative() { result += d128!(1); } 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 => { Floor => {
let mut result = child_answer.value.quantize(d128!(1)); let mut result = child_answer.value.quantize(d128!(1));
let rounding_change = result - child_answer.value; let rounding_change = result - child_answer.value;
if !rounding_change.is_negative() { result -= d128!(1); } 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 => { Abs => {
let mut result = child_answer.value.abs(); let mut result = child_answer.value.abs();
let rounding_change = result - child_answer.value; let rounding_change = result - child_answer.value;
if rounding_change == d128!(-0.5) { result += d128!(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))
}, },
Sin => { Sin => {
let result = sin(child_answer.value); let result = sin(child_answer.value);
return Ok(Answer::new(result, child_answer.unit)) return Ok(Number::new(result, child_answer.unit))
}, },
Cos => { Cos => {
let result = cos(child_answer.value); let result = cos(child_answer.value);
return Ok(Answer::new(result, child_answer.unit)) return Ok(Number::new(result, child_answer.unit))
}, },
Tan => { Tan => {
let result = tan(child_answer.value); 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) => { Token::Unit(unit) => {
let child_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(Number::new(child_answer.value, unit.clone()))
}, },
Token::Negative => { Token::Negative => {
let child_node = children.get(0).expect("Negative has no child[0]"); let child_node = children.get(0).expect("Negative has no child[0]");
let child_answer = evaluate_node(child_node)?; 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 => { Token::Paren => {
let child_node = children.get(0).expect("Paren has no child[0]"); let child_node = children.get(0).expect("Paren has no child[0]");
@ -211,14 +196,14 @@ fn evaluate_node(ast_node: &AstNode) -> Result<Answer, String> {
let child_answer = evaluate_node(child_node)?; let child_answer = evaluate_node(child_node)?;
match operator { match operator {
Percent => { Percent => {
Ok(Answer::new(child_answer.value / d128!(100), child_answer.unit)) Ok(Number::new(child_answer.value / d128!(100), child_answer.unit))
}, },
Factorial => { Factorial => {
let result = factorial(child_answer.value); let result = factorial(child_answer.value);
if result.is_nan() { if result.is_nan() {
return Err("Can only perform factorial on integers from 0 to 1000".to_string()); 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<Answer, String> {
if let Token::Unit(right_unit) = right_child.token { if let Token::Unit(right_unit) = right_child.token {
let left = evaluate_node(left_child)?; let left = evaluate_node(left_child)?;
if left.unit.category() == right_unit.category() { if left.unit.category() == right_unit.category() {
let left_weight = left.unit.weight(); let result = convert(left, right_unit)?;
let right_weight = right_unit.weight(); return Ok(result)
let result = left.value * left_weight / right_weight;
return Ok(Answer::new(result, right_unit))
} else { } else {
return Err(format!("Cannot convert from {:?} to {:?}", left.unit, right_unit)) return Err(format!("Cannot convert from {:?} to {:?}", left.unit, right_unit))
} }
@ -246,7 +229,7 @@ fn evaluate_node(ast_node: &AstNode) -> Result<Answer, String> {
let left = evaluate_node(left_child)?; let left = evaluate_node(left_child)?;
let right = evaluate_node(right_child)?; let right = evaluate_node(right_child)?;
if left.unit == Unit::NoUnit { 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 { } else {
return Err("child[0] of the Of operator must be NoUnit".to_string()) return Err("child[0] of the Of operator must be NoUnit".to_string())
} }
@ -261,20 +244,20 @@ fn evaluate_node(ast_node: &AstNode) -> Result<Answer, String> {
match operator { match operator {
Plus => { Plus => {
if left.unit == right.unit { 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() { } else if left.unit.category() == right.unit.category() {
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(Number::new(result, Unit::Millimeter))
} else { } else {
return Err(format!("Cannot add {:?} and {:?}", left.unit, right.unit)) return Err(format!("Cannot add {:?} and {:?}", left.unit, right.unit))
} }
}, },
Minus => { Minus => {
if left.unit == right.unit { 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() { } else if left.unit.category() == right.unit.category() {
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(Number::new(result, Unit::Millimeter))
} else { } else {
return Err(format!("Cannot subtract {:?} by {:?}", left.unit, right.unit)) return Err(format!("Cannot subtract {:?} by {:?}", left.unit, right.unit))
} }
@ -282,13 +265,13 @@ fn evaluate_node(ast_node: &AstNode) -> Result<Answer, String> {
Multiply => { Multiply => {
if left.unit == Unit::NoUnit && right.unit == Unit::NoUnit { if left.unit == Unit::NoUnit && right.unit == Unit::NoUnit {
// 3 * 2 // 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 { } else if left.unit == Unit::NoUnit && right.unit != Unit::NoUnit {
// 3 * 1 km // 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 { } else if right.unit == Unit::NoUnit && left.unit != Unit::NoUnit {
// 1 km * 3 // 1 km * 3
return Ok(Answer::new(left.value * right.value, left.unit)) return Ok(Number::new(left.value * right.value, left.unit))
} else { } else {
return Err(format!("Cannot multiply {:?} and {:?}", left.unit, right.unit)) return Err(format!("Cannot multiply {:?} and {:?}", left.unit, right.unit))
} }
@ -296,10 +279,10 @@ fn evaluate_node(ast_node: &AstNode) -> Result<Answer, String> {
Divide => { Divide => {
if left.unit == Unit::NoUnit && right.unit == Unit::NoUnit { if left.unit == Unit::NoUnit && right.unit == Unit::NoUnit {
// 3 / 2 // 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 { } else if left.unit != Unit::NoUnit && right.unit == Unit::NoUnit {
// 1 km / 2 // 1 km / 2
return Ok(Answer::new(left.value / right.value, right.unit)) return Ok(Number::new(left.value / right.value, right.unit))
} else { } else {
return Err(format!("Cannot divide {:?} by {:?}", left.unit, right.unit)) return Err(format!("Cannot divide {:?} by {:?}", left.unit, right.unit))
} }
@ -307,10 +290,10 @@ fn evaluate_node(ast_node: &AstNode) -> Result<Answer, String> {
Modulo => { Modulo => {
if left.unit == Unit::NoUnit && right.unit == Unit::NoUnit { if left.unit == Unit::NoUnit && right.unit == Unit::NoUnit {
// 3 / 2 // 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 { } else if left.unit != Unit::NoUnit && right.unit == Unit::NoUnit {
// 1 km / 2 // 1 km / 2
return Ok(Answer::new(left.value % right.value, right.unit)) return Ok(Number::new(left.value % right.value, right.unit))
} else { } else {
return Err(format!("Cannot modulo {:?} by {:?}", left.unit, right.unit)) return Err(format!("Cannot modulo {:?} by {:?}", left.unit, right.unit))
} }
@ -318,10 +301,10 @@ fn evaluate_node(ast_node: &AstNode) -> Result<Answer, String> {
Caret => { Caret => {
if left.unit == Unit::NoUnit && right.unit == Unit::NoUnit { if left.unit == Unit::NoUnit && right.unit == Unit::NoUnit {
// 3 ^ 2 // 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 { } else if right.unit == Unit::NoUnit && left.unit != Unit::NoUnit {
// 1 km ^ 3 // 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 { } else {
return Err(format!("Cannot multiply {:?} and {:?}", left.unit, right.unit)) return Err(format!("Cannot multiply {:?} and {:?}", left.unit, right.unit))
} }

View File

@ -13,17 +13,17 @@ pub enum UnitType {
use UnitType::*; use UnitType::*;
macro_rules! create_units { macro_rules! create_units {
( $( $x:ident : $y:expr ),*, ) => { ( $( $variant:ident : $properties:expr ),*, ) => {
#[derive(Clone, Copy, PartialEq, Debug)] #[derive(Clone, Copy, PartialEq, Debug)]
pub enum Unit { pub enum Unit {
$($x),* $($variant),*
} }
use Unit::*; use Unit::*;
fn get_info(unit: &Unit) -> (UnitType, d128) { fn get_info(unit: &Unit) -> (UnitType, d128) {
match unit { 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 { fn get_convertion_factor(unit: Unit, to_unit: Unit) -> d128 {
return unit.weight() / to_unit.weight(); return unit.weight() / to_unit.weight();
} }
pub fn convert(value: d128, unit: Unit, to_unit: Unit) -> Result<d128, String> { pub fn convert(number: Number, to_unit: Unit) -> Result<Number, String> {
if unit.category() == UnitType::Temperature { let value = number.value;
match (unit, to_unit) { let ok = |new_value| {
(Kelvin, Kelvin) => Ok(value), Ok(Number::new(new_value, to_unit))
(Kelvin, Celcius) => Ok(value-d128!(273.15)), };
(Kelvin, Fahrenheit) => Ok(value*d128!(1.8)-d128!(459.67)), if number.unit.category() == UnitType::Temperature {
(Celcius, Celcius) => Ok(value), match (number.unit, to_unit) {
(Celcius, Kelvin) => Ok(value+d128!(273.15)), (Kelvin, Kelvin) => ok(value),
(Celcius, Fahrenheit) => Ok(value*d128!(1.8)+d128!(32)), (Kelvin, Celcius) => ok(value-d128!(273.15)),
(Fahrenheit, Fahrenheit) => Ok(value), (Kelvin, Fahrenheit) => ok(value*d128!(1.8)-d128!(459.67)),
(Fahrenheit, Kelvin) => Ok((value+d128!(459.67))*d128!(5)/d128!(9)), (Celcius, Celcius) => ok(value),
(Fahrenheit, Celcius) => Ok((value-d128!(32))/d128!(1.8)), (Celcius, Kelvin) => ok(value+d128!(273.15)),
_ => Err(format!("Error converting temperature {:?} to {:?}", unit, to_unit)), (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 { } else {
let convertion_factor = get_convertion_factor(unit, to_unit); let convertion_factor = get_convertion_factor(number.unit, to_unit);
Ok(value * convertion_factor) ok(number.value * convertion_factor)
} }
} }
@ -154,9 +173,10 @@ mod tests {
let value_string = &value.to_string(); let value_string = &value.to_string();
let value_d128 = d128::from_str(value_string).unwrap(); 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 result = convert(number, to_unit);
let string_result = &result.unwrap().to_string(); let string_result = &result.unwrap().value.to_string();
let float_result = f64::from_str(string_result).unwrap(); let float_result = f64::from_str(string_result).unwrap();
return float_result; return float_result;