use crate::parser::OwnedLiteral; use std::convert::TryFrom; use std::convert::TryInto; use std::ops::Add; use std::ops::Div; use std::ops::Mul; use std::ops::Rem; use std::ops::Sub; #[derive(Debug)] pub enum MathNumber { Integer(i128), Decimal(f64), Failure, } impl From<&OwnedLiteral> for MathNumber { fn from(original: &OwnedLiteral) -> Self { match original { OwnedLiteral::LString(_) => panic!("Strings should not be cast to numbers for math"), OwnedLiteral::LPositiveInteger(num) => { return MathNumber::Integer((*num).try_into().unwrap()) } OwnedLiteral::LNegativeInteger(num) => return MathNumber::Integer((*num).into()), OwnedLiteral::LFloat(num) => return MathNumber::Decimal(*num), } } } impl Add for MathNumber { type Output = Option; fn add(self, other: MathNumber) -> Self::Output { match (self, other) { (MathNumber::Failure, _) | (_, MathNumber::Failure) => None, (MathNumber::Integer(self_num), MathNumber::Integer(other_num)) => { math_ints(self_num, other_num, std::ops::Add::add) } (MathNumber::Decimal(self_num), MathNumber::Decimal(other_num)) => { Some(OwnedLiteral::LFloat(self_num + other_num)) } (MathNumber::Integer(self_num), MathNumber::Decimal(other_num)) => { Some(OwnedLiteral::LFloat((self_num as f64) + other_num)) } (MathNumber::Decimal(self_num), MathNumber::Integer(other_num)) => { Some(OwnedLiteral::LFloat(self_num + (other_num as f64))) } } } } impl Sub for MathNumber { type Output = Option; fn sub(self, other: MathNumber) -> Self::Output { match (self, other) { (MathNumber::Failure, _) | (_, MathNumber::Failure) => None, (MathNumber::Integer(self_num), MathNumber::Integer(other_num)) => { math_ints(self_num, other_num, std::ops::Sub::sub) } (MathNumber::Decimal(self_num), MathNumber::Decimal(other_num)) => { Some(OwnedLiteral::LFloat(self_num - other_num)) } (MathNumber::Integer(self_num), MathNumber::Decimal(other_num)) => { Some(OwnedLiteral::LFloat((self_num as f64) - other_num)) } (MathNumber::Decimal(self_num), MathNumber::Integer(other_num)) => { Some(OwnedLiteral::LFloat(self_num - (other_num as f64))) } } } } impl Mul for MathNumber { type Output = Option; fn mul(self, other: MathNumber) -> Self::Output { match (self, other) { (MathNumber::Failure, _) | (_, MathNumber::Failure) => None, (MathNumber::Integer(self_num), MathNumber::Integer(other_num)) => { math_ints(self_num, other_num, std::ops::Mul::mul) } (MathNumber::Decimal(self_num), MathNumber::Decimal(other_num)) => { Some(OwnedLiteral::LFloat(self_num * other_num)) } (MathNumber::Integer(self_num), MathNumber::Decimal(other_num)) => { Some(OwnedLiteral::LFloat((self_num as f64) * other_num)) } (MathNumber::Decimal(self_num), MathNumber::Integer(other_num)) => { Some(OwnedLiteral::LFloat(self_num * (other_num as f64))) } } } } impl Div for MathNumber { type Output = Option; fn div(self, other: MathNumber) -> Self::Output { match (self, other) { (MathNumber::Failure, _) | (_, MathNumber::Failure) => None, (MathNumber::Integer(self_num), MathNumber::Integer(other_num)) => { Some(OwnedLiteral::LFloat((self_num as f64) / (other_num as f64))) } (MathNumber::Decimal(self_num), MathNumber::Decimal(other_num)) => { Some(OwnedLiteral::LFloat(self_num / other_num)) } (MathNumber::Integer(self_num), MathNumber::Decimal(other_num)) => { Some(OwnedLiteral::LFloat((self_num as f64) / other_num)) } (MathNumber::Decimal(self_num), MathNumber::Integer(other_num)) => { Some(OwnedLiteral::LFloat(self_num / (other_num as f64))) } } } } impl Rem for MathNumber { type Output = Option; fn rem(self, other: MathNumber) -> Self::Output { match (self, other) { (MathNumber::Failure, _) | (_, MathNumber::Failure) => None, (MathNumber::Integer(self_num), MathNumber::Integer(other_num)) => { math_ints(self_num, other_num, std::ops::Rem::rem) } (MathNumber::Decimal(self_num), MathNumber::Decimal(other_num)) => { Some(OwnedLiteral::LFloat(self_num % other_num)) } (MathNumber::Integer(self_num), MathNumber::Decimal(other_num)) => { Some(OwnedLiteral::LFloat((self_num as f64) % other_num)) } (MathNumber::Decimal(self_num), MathNumber::Integer(other_num)) => { Some(OwnedLiteral::LFloat(self_num % (other_num as f64))) } } } } impl MathNumber { pub fn math_abs(&self) -> Option { match self { MathNumber::Failure => None, MathNumber::Integer(num) => num.abs().try_into().ok(), MathNumber::Decimal(num) => Some(OwnedLiteral::LFloat(num.abs())), } } pub fn math_floor(&self) -> Option { match self { MathNumber::Failure => None, MathNumber::Integer(num) => (*num).try_into().ok(), MathNumber::Decimal(num) => Some(OwnedLiteral::LFloat(num.floor())), } } pub fn math_ceil(&self) -> Option { match self { MathNumber::Failure => None, MathNumber::Integer(num) => (*num).try_into().ok(), MathNumber::Decimal(num) => Some(OwnedLiteral::LFloat(num.ceil())), } } } /// For math operations that take in integers and return integers /// (add, subtract, multiply) pub fn math_ints(left: L, right: R, operation: F) -> Option where L: Into, R: Into, F: Fn(i128, i128) -> i128, { operation(left.into(), right.into()).try_into().ok() } impl TryFrom for OwnedLiteral { type Error = &'static str; fn try_from(original: i128) -> Result { std::convert::TryInto::::try_into(original) .map(OwnedLiteral::LPositiveInteger) .ok() .or(std::convert::TryInto::::try_into(original) .map(OwnedLiteral::LNegativeInteger) .ok()) .ok_or("Value does not fit into either u64 or i64") } }