diff options
| -rw-r--r-- | bytecode/src/bytecode.rs | 141 | ||||
| -rw-r--r-- | bytecode/src/collector.rs | 1 | ||||
| -rw-r--r-- | bytecode/src/data.rs | 225 | ||||
| -rw-r--r-- | bytecode/src/encoding.rs | 12 | ||||
| -rw-r--r-- | bytecode/src/heap.rs | 225 | ||||
| -rw-r--r-- | bytecode/src/main.rs | 1 | ||||
| -rw-r--r-- | bytecode/src/stack.rs | 33 |
7 files changed, 561 insertions, 77 deletions
diff --git a/bytecode/src/bytecode.rs b/bytecode/src/bytecode.rs index ab6edf9..84a9b8b 100644 --- a/bytecode/src/bytecode.rs +++ b/bytecode/src/bytecode.rs @@ -1,4 +1,5 @@ -use crate::heap::{Heap, Pointer}; +use crate::data::{Pointer, Value}; +use crate::heap::Heap; use std::io::{BufWriter, Read, StdinLock, Write}; // The cute scheme virtual machine has two stacks: @@ -39,21 +40,25 @@ pub enum Op { // ====== // ( n -- a ) - // Allocates n bytes and returns the address. + // Allocates n words and returns the address. Alloc, - // ( a -- u ) + // ( n -- a ) + // Allocates n bytes and returns the address. The contents of this allocation will be treated + // as raw data and not walked by the garbage collector. + AllocBytevector, + // ( a i -- u ) // Fetches a 64 bit word from the specified address plus the // given offset. - Peek(u8), - // ( u a ) + Peek, + // ( u a i ) // Stores a 64 bit word at the specified address plus the // given offset. - Poke(u8), - // ( a -- n ) - // Fetches a byte from the specified address. + Poke, + // ( a i -- n ) + // Fetches a byte from the specified address plus the given offset. PeekByte, - // ( n a ) - // Stores a byte at the specified address. + // ( n a i ) + // Stores a byte at the specified address plus the given offset. PokeByte, // Stack @@ -70,7 +75,7 @@ pub enum Op { // ============ // ( f -- ) - // Jumps to the specified offset if the argument is non-zero. + // Jumps to the specified offset if the argument is not false. If(i64), // ( i-addr a1 a2 ... an ) // ( Locals stack: -- instruction-ptr a1 a2 ... an argument-ptr ) @@ -98,36 +103,43 @@ pub enum Op { } struct Stack { - v: Vec<u64>, + v: Vec<Value>, } impl Stack { - fn pop(&mut self) -> Result<u64, String> { - self.v.pop().ok_or(String::from("stack underflow")) + fn pop(&mut self) -> Result<Value, String> { + return self.v.pop().ok_or(String::from("stack underflow")); } fn pop_int(&mut self) -> Result<i64, String> { - Ok(self.pop()? as i64 >> 1) + return self.pop()?.to_int(); } fn push_int(&mut self, i: i64) { - self.v.push((i as u64) << 1 | 1); + self.v.push(Value::from_int(i)); } fn pop_pointer(&mut self) -> Result<Pointer, String> { - Ok(Pointer::from_bytes(self.pop()?)) + return self.pop()?.to_pointer(); } fn push_pointer(&mut self, p: Pointer) { - self.v.push(p.bytes()); + self.v.push(Value::from_pointer(p)); } fn pop_usize(&mut self) -> Result<usize, String> { - Ok(usize::try_from(self.pop()?).unwrap() >> 1) + let Value(stack_representation) = self.pop()?; + Ok(usize::try_from(stack_representation).unwrap() >> 3) } fn push_usize(&mut self, p: usize) { - self.v.push(u64::try_from(p).unwrap() << 1 | 1); + // We're going to disguise p as a pointer by shifting. + if p >= 0x2000000000000000 + /* 2^61 */ + { + panic!("pointer overflow!"); + } + self.v.push(Value(u64::try_from(p).unwrap() << 3)); } } @@ -170,41 +182,53 @@ fn fn_mod(stack: &mut Stack) -> Result<(), String> { Ok(()) } -fn alloc(stack: &mut Stack, heap: &mut Heap) -> Result<(), String> { +fn alloc(stack: &mut Stack, locals: &mut Stack, heap: &mut Heap) -> Result<(), String> { let n = stack.pop_int()?; - let n_usize = match n.try_into() { - Ok(x) => x, - Err(_) => { - return Err(String::from("invalid size")); - } - }; - stack.push_pointer(heap.alloc(n_usize)); - Ok(()) + if n < 0 { + return Err(String::from("tried to allocate negative memory")); + } + let p = heap.alloc(usize::try_from(n).unwrap(), &mut stack.v, &mut locals.v)?; + stack.push_pointer(p); + return Ok(()); } -fn peek(stack: &mut Stack, heap: &Heap, n: u8) -> Result<(), String> { +fn alloc_bytevector(stack: &mut Stack, locals: &mut Stack, heap: &mut Heap) -> Result<(), String> { + let n = stack.pop_int()?; + if n < 0 { + return Err(String::from("tried to allocate negative memory")); + } + let p = heap.alloc_bytevector(usize::try_from(n).unwrap(), &mut stack.v, &mut locals.v)?; + stack.push_pointer(p); + return Ok(()); +} + +fn peek(stack: &mut Stack, heap: &Heap) -> Result<(), String> { + let i = stack.pop_int()?; let a = stack.pop_pointer()?; - stack.v.push(heap.peek(a.offset(n))?); + stack.v.push(heap.peek(a.offset(i))?); Ok(()) } -fn poke(stack: &mut Stack, heap: &mut Heap, n: u8) -> Result<(), String> { +fn poke(stack: &mut Stack, heap: &mut Heap) -> Result<(), String> { + let i = stack.pop_int()?; let a = stack.pop_pointer()?; let u = stack.pop()?; - heap.poke(u, a.offset(n))?; + heap.poke(u, a.offset(i))?; Ok(()) } fn peek_byte(stack: &mut Stack, heap: &Heap) -> Result<(), String> { + let i = stack.pop_int()?; let a = stack.pop_pointer()?; - stack.push_int(i64::from(heap.peek_byte(a))); + stack.push_int(i64::from(heap.peek_byte(a.offset(i)))); Ok(()) } fn poke_byte(stack: &mut Stack, heap: &mut Heap) -> Result<(), String> { + let i = stack.pop_int()?; let a = stack.pop_pointer()?; let n = stack.pop_int()?; - heap.poke_byte((n & 0xff).try_into().unwrap(), a); + heap.poke_byte((n & 0xff).try_into().unwrap(), a.offset(i)); Ok(()) } @@ -395,9 +419,10 @@ pub fn eval(prog: &[Op]) -> Result<u8, String> { Op::Mul => mul(&mut stack)?, Op::Div => div(&mut stack)?, Op::Mod => fn_mod(&mut stack)?, - Op::Alloc => alloc(&mut stack, &mut heap)?, - Op::Peek(n) => peek(&mut stack, &heap, n)?, - Op::Poke(n) => poke(&mut stack, &mut heap, n)?, + Op::Alloc => alloc(&mut stack, &mut locals_stack, &mut heap)?, + Op::AllocBytevector => alloc_bytevector(&mut stack, &mut locals_stack, &mut heap)?, + Op::Peek => peek(&mut stack, &heap)?, + Op::Poke => poke(&mut stack, &mut heap)?, Op::PeekByte => peek_byte(&mut stack, &heap)?, Op::PokeByte => poke_byte(&mut stack, &mut heap)?, Op::Pop => pop(&mut stack)?, @@ -409,7 +434,7 @@ pub fn eval(prog: &[Op]) -> Result<u8, String> { Op::GetC => getc(&mut stack, &mut files)?, Op::Exit => { let n = stack.pop_int()?; - return Ok(u8::try_from(n & 0xff).unwrap()); + return Ok((n & 0xff) as u8); } } ip += 1; @@ -457,28 +482,58 @@ mod tests { } #[test] + fn eval_bytevector() { + assert_eq!( + Ok(0), + eval(&vec![Const(10), AllocBytevector, Pop, Const(0), Exit]) + ); + } + + #[test] fn eval_peek() { - assert_eq!(Ok(0), eval(&vec![Const(8), Alloc, Peek(0), Exit])); + assert_eq!( + Ok(0), + eval(&vec![Const(8), Alloc, Const(0), Peek, Const(0), Exit]) + ); } #[test] fn eval_poke() { assert_eq!( Ok(0), - eval(&vec![Const(5), Const(8), Alloc, Poke(0), Const(0), Exit]) + eval(&vec![ + Const(5), + Const(8), + Alloc, + Const(0), + Poke, + Const(0), + Exit + ]) ); } #[test] fn eval_peek_byte() { - assert_eq!(Ok(0), eval(&vec![Const(1), Alloc, PeekByte, Exit])); + assert_eq!( + Ok(0), + eval(&vec![Const(1), Alloc, Const(0), PeekByte, Exit]) + ); } #[test] fn eval_poke_byte() { assert_eq!( Ok(0), - eval(&vec![Const(5), Const(1), Alloc, PokeByte, Const(0), Exit]) + eval(&vec![ + Const(5), + Const(1), + Alloc, + Const(0), + PokeByte, + Const(0), + Exit + ]) ); } diff --git a/bytecode/src/collector.rs b/bytecode/src/collector.rs new file mode 100644 index 0000000..38a2cf9 --- /dev/null +++ b/bytecode/src/collector.rs @@ -0,0 +1 @@ +fn collect_garbage(heap: 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()); + } +} diff --git a/bytecode/src/encoding.rs b/bytecode/src/encoding.rs index b3a7983..deacae5 100644 --- a/bytecode/src/encoding.rs +++ b/bytecode/src/encoding.rs @@ -43,8 +43,8 @@ fn op_decoding<T: Read>(prog: &mut T) -> Result<Op, String> { 1050 => Op::Div, 1060 => Op::Mod, 2010 => Op::Alloc, - 2020 => Op::Peek(read_u8(prog)?), - 2030 => Op::Poke(read_u8(prog)?), + 2020 => Op::Peek, + 2030 => Op::Poke, 2040 => Op::PeekByte, 2050 => Op::PokeByte, 3010 => Op::Pop, @@ -130,16 +130,16 @@ mod tests { #[test] fn decode_peek() { assert_eq!( - Ok(vec![Op::Peek(10)]), - decode(&vec![0xe4, 0x7, 0, 0, 0, 0, 0, 0, 0xa]) + Ok(vec![Op::Peek]), + decode(&vec![0xe4, 0x7, 0, 0, 0, 0, 0, 0]) ); } #[test] fn decode_poke() { assert_eq!( - Ok(vec![Op::Poke(10)]), - decode(&vec![0xee, 0x7, 0, 0, 0, 0, 0, 0, 0xa]) + Ok(vec![Op::Poke]), + decode(&vec![0xee, 0x7, 0, 0, 0, 0, 0, 0]) ); } diff --git a/bytecode/src/heap.rs b/bytecode/src/heap.rs index d76456a..fc70a23 100644 --- a/bytecode/src/heap.rs +++ b/bytecode/src/heap.rs @@ -1,3 +1,7 @@ +use crate::data::{Pointer, Value}; +use std::collections::HashMap; +use std::iter::Iterator; + fn transmute(p: &u8) -> Result<&u64, String> { let u8_p = p as *const u8; let u64_p = u8_p as *const u64; @@ -20,43 +24,224 @@ fn transmute_mut(p: &mut u8) -> Result<&mut u64, String> { } } +fn rewrite_pointers( + stack: &mut [Value], + rewrites: &HashMap<Pointer, Pointer>, +) -> Result<(), String> { + for val in stack.iter_mut() { + let p = match val.to_pointer() { + Ok(p) => p, + Err(_) => { + continue; + } + }; + let Pointer(u) = p; + *val = Value::from_pointer( + *rewrites + .get(&p) + .ok_or(format!("no rewrite found for {:x}", u))?, + ); + } + return Ok(()); +} + pub struct Heap { heap: Vec<u8>, + free_pointer: usize, + spare_heap: Vec<u8>, } -pub struct Pointer(usize); - impl Heap { pub fn new() -> Self { Heap { - heap: vec![0; 128], // 1 kB + heap: vec![0; 1024], // 1 kB + free_pointer: 0, + spare_heap: Vec::new(), } } - pub fn alloc(&mut self, n: usize) -> Pointer { - let p = Pointer(self.heap.len()); - self.heap.append(&mut vec![0; n]); - p + fn alloc_size(&mut self, p: Pointer) -> Result<usize, String> { + let Pointer(u) = p; + // Alloc size is stored just below the pointer. + let u8_p = &self.heap[u - 8]; + let u64_p = transmute(u8_p)?; + return Ok(usize::try_from(*u64_p).unwrap() >> 1); } - pub fn peek(&self, p: Pointer) -> Result<u64, String> { + fn is_bytevector(&mut self, p: Pointer) -> Result<bool, String> { + let Pointer(u) = p; + let u8_p = &self.heap[u - 8]; + let u64_p = transmute(u8_p)?; + // Low bit 1 means bytevector. + return Ok(*u64_p & 1 != 0); + } + + fn gc_process_value( + &mut self, + val: Value, + spare_heap_ptr: &mut usize, + rewrites: &mut HashMap<Pointer, Pointer>, + ) -> Result<(), String> { + let p = match val.to_pointer() { + Ok(p) => p, + Err(_) => { + return Ok(()); + } + }; + if rewrites.contains_key(&p) { + // Already copied this one. + return Ok(()); + } + let Pointer(u) = p; + let object_size = self.alloc_size(p)?; + // Copy object and size. + self.spare_heap[*spare_heap_ptr..*spare_heap_ptr + object_size + 8] + .copy_from_slice(&self.heap[u - 8..u + object_size]); + rewrites.insert(p, Pointer(*spare_heap_ptr + 8)); + *spare_heap_ptr += object_size + 8; + if self.is_bytevector(p)? { + // Don't process bytevectors recursively. We're all done. + return Ok(()); + } + for i in (u..u + object_size).step_by(8) { + self.gc_process_value(self.peek(Pointer(i))?, spare_heap_ptr, rewrites)?; + } + return Ok(()); + } + + fn walk_gc_roots( + &mut self, + roots: &[Value], + spare_heap_ptr: &mut usize, + rewrites: &mut HashMap<Pointer, Pointer>, + ) -> Result<(), String> { + for &val in roots { + self.gc_process_value(val, spare_heap_ptr, rewrites)?; + } + return Ok(()); + } + + fn collect_garbage( + &mut self, + size_hint: usize, + stack: &mut [Value], + locals: &mut [Value], + ) -> Result<(), String> { + const MAX_HEAP_SIZE: usize = 4 * 1024 * 1024; // 4 GB + // Always at least double the heap size (keeping in mind the max heap size). + let mut size_hint = size_hint; + if size_hint < self.heap.len() { + size_hint = self.heap.len(); + } + let mut new_heap_size = self.heap.len() + size_hint; + if new_heap_size > MAX_HEAP_SIZE / 2 { + new_heap_size = MAX_HEAP_SIZE / 2; + } + self.spare_heap.resize(new_heap_size, 0); + let mut spare_heap_ptr = 0; + let mut rewrites = HashMap::new(); + self.walk_gc_roots(stack, &mut spare_heap_ptr, &mut rewrites)?; + self.walk_gc_roots(locals, &mut spare_heap_ptr, &mut rewrites)?; + // Walk the stacks and rewrite. + rewrite_pointers(stack, &rewrites)?; + rewrite_pointers(locals, &rewrites)?; + // Activate the new heap! + std::mem::swap(&mut self.heap, &mut self.spare_heap); + self.free_pointer = spare_heap_ptr; + // Walk objects in the heap and rewrite pointers. First object is at address 8. + let mut i = 8; + while i < self.free_pointer { + let p = Pointer(i); + if self.is_bytevector(p)? { + i += self.alloc_size(p)?; + continue; + } + for j in (i..i + self.alloc_size(p)?).step_by(8) { + let q = Pointer(j); + let val = self.peek(q)?; + let vp = match val.to_pointer() { + Ok(x) => x, + Err(_) => { + continue; + } + }; + let Pointer(u) = vp; + self.poke( + Value::from_pointer( + *rewrites + .get(&vp) + .ok_or(format!("no rewrite found for {:x}", u))?, + ), + q, + )?; + } + } + // Done?? + return Ok(()); + } + + fn alloc_b( + &mut self, + n: usize, + stack: &mut [Value], + locals: &mut [Value], + bytevector_p: bool, + ) -> Result<Pointer, String> { + if self.heap.len() - self.free_pointer < n { + self.collect_garbage(n, stack, locals)?; + if self.heap.len() - self.free_pointer < n { + return Err(String::from("out of space")); + } + } + let len_p = transmute_mut(&mut self.heap[self.free_pointer])?; + *len_p = u64::try_from(n).unwrap() << 1; + if bytevector_p { + *len_p |= 1; + } + self.free_pointer += 8; + let p = Pointer(self.free_pointer); + self.heap[self.free_pointer..self.free_pointer + n].fill(0); + self.free_pointer += n; + return Ok(p); + } + + pub fn alloc( + &mut self, + n: usize, + stack: &mut [Value], + locals: &mut [Value], + ) -> Result<Pointer, String> { + return self.alloc_b(n, stack, locals, false); + } + + pub fn alloc_bytevector( + &mut self, + n: usize, + stack: &mut [Value], + locals: &mut [Value], + ) -> Result<Pointer, String> { + return self.alloc_b(n, stack, locals, true); + } + + pub fn peek(&self, p: Pointer) -> Result<Value, String> { let Pointer(x) = p; let u8_p = &self.heap[x]; let u64_p = transmute(u8_p)?; - Ok(*u64_p) + return Ok(Value(*u64_p)); } - pub fn poke(&mut self, u: u64, p: Pointer) -> Result<(), String> { + pub fn poke(&mut self, v: Value, p: Pointer) -> Result<(), String> { let Pointer(x) = p; + let Value(u) = v; let u8_p = &mut self.heap[x]; let u64_p = transmute_mut(u8_p)?; *u64_p = u; - Ok(()) + return Ok(()); } pub fn peek_byte(&self, p: Pointer) -> u8 { let Pointer(x) = p; - self.heap[x] + return self.heap[x]; } pub fn poke_byte(&mut self, u: u8, p: Pointer) { @@ -64,19 +249,3 @@ impl Heap { self.heap[x] = u; } } - -impl Pointer { - pub fn from_bytes(p: u64) -> Self { - Pointer(usize::try_from(p).unwrap() >> 1) - } - - pub fn bytes(&self) -> u64 { - let Pointer(x) = self; - u64::try_from(*x).unwrap() << 1 - } - - pub fn offset(&self, off: u8) -> Pointer { - let Pointer(x) = self; - Pointer(*x + usize::from(off)) - } -} diff --git a/bytecode/src/main.rs b/bytecode/src/main.rs index b5a05ef..683ff26 100644 --- a/bytecode/src/main.rs +++ b/bytecode/src/main.rs @@ -1,4 +1,5 @@ mod bytecode; +mod data; mod encoding; mod heap; diff --git a/bytecode/src/stack.rs b/bytecode/src/stack.rs new file mode 100644 index 0000000..66d3f7c --- /dev/null +++ b/bytecode/src/stack.rs @@ -0,0 +1,33 @@ +struct Stack { + v: Vec<u64>, +} + +impl Stack { + fn pop(&mut self) -> Result<u64, String> { + self.v.pop().ok_or(String::from("stack underflow")) + } + + fn pop_int(&mut self) -> Result<i64, String> { + Ok(self.pop()? as i64 >> 1) + } + + fn push_int(&mut self, i: i64) { + self.v.push((i as u64) << 1 | 1); + } + + fn pop_pointer(&mut self) -> Result<Pointer, String> { + Ok(Pointer::from_bytes(self.pop()?)) + } + + fn push_pointer(&mut self, p: Pointer) { + self.v.push(p.bytes()); + } + + fn pop_usize(&mut self) -> Result<usize, String> { + Ok(usize::try_from(self.pop()?).unwrap() >> 1) + } + + fn push_usize(&mut self, p: usize) { + self.v.push(u64::try_from(p).unwrap() << 1 | 1); + } +} |
