aboutsummaryrefslogtreecommitdiffstats
path: root/bytecode/src/data.rs
diff options
context:
space:
mode:
authorRose Hogenson <rhogenson@posteo.net>2022-03-03 13:48:19 -0800
committerRose Hogenson <rhogenson@posteo.net>2022-03-03 13:48:19 -0800
commit056fbf7e7dc727d020ba54e439f2b47411c58e76 (patch)
treea85cd582c4b06932463c4754ef466216d9afc8aa /bytecode/src/data.rs
parent1dd8b1f6eaf611cf5525065aa18f20baa1ada5f3 (diff)
downloadchromatopelma-056fbf7e7dc727d020ba54e439f2b47411c58e76.tar.zst
Start the garbage collector.
I don't think we finished it, but I wrote this code a while ago and I'm just trying to get it committed.
Diffstat (limited to 'bytecode/src/data.rs')
-rw-r--r--bytecode/src/data.rs225
1 files changed, 225 insertions, 0 deletions
diff --git a/bytecode/src/data.rs b/bytecode/src/data.rs
new file mode 100644
index 0000000..9735fa9
--- /dev/null
+++ b/bytecode/src/data.rs
@@ -0,0 +1,225 @@
+// 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<Pointer, String> {
+ 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<i64, String> {
+ 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<bool, String> {
+ 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<char, String> {
+ 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());
+ }
+}