mod arithmetic;
mod bitwise;
mod comparison;
mod conversion;
mod error;
mod format;
mod numeric;
mod parsing;
use std::{cmp::Ordering, f64};
use num_traits::ToPrimitive as _;
pub use error::{ArithmeticError, Error, ParseValueError};
macro_rules! dispatch_operation {
($lhs:expr, $rhs:expr, $n:ident, $op:expr) => {{
$lhs.match_orders(&mut $rhs);
debug_assert_eq!(
$lhs.order(),
$rhs.order(),
"orders must match after match_orders"
);
match $lhs {
Value::UnsignedInt(n) => {
let rhs = u64::try_from($rhs).expect("orders must match");
let $n = n;
$op(rhs)
}
Value::UnsignedBigInt(n) => {
let rhs = u128::try_from($rhs).expect("orders must match");
let $n = n;
$op(rhs)
}
Value::SignedInt(n) => {
let rhs = i64::try_from($rhs).expect("orders must match");
let $n = n;
$op(rhs)
}
Value::SignedBigInt(n) => {
let rhs = i128::try_from($rhs).expect("orders must match");
let $n = n;
$op(rhs)
}
Value::Float(n) => {
let rhs = f64::try_from($rhs).expect("orders must match");
let $n = n;
$op(rhs)
}
}
}};
(INTS: $lhs:expr, $rhs:expr, $n:ident, $op:expr) => {{
$lhs.match_orders(&mut $rhs);
debug_assert_eq!(
$lhs.order(),
$rhs.order(),
"orders must match after match_orders"
);
match $lhs {
Value::UnsignedInt(n) => {
let rhs = u64::try_from($rhs).expect("orders must match");
let $n = n;
Ok($op(rhs))
}
Value::UnsignedBigInt(n) => {
let rhs = u128::try_from($rhs).expect("orders must match");
let $n = n;
Ok($op(rhs))
}
Value::SignedInt(n) => {
let rhs = i64::try_from($rhs).expect("orders must match");
let $n = n;
Ok($op(rhs))
}
Value::SignedBigInt(n) => {
let rhs = i128::try_from($rhs).expect("orders must match");
let $n = n;
Ok($op(rhs))
}
Value::Float(_) => Err(Error::ImproperlyFloat),
}
}};
}
pub(crate) use dispatch_operation;
#[derive(
Debug,
Clone,
Copy,
strum::EnumDiscriminants,
derive_more::From,
derive_more::TryInto,
derive_more::Display,
derive_more::Binary,
derive_more::Octal,
derive_more::LowerHex,
derive_more::UpperHex,
derive_more::LowerExp,
derive_more::UpperExp,
)]
#[strum_discriminants(derive(PartialOrd, Ord))]
#[strum_discriminants(name(Order))]
pub enum Value {
UnsignedInt(u64),
UnsignedBigInt(u128),
SignedInt(i64),
SignedBigInt(i128),
#[binary("{_0}")]
#[octal("{_0}")]
#[lower_hex("{_0}")]
#[upper_hex("{_0}")]
Float(f64),
}
pub(crate) type Result<T = Value, E = Error> = std::result::Result<T, E>;
impl Value {
pub const PI: Self = Self::Float(f64::consts::PI);
pub const E: Self = Self::Float(f64::consts::E);
pub(crate) fn order(&self) -> Order {
Order::from(*self)
}
pub(crate) fn promote(&mut self) {
*self = match *self {
Value::UnsignedInt(n) => Self::UnsignedBigInt(n as _),
Value::UnsignedBigInt(n) => {
const SI_MAX: u128 = i64::MAX as _;
const SBI_MIN: u128 = SI_MAX + 1;
const SBI_MAX: u128 = i128::MAX as _;
match n {
0..=SI_MAX => Self::SignedInt(n as _),
SBI_MIN..=SBI_MAX => Self::SignedBigInt(n as _),
_ => Self::Float(n.to_f64().expect("all u128 convert to f64")),
}
}
Value::SignedInt(n) => Self::SignedBigInt(n as _),
Value::SignedBigInt(n) => Self::Float(n.to_f64().expect("all i128 convert to f64")),
Value::Float(n) => Self::Float(n),
}
}
pub(crate) fn promote_to_signed(&mut self) {
while self.order() <= Order::UnsignedBigInt {
self.promote();
}
}
pub(crate) fn promote_to_float(&mut self) -> &mut f64 {
*self = match *self {
Value::UnsignedInt(n) => (n as f64).into(),
Value::UnsignedBigInt(n) => (n as f64).into(),
Value::SignedInt(n) => (n as f64).into(),
Value::SignedBigInt(n) => (n as f64).into(),
Value::Float(n) => n.into(),
};
let Self::Float(ref mut f) = self else {
unreachable!("we just promoted up to float")
};
f
}
pub(crate) fn demote(&mut self) {
const ZERO: f64 = 0.0;
const UI_MAX: f64 = u64::MAX as _;
const UBI_MAX: f64 = u128::MAX as _;
const SI_MIN: f64 = i64::MIN as _;
const SI_MAX: f64 = i64::MAX as _;
const SBI_MIN: f64 = i128::MIN as _;
const SBI_MAX: f64 = i128::MAX as _;
let value = *self.clone().promote_to_float();
debug_assert!(
value.fract().abs() < f64::EPSILON,
"we should never demote values not already known to be integral"
);
let narrowest_order = [
(ZERO..=UI_MAX, Order::UnsignedInt),
(ZERO..=UBI_MAX, Order::UnsignedBigInt),
(SI_MIN..=SI_MAX, Order::SignedInt),
(SBI_MIN..=SBI_MAX, Order::SignedBigInt),
]
.into_iter()
.find_map(|(range, order)| range.contains(&value).then_some(order))
.unwrap_or(Order::Float);
let mut rhs = *self;
*self = dispatch_operation!(self, rhs, n, |_rhs| {
#[expect(clippy::unnecessary_cast)]
match narrowest_order {
Order::UnsignedInt => (*n as u64).into(),
Order::UnsignedBigInt => (*n as u128).into(),
Order::SignedInt => (*n as i64).into(),
Order::SignedBigInt => (*n as i128).into(),
Order::Float => (*n as f64).into(),
}
});
}
pub(crate) fn match_orders(&mut self, other: &mut Self) {
while self.order() != other.order() {
match self.order().cmp(&other.order()) {
Ordering::Equal => unreachable!("orders already known not to be equal"),
Ordering::Less => self.promote(),
Ordering::Greater => other.promote(),
}
}
}
}