Made the "Answer" struct a generic struct "Number"
This commit is contained in:
parent
d428546ecf
commit
80aacc6493
@ -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<Answer, String> {
|
||||
pub fn evaluate(ast: &AstNode) -> Result<Number, String> {
|
||||
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<Answer, String> {
|
||||
fn evaluate_node(ast_node: &AstNode) -> Result<Number, String> {
|
||||
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<Answer, String> {
|
||||
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<Answer, String> {
|
||||
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<Answer, String> {
|
||||
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<Answer, String> {
|
||||
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<Answer, String> {
|
||||
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<Answer, String> {
|
||||
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<Answer, String> {
|
||||
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<Answer, String> {
|
||||
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<Answer, String> {
|
||||
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<Answer, String> {
|
||||
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<Answer, String> {
|
||||
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<Answer, String> {
|
||||
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<Answer, String> {
|
||||
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<Answer, String> {
|
||||
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))
|
||||
}
|
||||
|
||||
60
src/units.rs
60
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<d128, String> {
|
||||
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<Number, String> {
|
||||
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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user