aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bytecode/src/bytecode.rs141
-rw-r--r--bytecode/src/collector.rs1
-rw-r--r--bytecode/src/data.rs225
-rw-r--r--bytecode/src/encoding.rs12
-rw-r--r--bytecode/src/heap.rs225
-rw-r--r--bytecode/src/main.rs1
-rw-r--r--bytecode/src/stack.rs33
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);
+ }
+}