use std::fmt; // The data representations of values of different types are given below. // - 0 represents an undefined value. Any operation with 0 will cause an error. // - Pointers are 64 bit unsigned (positive) ints which are // word-aligned, i.e. 0 mod 8. All types not listed below are // allocated on the heap behind a pointer. // - Int: // < 63-bit signed int > 1 // Ints are a 63 bit int with a 1 in the low bit. // - Booleans and nil are 2 mod 8: // - False is 0x2 // - True is 0xA // - Nil is 0x12 // - A symbol's low byte is 0x4, and the high 56 bytes are an unsigned integer constant // representing a pointer into the symbol table. // // n.b. 6 mod 8 is unused. #[derive(PartialEq, Eq, Debug, Hash, Clone, Copy)] pub struct Pointer(pub usize); impl Pointer { pub fn offset(self, i: usize) -> Self { let Pointer(u) = self; Pointer(u + i) } } impl fmt::LowerHex for Pointer { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let val = self.0; fmt::LowerHex::fmt(&val, f) } } #[derive(PartialEq, Eq, Debug, Clone, Copy)] pub struct Value(pub u64); impl Value { pub fn is_pointer(self) -> bool { let Value(stack_representation) = self; return stack_representation != 0 && stack_representation & 0x7 == 0; } pub fn from_pointer(p: Pointer) -> Self { let Pointer(x) = p; return Value(u64::try_from(x).unwrap()); } pub fn to_pointer(self) -> Result { let Value(stack_representation) = self; if !self.is_pointer() { return Err(format!( "value 0x{:x} is not a pointer", stack_representation )); } return Ok(Pointer(usize::try_from(stack_representation).unwrap())); } pub fn is_int(self) -> bool { let Value(stack_representation) = self; return stack_representation & 0x1 == 1; } pub fn from_int(i: i64) -> Self { return Value((i as u64) << 1 | 1); } pub fn to_int(self) -> Result { let Value(stack_representation) = self; if !self.is_int() { return Err(format!("value 0x{:x} is not an int", stack_representation)); } return Ok(stack_representation as i64 >> 1); } pub fn from_bool(b: bool) -> Self { if b { return Value(0xa); } return Value(0x2); } pub const NIL: Self = Value(0x12); } #[cfg(test)] mod tests { use super::*; #[test] fn is_pointer() { assert_eq!(true, Value(0xf8).is_pointer()); } #[test] fn is_not_pointer() { assert_eq!(false, Value(1).is_pointer()); } #[test] fn from_pointer() { assert_eq!(Value(0xf8), Value::from_pointer(Pointer(0xf8))); } #[test] fn to_pointer() { assert_eq!(Ok(Pointer(0xf8)), Value(0xf8).to_pointer()); } #[test] fn is_int() { assert_eq!(true, Value(1).is_int()); } #[test] fn is_not_int() { assert_eq!(false, Value(0).is_int()); } #[test] fn from_int() { assert_eq!(Value(0xb), Value::from_int(5)); } #[test] fn to_int() { assert_eq!(Ok(5), Value(0xb).to_int()); } #[test] fn true_from_bool() { assert_eq!(Value(0xa), Value::from_bool(true)); } #[test] fn false_from_bool() { assert_eq!(Value(2), Value::from_bool(false)); } }