use shape_value::{KindedSlot, NativeKind, VMError, heap_value::HeapKind};
#[inline]
pub(crate) fn coerce_to_f64(slot: &KindedSlot) -> Option<f64> {
match slot.kind {
NativeKind::Int64 => slot.as_i64().map(|i| i as f64),
NativeKind::Float64 => slot.as_f64(),
_ => None,
}
}
#[inline]
pub(crate) fn number_operand(slot: &KindedSlot) -> Result<f64, VMError> {
coerce_to_f64(slot).ok_or_else(|| {
VMError::RuntimeError(format!("expected int or float, got {:?}", slot.kind))
})
}
#[inline]
pub(crate) fn int_operand(slot: &KindedSlot) -> Result<i64, VMError> {
match slot.kind {
NativeKind::Int8
| NativeKind::Int16
| NativeKind::Int32
| NativeKind::Int64
| NativeKind::IntSize
| NativeKind::UInt8
| NativeKind::UInt16
| NativeKind::UInt32
| NativeKind::UInt64
| NativeKind::UIntSize => slot.as_i64().ok_or_else(|| {
VMError::RuntimeError(format!("expected integer, got {:?}", slot.kind))
}),
_ => Err(VMError::RuntimeError(format!(
"expected integer, got {:?}",
slot.kind
))),
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum NumericDomain {
Int,
Float,
Decimal,
BigInt,
}
#[inline]
pub(crate) fn numeric_domain(slot: &KindedSlot) -> Result<NumericDomain, VMError> {
match slot.kind {
k if k.is_integer_family() => Ok(NumericDomain::Int),
NativeKind::Float64 | NativeKind::NullableFloat64 => Ok(NumericDomain::Float),
NativeKind::Ptr(HeapKind::Decimal) => Ok(NumericDomain::Decimal),
NativeKind::Ptr(HeapKind::BigInt) => Ok(NumericDomain::BigInt),
_ => Err(VMError::RuntimeError(format!(
"expected numeric, got {:?}",
slot.kind
))),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn coerce_to_f64_int_widens() {
let s = KindedSlot::from_int(42);
assert_eq!(coerce_to_f64(&s), Some(42.0));
}
#[test]
fn coerce_to_f64_float_passes_through() {
let s = KindedSlot::from_number(3.14);
assert_eq!(coerce_to_f64(&s), Some(3.14));
}
#[test]
fn coerce_to_f64_bool_returns_none() {
let s = KindedSlot::from_bool(true);
assert_eq!(coerce_to_f64(&s), None);
}
#[test]
fn coerce_to_f64_string_returns_none() {
use std::sync::Arc;
let s = KindedSlot::from_string_arc(Arc::new("nope".to_string()));
assert_eq!(coerce_to_f64(&s), None);
}
#[test]
fn number_operand_int_widens() {
let s = KindedSlot::from_int(42);
assert_eq!(number_operand(&s).unwrap(), 42.0);
}
#[test]
fn number_operand_float_passes_through() {
let s = KindedSlot::from_number(2.718);
assert_eq!(number_operand(&s).unwrap(), 2.718);
}
#[test]
fn number_operand_bool_errors() {
let s = KindedSlot::from_bool(true);
assert!(number_operand(&s).is_err());
}
#[test]
fn int_operand_int64_passes_through() {
let s = KindedSlot::from_int(99);
assert_eq!(int_operand(&s).unwrap(), 99);
}
#[test]
fn int_operand_float_errors() {
let s = KindedSlot::from_number(1.0);
assert!(int_operand(&s).is_err());
}
#[test]
fn int_operand_bool_errors() {
let s = KindedSlot::from_bool(false);
assert!(int_operand(&s).is_err());
}
#[test]
fn numeric_domain_int_classifies() {
let s = KindedSlot::from_int(42);
assert_eq!(numeric_domain(&s).unwrap(), NumericDomain::Int);
}
#[test]
fn numeric_domain_float_classifies() {
let s = KindedSlot::from_number(3.14);
assert_eq!(numeric_domain(&s).unwrap(), NumericDomain::Float);
}
#[test]
fn numeric_domain_decimal_classifies() {
use std::sync::Arc;
let s = KindedSlot::from_decimal(Arc::new(rust_decimal::Decimal::new(123, 2)));
assert_eq!(numeric_domain(&s).unwrap(), NumericDomain::Decimal);
}
#[test]
fn numeric_domain_bigint_classifies() {
use std::sync::Arc;
let s = KindedSlot::from_bigint(Arc::new(1_000_000_000_000));
assert_eq!(numeric_domain(&s).unwrap(), NumericDomain::BigInt);
}
#[test]
fn numeric_domain_bool_errors() {
let s = KindedSlot::from_bool(true);
assert!(numeric_domain(&s).is_err());
}
#[test]
fn numeric_domain_string_errors() {
use std::sync::Arc;
let s = KindedSlot::from_string_arc(Arc::new("nope".to_string()));
assert!(numeric_domain(&s).is_err());
}
}