// The data representations of values of different types are given below. // - Nil is represented as 0. // - 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 are 2 mod 8: // - True is 0xA // - False is 0x2 // - A char's low 4 bytes are always 0x4, and the high 4 bytes are a // unicode code point value. // // 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: i64) -> Self { let Pointer(u) = self; if i > 0 { return Pointer(u + usize::try_from(i).unwrap()); } return Pointer(u - usize::try_from(-i).unwrap()); } } #[derive(PartialEq, Eq, Debug, Clone, Copy)] pub struct Value(pub u64); impl Value { pub fn is_nil(self) -> bool { let Value(stack_representation) = self; return stack_representation == 0; } pub fn is_pointer(self) -> bool { let Value(stack_representation) = self; return 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 {: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 {:x} is not an int", stack_representation)); } return Ok(stack_representation as i64 >> 1); } pub fn is_bool(self) -> bool { let Value(stack_representation) = self; return stack_representation == 0xa || stack_representation == 0x2; } pub fn from_bool(b: bool) -> Self { if b { return Value(0xa); } return Value(0x2); } pub fn to_bool(self) -> Result { let Value(stack_representation) = self; if !self.is_bool() { return Err(format!("value {:x} is not a boolean", stack_representation)); } return Ok(stack_representation == 0xa); } pub fn is_char(self) -> bool { let Value(stack_representation) = self; return stack_representation & 0x7 == 4; } pub fn from_char(c: char) -> Self { return Value(u64::from(c) << 32 | 0x4); } pub fn to_char(self) -> Result { let Value(stack_representation) = self; if !self.is_char() { return Err(format!("value {:x} is not a char", stack_representation)); } return Ok( char::from_u32((stack_representation >> 32) as u32).ok_or(format!( "value {:x} is not a valid char", stack_representation >> 32 ))?, ); } } #[cfg(test)] mod tests { use super::*; #[test] fn is_nil() { assert_eq!(true, Value(0).is_nil()); } #[test] fn is_not_nil() { assert_eq!(false, Value(1).is_nil()); } #[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_is_bool() { assert_eq!(true, Value(0xa).is_bool()); } #[test] fn false_is_bool() { assert_eq!(true, Value(2).is_bool()); } #[test] fn is_not_bool() { assert_eq!(false, Value(0).is_bool()); } #[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)); } #[test] fn true_to_bool() { assert_eq!(Ok(true), Value(0xa).to_bool()); } #[test] fn false_to_bool() { assert_eq!(Ok(false), Value(2).to_bool()); } #[test] fn is_char() { assert_eq!(true, Value(0x6100000004).is_char()); } #[test] fn is_not_char() { assert_eq!(false, Value(0).is_char()); } #[test] fn from_char() { assert_eq!(Value(0x5800000004), Value::from_char('X')); } #[test] fn to_char() { assert_eq!(Ok('😂'), Value(0x1f60200000004).to_char()); } }