2020-06-13 19:13:02 -04:00
|
|
|
use crate::parser::OwnedLiteral;
|
2020-06-13 22:46:03 -04:00
|
|
|
use std::convert::TryFrom;
|
2020-06-13 19:13:02 -04:00
|
|
|
use std::convert::TryInto;
|
|
|
|
use std::ops::Add;
|
2020-06-13 22:23:40 -04:00
|
|
|
use std::ops::Div;
|
|
|
|
use std::ops::Mul;
|
|
|
|
use std::ops::Rem;
|
|
|
|
use std::ops::Sub;
|
2020-06-13 19:13:02 -04:00
|
|
|
|
|
|
|
#[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<MathNumber> for MathNumber {
|
|
|
|
type Output = Option<OwnedLiteral>;
|
|
|
|
|
|
|
|
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)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-13 22:23:40 -04:00
|
|
|
impl Sub<MathNumber> for MathNumber {
|
|
|
|
type Output = Option<OwnedLiteral>;
|
|
|
|
|
|
|
|
fn sub(self, other: MathNumber) -> Self::Output {
|
2020-06-13 22:32:30 -04:00
|
|
|
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)))
|
|
|
|
}
|
|
|
|
}
|
2020-06-13 22:23:40 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Mul<MathNumber> for MathNumber {
|
|
|
|
type Output = Option<OwnedLiteral>;
|
|
|
|
|
|
|
|
fn mul(self, other: MathNumber) -> Self::Output {
|
2020-06-13 22:32:30 -04:00
|
|
|
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)))
|
|
|
|
}
|
|
|
|
}
|
2020-06-13 22:23:40 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Div<MathNumber> for MathNumber {
|
|
|
|
type Output = Option<OwnedLiteral>;
|
|
|
|
|
|
|
|
fn div(self, other: MathNumber) -> Self::Output {
|
2020-06-13 22:32:30 -04:00
|
|
|
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)))
|
|
|
|
}
|
|
|
|
}
|
2020-06-13 22:23:40 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Rem<MathNumber> for MathNumber {
|
|
|
|
type Output = Option<OwnedLiteral>;
|
|
|
|
|
|
|
|
fn rem(self, other: MathNumber) -> Self::Output {
|
2020-06-13 22:32:30 -04:00
|
|
|
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)))
|
|
|
|
}
|
|
|
|
}
|
2020-06-13 22:23:40 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl MathNumber {
|
2020-06-13 22:52:07 -04:00
|
|
|
pub fn math_abs(&self) -> Option<OwnedLiteral> {
|
2020-06-13 22:46:03 -04:00
|
|
|
match self {
|
|
|
|
MathNumber::Failure => None,
|
|
|
|
MathNumber::Integer(num) => num.abs().try_into().ok(),
|
|
|
|
MathNumber::Decimal(num) => Some(OwnedLiteral::LFloat(num.abs())),
|
|
|
|
}
|
2020-06-13 22:23:40 -04:00
|
|
|
}
|
2020-06-13 22:52:07 -04:00
|
|
|
pub fn math_floor(&self) -> Option<OwnedLiteral> {
|
2020-06-13 22:46:03 -04:00
|
|
|
match self {
|
|
|
|
MathNumber::Failure => None,
|
|
|
|
MathNumber::Integer(num) => (*num).try_into().ok(),
|
|
|
|
MathNumber::Decimal(num) => Some(OwnedLiteral::LFloat(num.floor())),
|
|
|
|
}
|
2020-06-13 22:23:40 -04:00
|
|
|
}
|
2020-06-13 22:52:07 -04:00
|
|
|
pub fn math_ceil(&self) -> Option<OwnedLiteral> {
|
2020-06-13 22:46:03 -04:00
|
|
|
match self {
|
|
|
|
MathNumber::Failure => None,
|
|
|
|
MathNumber::Integer(num) => (*num).try_into().ok(),
|
|
|
|
MathNumber::Decimal(num) => Some(OwnedLiteral::LFloat(num.ceil())),
|
|
|
|
}
|
2020-06-13 22:23:40 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-13 19:13:02 -04:00
|
|
|
/// For math operations that take in integers and return integers
|
|
|
|
/// (add, subtract, multiply)
|
|
|
|
pub fn math_ints<L, R, F>(left: L, right: R, operation: F) -> Option<OwnedLiteral>
|
|
|
|
where
|
|
|
|
L: Into<i128>,
|
|
|
|
R: Into<i128>,
|
|
|
|
F: Fn(i128, i128) -> i128,
|
|
|
|
{
|
2020-06-13 22:46:03 -04:00
|
|
|
operation(left.into(), right.into()).try_into().ok()
|
2020-06-13 19:13:02 -04:00
|
|
|
}
|
|
|
|
|
2020-06-13 22:46:03 -04:00
|
|
|
impl TryFrom<i128> for OwnedLiteral {
|
|
|
|
type Error = &'static str;
|
|
|
|
|
|
|
|
fn try_from(original: i128) -> Result<Self, Self::Error> {
|
|
|
|
std::convert::TryInto::<u64>::try_into(original)
|
|
|
|
.map(OwnedLiteral::LPositiveInteger)
|
|
|
|
.ok()
|
|
|
|
.or(std::convert::TryInto::<i64>::try_into(original)
|
|
|
|
.map(OwnedLiteral::LNegativeInteger)
|
|
|
|
.ok())
|
|
|
|
.ok_or("Value does not fit into either u64 or i64")
|
|
|
|
}
|
|
|
|
}
|