diff --git a/src/evaluator.rs b/src/evaluator.rs index 6f4eb76..2fa906b 100644 --- a/src/evaluator.rs +++ b/src/evaluator.rs @@ -1,6 +1,6 @@ use decimal::d128; use crate::Token; -use crate::units::{Unit, UnitType, Number, convert, add, subtract}; +use crate::units::{Unit, UnitType, Number, convert, add, subtract, multiply, divide, modulo, pow}; use crate::parser::AstNode; use crate::Operator::{Caret, Divide, Minus, Modulo, Multiply, Plus}; use crate::Constant::{Pi, E}; @@ -245,51 +245,20 @@ fn evaluate_node(ast_node: &AstNode) -> Result { Ok(subtract(left, right)?) }, Multiply => { - if left.unit == Unit::NoUnit && right.unit == Unit::NoUnit { - // 3 * 2 - 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(Number::new(left.value * right.value, right.unit)) - } else if right.unit == Unit::NoUnit && left.unit != Unit::NoUnit { - // 1 km * 3 - return Ok(Number::new(left.value * right.value, left.unit)) - } else { - return Err(format!("Cannot multiply {:?} and {:?}", left.unit, right.unit)) - } + Ok(multiply(left, right)?) + // } }, Divide => { - if left.unit == Unit::NoUnit && right.unit == Unit::NoUnit { - // 3 / 2 - 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(Number::new(left.value / right.value, right.unit)) - } else { - return Err(format!("Cannot divide {:?} by {:?}", left.unit, right.unit)) - } + Ok(divide(left, right)?) + // } }, Modulo => { - if left.unit == Unit::NoUnit && right.unit == Unit::NoUnit { - // 3 / 2 - 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(Number::new(left.value % right.value, right.unit)) - } else { - return Err(format!("Cannot modulo {:?} by {:?}", left.unit, right.unit)) - } + Ok(modulo(left, right)?) + // } }, Caret => { - if left.unit == Unit::NoUnit && right.unit == Unit::NoUnit { - // 3 ^ 2 - 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(Number::new(left.value.pow(right.value), left.unit)) - } else { - return Err(format!("Cannot multiply {:?} and {:?}", left.unit, right.unit)) - } + Ok(pow(left, right)?) + // } }, _ => { Err(format!("Unexpected operator {:?}", operator)) diff --git a/src/units.rs b/src/units.rs index 17695cb..4f125fb 100644 --- a/src/units.rs +++ b/src/units.rs @@ -254,38 +254,97 @@ pub fn convert(number: Number, to_unit: Unit) -> Result { } } +pub fn convert_to_lowest(left: Number, right: Number) -> Result<(Number, Number), String> { + if left.unit.weight() == right.unit.weight() { + Ok((left, right)) + } else if left.unit.weight() > right.unit.weight() { + let left_converted = convert(left, right.unit)?; + Ok((left_converted, right)) + } else { + let right_converted = convert(right, left.unit)?; + Ok((left, right_converted)) + } +} + pub fn add(left: Number, right: Number) -> Result { - if left.unit == right.unit { + if left.unit.category() == right.unit.category() && left.unit.category() != Temperature { + let (left, right) = convert_to_lowest(left, right)?; Ok(Number::new(left.value + right.value, left.unit)) - } else if left.unit.category() == right.unit.category() && left.unit.category() != Temperature { - if left.unit.weight() > right.unit.weight() { - let left_converted = convert(left, right.unit)?; - Ok(Number::new(left_converted.value + right.value, right.unit)) - } else { - let right_converted = convert(right, left.unit)?; - Ok(Number::new(right_converted.value + left.value, left.unit)) - } } else { return Err(format!("Cannot add {:?} and {:?}", left.unit, right.unit)) } } pub fn subtract(left: Number, right: Number) -> Result { - if left.unit == right.unit { + if left.unit.category() == right.unit.category() && left.unit.category() != Temperature { + let (left, right) = convert_to_lowest(left, right)?; Ok(Number::new(left.value - right.value, left.unit)) - } else if left.unit.category() == right.unit.category() && left.unit.category() != Temperature { - if left.unit.weight() > right.unit.weight() { - let left_converted = convert(left, right.unit)?; - Ok(Number::new(left_converted.value - right.value, right.unit)) - } else { - let right_converted = convert(right, left.unit)?; - Ok(Number::new(right_converted.value - left.value, left.unit)) - } } else { return Err(format!("Cannot subtract {:?} by {:?}", left.unit, right.unit)) } } +pub fn multiply(left: Number, right: Number) -> Result { + if left.unit == Unit::NoUnit && right.unit == Unit::NoUnit { + // 3 * 2 + return Ok(Number::new(left.value * right.value, left.unit)) + } else if left.unit.category() == Temperature || right.unit.category() == Temperature { + // if temperature + return Err(format!("Cannot multiply {:?} and {:?}", left.unit, right.unit)) + } else if left.unit == Unit::NoUnit && right.unit != Unit::NoUnit { + // 3 * 1 km + 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(Number::new(left.value * right.value, left.unit)) + } else { + return Err(format!("Cannot multiply {:?} and {:?}", left.unit, right.unit)) + } +} + +pub fn divide(left: Number, right: Number) -> Result { + if left.unit == Unit::NoUnit && right.unit == Unit::NoUnit { + // 3 / 2 + Ok(Number::new(left.value / right.value, left.unit)) + } else if left.unit.category() == Temperature || right.unit.category() == Temperature { + // if temperature + return Err(format!("Cannot divide {:?} by {:?}", left.unit, right.unit)) + } else if left.unit != Unit::NoUnit && right.unit == Unit::NoUnit { + // 1 km / 2 + Ok(Number::new(left.value / right.value, right.unit)) + } else if left.unit.category() == right.unit.category() { + // 1 km / 1 km + let (left, right) = convert_to_lowest(left, right)?; + Ok(Number::new(left.value * right.value, Unit::NoUnit)) + } else { + Err(format!("Cannot divide {:?} by {:?}", left.unit, right.unit)) + } +} + +pub fn modulo(left: Number, right: Number) -> Result { + if left.unit == Unit::NoUnit && right.unit == Unit::NoUnit { + // 3 / 2 + 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(Number::new(left.value % right.value, right.unit)) + } else { + return Err(format!("Cannot modulo {:?} by {:?}", left.unit, right.unit)) + } +} + +pub fn pow(left: Number, right: Number) -> Result { + if left.unit == Unit::NoUnit && right.unit == Unit::NoUnit { + // 3 ^ 2 + 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(Number::new(left.value.pow(right.value), left.unit)) + } else { + return Err(format!("Cannot multiply {:?} and {:?}", left.unit, right.unit)) + } +} + #[cfg(test)] mod tests { use super::*;