use super::rtobject_type::RtObject;
use super::types::{
ArrayOps, BigIntData, FieldAccess, HeapObject, ObjectFlags, ObjectHeader, ObjectTable, RtArith,
RtObjectCmp, RtObjectPool, StringOps, TypeRegistry, TypeTag,
};
pub(super) const TAG_HEAP: u8 = 0x00;
pub(super) const TAG_SMALL_NAT: u8 = 0x01;
pub(super) const TAG_BOOL: u8 = 0x02;
pub(super) const TAG_UNIT: u8 = 0x03;
pub(super) const TAG_CHAR: u8 = 0x04;
pub(super) const TAG_CTOR: u8 = 0x05;
pub(super) const TAG_INT: u8 = 0x06;
pub(super) const TAG_FLOAT_BITS: u8 = 0x07;
pub(super) const TAG_CLOSURE: u8 = 0x08;
pub(super) const TAG_ARRAY: u8 = 0x09;
pub(super) const TAG_STRING: u8 = 0x0A;
pub(super) const TAG_THUNK: u8 = 0x0B;
pub(super) const TAG_IO_ACTION: u8 = 0x0C;
pub(super) const TAG_TASK: u8 = 0x0D;
pub(super) const TAG_EXTERNAL: u8 = 0x0E;
pub(super) const _TAG_RESERVED: u8 = 0x0F;
pub(super) const PAYLOAD_MASK: u64 = 0x00FF_FFFF_FFFF_FFFF;
pub(super) const MAX_SMALL_NAT: u64 = PAYLOAD_MASK;
pub(super) const MAX_SMALL_INT: i64 = (1_i64 << 55) - 1;
pub(super) const MIN_SMALL_INT: i64 = -(1_i64 << 55);
pub trait BoxInto {
fn box_into(self) -> RtObject;
}
pub trait UnboxFrom: Sized {
fn unbox_from(obj: &RtObject) -> Option<Self>;
}
impl BoxInto for bool {
fn box_into(self) -> RtObject {
RtObject::bool_val(self)
}
}
impl UnboxFrom for bool {
fn unbox_from(obj: &RtObject) -> Option<Self> {
obj.as_bool()
}
}
impl BoxInto for u64 {
fn box_into(self) -> RtObject {
RtObject::nat(self)
}
}
impl UnboxFrom for u64 {
fn unbox_from(obj: &RtObject) -> Option<Self> {
obj.as_small_nat()
}
}
impl BoxInto for i64 {
fn box_into(self) -> RtObject {
RtObject::small_int(self).unwrap_or_else(|| {
let negative = self < 0;
let magnitude = if negative {
(self as i128).unsigned_abs() as u64
} else {
self as u64
};
let heap = HeapObject::BigInt(BigIntData {
header: ObjectHeader::new(TypeTag::BigInt, 2),
negative,
digits: vec![magnitude],
});
RtObject::from_heap(heap)
})
}
}
impl UnboxFrom for i64 {
fn unbox_from(obj: &RtObject) -> Option<Self> {
obj.as_small_int()
}
}
impl BoxInto for char {
fn box_into(self) -> RtObject {
RtObject::char_val(self)
}
}
impl UnboxFrom for char {
fn unbox_from(obj: &RtObject) -> Option<Self> {
obj.as_char()
}
}
impl BoxInto for f64 {
fn box_into(self) -> RtObject {
RtObject::boxed_float(self)
}
}
impl BoxInto for String {
fn box_into(self) -> RtObject {
RtObject::string(self)
}
}
impl BoxInto for &str {
fn box_into(self) -> RtObject {
RtObject::string(self.to_string())
}
}
impl BoxInto for () {
fn box_into(self) -> RtObject {
RtObject::unit()
}
}
impl UnboxFrom for () {
fn unbox_from(obj: &RtObject) -> Option<Self> {
if obj.is_unit() {
Some(())
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_small_nat() {
let n = RtObject::nat(42);
assert!(n.is_nat());
assert_eq!(n.as_small_nat(), Some(42));
}
#[test]
fn test_bool() {
let t = RtObject::bool_val(true);
let f = RtObject::bool_val(false);
assert_eq!(t.as_bool(), Some(true));
assert_eq!(f.as_bool(), Some(false));
}
#[test]
fn test_unit() {
let u = RtObject::unit();
assert!(u.is_unit());
}
#[test]
fn test_char() {
let c = RtObject::char_val('A');
assert_eq!(c.as_char(), Some('A'));
}
#[test]
fn test_small_int() {
let positive = RtObject::small_int(42).expect("test operation should succeed");
assert_eq!(positive.as_small_int(), Some(42));
let negative = RtObject::small_int(-7).expect("test operation should succeed");
assert_eq!(negative.as_small_int(), Some(-7));
}
#[test]
fn test_object_header_encode_decode() {
let header = ObjectHeader::new(TypeTag::Closure, 5);
let encoded = header.encode();
let decoded = ObjectHeader::decode(encoded).expect("test operation should succeed");
assert_eq!(decoded.type_tag, TypeTag::Closure);
assert_eq!(decoded.size_words, 5);
assert_eq!(decoded.rc_count, 1);
}
#[test]
fn test_object_flags() {
let mut flags = ObjectFlags::empty();
assert!(!flags.has(ObjectFlags::pinned()));
flags.set(ObjectFlags::pinned());
assert!(flags.has(ObjectFlags::pinned()));
flags.clear(ObjectFlags::pinned());
assert!(!flags.has(ObjectFlags::pinned()));
}
#[test]
fn test_nat_arithmetic() {
let a = RtObject::nat(10);
let b = RtObject::nat(3);
assert_eq!(
RtArith::nat_add(&a, &b)
.expect("type conversion should succeed")
.as_small_nat(),
Some(13)
);
assert_eq!(
RtArith::nat_sub(&a, &b)
.expect("type conversion should succeed")
.as_small_nat(),
Some(7)
);
assert_eq!(
RtArith::nat_mul(&a, &b)
.expect("type conversion should succeed")
.as_small_nat(),
Some(30)
);
assert_eq!(
RtArith::nat_div(&a, &b)
.expect("type conversion should succeed")
.as_small_nat(),
Some(3)
);
assert_eq!(
RtArith::nat_mod(&a, &b)
.expect("type conversion should succeed")
.as_small_nat(),
Some(1)
);
}
#[test]
fn test_object_table() {
let mut table = ObjectTable::new();
table.insert("x".to_string(), RtObject::nat(42));
table.insert("y".to_string(), RtObject::bool_val(true));
assert_eq!(table.len(), 2);
assert_eq!(
table
.get("x")
.expect("key should exist in map")
.as_small_nat(),
Some(42)
);
assert!(table.contains("y"));
}
#[test]
fn test_type_registry_builtins() {
let mut registry = TypeRegistry::new();
registry.register_builtins();
assert!(registry.lookup("Nat").is_some());
assert!(registry.lookup("Bool").is_some());
assert!(registry.lookup("Unit").is_some());
assert!(registry.lookup("List").is_some());
}
#[test]
fn test_boxing() {
let b: RtObject = true.box_into();
assert_eq!(bool::unbox_from(&b), Some(true));
let n: RtObject = 42u64.box_into();
assert_eq!(u64::unbox_from(&n), Some(42));
let u: RtObject = ().box_into();
assert_eq!(<()>::unbox_from(&u), Some(()));
}
#[test]
fn test_string_object() {
let s = RtObject::string("hello".to_string());
assert!(s.is_string_ref());
assert_eq!(StringOps::as_str(&s), Some("hello".to_string()));
assert_eq!(StringOps::byte_len(&s), Some(5));
}
#[test]
fn test_array_object() {
let arr = RtObject::array(vec![RtObject::nat(1), RtObject::nat(2), RtObject::nat(3)]);
assert_eq!(ArrayOps::len(&arr), Some(3));
assert_eq!(
ArrayOps::get(&arr, 1)
.expect("type conversion should succeed")
.as_small_nat(),
Some(2)
);
}
#[test]
fn test_constructor_fields() {
let pair = RtObject::constructor(0, vec![RtObject::nat(1), RtObject::nat(2)]);
assert_eq!(FieldAccess::get_ctor_index(&pair), Some(0));
assert_eq!(FieldAccess::num_fields(&pair), Some(2));
assert_eq!(
FieldAccess::proj_fst(&pair)
.expect("type conversion should succeed")
.as_small_nat(),
Some(1)
);
assert_eq!(
FieldAccess::proj_snd(&pair)
.expect("type conversion should succeed")
.as_small_nat(),
Some(2)
);
}
#[test]
fn test_bool_ops() {
let t = RtObject::bool_val(true);
let f = RtObject::bool_val(false);
assert_eq!(
RtArith::bool_and(&t, &f)
.expect("type conversion should succeed")
.as_bool(),
Some(false)
);
assert_eq!(
RtArith::bool_or(&t, &f)
.expect("type conversion should succeed")
.as_bool(),
Some(true)
);
assert_eq!(
RtArith::bool_not(&t)
.expect("type conversion should succeed")
.as_bool(),
Some(false)
);
}
}
#[cfg(test)]
mod extra_object_tests {
use super::*;
#[test]
fn test_numeric_eq_int() {
let a = RtObject::small_int(10).expect("test operation should succeed");
let b = RtObject::small_int(10).expect("test operation should succeed");
let c = RtObject::small_int(11).expect("test operation should succeed");
assert!(RtObjectCmp::numeric_eq(&a, &b));
assert!(!RtObjectCmp::numeric_eq(&a, &c));
}
#[test]
fn test_int_lt() {
let a = RtObject::small_int(3).expect("test operation should succeed");
let b = RtObject::small_int(5).expect("test operation should succeed");
assert_eq!(RtObjectCmp::int_lt(&a, &b), Some(true));
assert_eq!(RtObjectCmp::int_lt(&b, &a), Some(false));
}
#[test]
fn test_object_pool() {
let mut pool = RtObjectPool::new();
let obj = pool.acquire_unit();
pool.release(obj);
assert_eq!(pool.free_count(), 1);
let _ = pool.acquire_unit();
assert_eq!(pool.free_count(), 0);
}
}