aboutsummaryrefslogtreecommitdiffstats
path: root/bytecode/src/bytecode.rs
diff options
context:
space:
mode:
authorRose Hogenson <rhogenson@posteo.net>2022-07-29 22:41:17 -0700
committerRose Hogenson <rhogenson@posteo.net>2022-07-29 22:41:17 -0700
commitc7ff0b98146693b60a3f075818f96764f646b2d5 (patch)
treeb8d2f409a934ae8c3858a30f42d1799b8abee5da /bytecode/src/bytecode.rs
parent762431b0f0a6ead18a93a05c0f3269dca5b8fef1 (diff)
downloadchromatopelma-c7ff0b98146693b60a3f075818f96764f646b2d5.tar.zst
Fix the bytecode interpreter.
I'm too scared to actually try to run it right now.
Diffstat (limited to 'bytecode/src/bytecode.rs')
-rw-r--r--bytecode/src/bytecode.rs721
1 files changed, 276 insertions, 445 deletions
diff --git a/bytecode/src/bytecode.rs b/bytecode/src/bytecode.rs
index 84a9b8b..df07485 100644
--- a/bytecode/src/bytecode.rs
+++ b/bytecode/src/bytecode.rs
@@ -1,598 +1,429 @@
-use crate::data::{Pointer, Value};
+use crate::data::Value;
use crate::heap::Heap;
-use std::io::{BufWriter, Read, StdinLock, Write};
-// The cute scheme virtual machine has two stacks:
-// - the data stack and
-// - the locals stack.
-// There are four pointers:
-// - the instruction pointer,
-// - the argument pointer,
-// - and the (data) stack pointer.
-// These pointers cannot be manipulated directly, but are referred to in
-// the comments below on the opcodes.
+// The cute scheme virtual machine is a register based VM.
+// There are 256 registers, also called locals.
-#[derive(Debug, Eq, PartialEq)]
+#[derive(Debug, Eq, PartialEq, Clone, Copy)]
+pub struct Local(pub u8);
+
+#[derive(Debug, Eq, PartialEq, Clone, Copy)]
+pub enum Arg {
+ L(Local),
+ Const(Value),
+}
+
+#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum Op {
// Signed arithmetic
// =================
- // ( -- n )
- // Pushes a constant.
- Const(i64),
- // ( n1 n2 -- n3 )
// Adds two integers.
- Add,
- // ( n1 n2 -- n3 )
+ Add(Local, Arg, Arg),
// Subtracts two integers.
- Sub,
- // ( n1 n2 -- n3 )
+ Sub(Local, Arg, Arg),
// Multiplies two integers.
- Mul,
- // ( n1 n2 -- n3 )
+ Mul(Local, Arg, Arg),
// Divides two integers and truncates the result.
- Div,
- // ( n1 n2 -- n3 )
+ Div(Local, Arg, Arg),
// Remainder from Div.
- Mod,
+ Mod(Local, Arg, Arg),
// Heap
// ======
- // ( n -- a )
// Allocates n words and returns the address.
- Alloc,
- // ( n -- a )
+ Alloc(Local, Arg),
// 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 )
+ AllocBytevector(Local, Arg),
// Fetches a 64 bit word from the specified address plus the
// given offset.
- Peek,
- // ( u a i )
+ Peek(Local, Arg, Arg),
// Stores a 64 bit word at the specified address plus the
// given offset.
- Poke,
- // ( a i -- n )
+ Poke(Arg, Arg, Arg),
// Fetches a byte from the specified address plus the given offset.
- PeekByte,
- // ( n a i )
+ PeekByte(Local, Arg, Arg),
// Stores a byte at the specified address plus the given offset.
- PokeByte,
+ PokeByte(Arg, Arg, Arg),
- // Stack
- // =====
+ // Locals
+ // ======
- // ( n -- )
- // Deletes an element from the stack.
- Pop,
- // ( -- n )
- // Copies an argument from the locals stack to the data stack. The argument is an index above the argument pointer.
- Local(u8),
+ // Loads a value into a register.
+ Mov(Local, Arg),
// Control flow
// ============
- // ( f -- )
- // 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 )
- // Pushes the instruction pointer, moves n arguments to the locals
- // stack, pushes the old argument pointer, and jumps to the
- // specified location.
- Call(u8),
- // ( Locals stack: instruction-ptr a1 a2 ... an argument-ptr )
- // Pops the argument pointer (effectively popping n more arguments which were added by Call),
- // and then pops the instruction pointer.
- Ret,
- // ( n )
+ // Jumps to the specified address.
+ Jmp(Arg),
+ // Jumps to the specified address if the argument is not false. Note: the first argument is the
+ // condition and the second argument is the address.
+ JmpIf(Arg, Arg),
// Terminates the interpreter with the given status code.
- Exit,
-
- // Input and output
- // ================
-
- // ( b file -- f )
- // Writes a byte to a file. Returns true on success, false on error.
- PutC,
- // ( file -- b )
- // Reads one byte from a file. Returns -1 on EOF, 0 on error.
- GetC,
+ Exit(Arg),
}
-struct Stack {
- v: Vec<Value>,
+struct Interpreter {
+ locals: Vec<Value>,
+ heap: Heap,
}
-impl Stack {
- 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> {
- return self.pop()?.to_int();
- }
-
- fn push_int(&mut self, i: i64) {
- self.v.push(Value::from_int(i));
+impl Interpreter {
+ fn read_arg(&self, x: Arg) -> Value {
+ match x {
+ Arg::L(Local(i)) => self.locals[usize::from(i)],
+ Arg::Const(c) => c,
+ }
}
- fn pop_pointer(&mut self) -> Result<Pointer, String> {
- return self.pop()?.to_pointer();
+ fn set_arg(&mut self, dest: Local, val: Value) {
+ let Local(i) = dest;
+ self.locals[usize::from(i)] = val;
}
- fn push_pointer(&mut self, p: Pointer) {
- self.v.push(Value::from_pointer(p));
+ fn add(&mut self, dest: Local, x: Arg, y: Arg) -> Result<(), String> {
+ let n1 = self.read_arg(x).to_int()?;
+ let n2 = self.read_arg(y).to_int()?;
+ self.set_arg(dest, Value::from_int(n1.wrapping_add(n2)));
+ Ok(())
}
- fn pop_usize(&mut self) -> Result<usize, String> {
- let Value(stack_representation) = self.pop()?;
- Ok(usize::try_from(stack_representation).unwrap() >> 3)
+ fn sub(&mut self, dest: Local, x: Arg, y: Arg) -> Result<(), String> {
+ let n1 = self.read_arg(x).to_int()?;
+ let n2 = self.read_arg(y).to_int()?;
+ self.set_arg(dest, Value::from_int(n1.wrapping_sub(n2)));
+ Ok(())
}
- fn push_usize(&mut self, p: usize) {
- // 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));
+ fn mul(&mut self, dest: Local, x: Arg, y: Arg) -> Result<(), String> {
+ let n1 = self.read_arg(x).to_int()?;
+ let n2 = self.read_arg(y).to_int()?;
+ self.set_arg(dest, Value::from_int(n1.wrapping_mul(n2)));
+ Ok(())
}
-}
-
-fn fn_const(stack: &mut Stack, n: i64) {
- stack.push_int(n);
-}
-
-fn add(stack: &mut Stack) -> Result<(), String> {
- let n2 = stack.pop_int()?;
- let n1 = stack.pop_int()?;
- stack.push_int(n1.wrapping_add(n2));
- Ok(())
-}
-fn sub(stack: &mut Stack) -> Result<(), String> {
- let n2 = stack.pop_int()?;
- let n1 = stack.pop_int()?;
- stack.push_int(n1.wrapping_sub(n2));
- Ok(())
-}
-
-fn mul(stack: &mut Stack) -> Result<(), String> {
- let n2 = stack.pop_int()?;
- let n1 = stack.pop_int()?;
- stack.push_int(n1.wrapping_mul(n2));
- Ok(())
-}
-
-fn div(stack: &mut Stack) -> Result<(), String> {
- let n2 = stack.pop_int()?;
- let n1 = stack.pop_int()?;
- stack.push_int(n1.wrapping_div(n2));
- Ok(())
-}
-
-fn fn_mod(stack: &mut Stack) -> Result<(), String> {
- let n2 = stack.pop_int()?;
- let n1 = stack.pop_int()?;
- stack.push_int(n1.wrapping_rem(n2));
- Ok(())
-}
-
-fn alloc(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"));
+ fn div(&mut self, dest: Local, x: Arg, y: Arg) -> Result<(), String> {
+ let n1 = self.read_arg(x).to_int()?;
+ let n2 = self.read_arg(y).to_int()?;
+ self.set_arg(dest, Value::from_int(n1.wrapping_div(n2)));
+ Ok(())
}
- let p = heap.alloc(usize::try_from(n).unwrap(), &mut stack.v, &mut locals.v)?;
- stack.push_pointer(p);
- return Ok(());
-}
-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"));
+ fn fn_mod(&mut self, dest: Local, x: Arg, y: Arg) -> Result<(), String> {
+ let n1 = self.read_arg(x).to_int()?;
+ let n2 = self.read_arg(y).to_int()?;
+ self.set_arg(dest, Value::from_int(n1.wrapping_rem(n2)));
+ Ok(())
}
- 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(i))?);
- Ok(())
-}
-
-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(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.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.offset(i));
- Ok(())
-}
-
-fn pop(stack: &mut Stack) -> Result<(), String> {
- stack.pop()?;
- Ok(())
-}
-
-fn local(stack: &mut Stack, locals: &Stack, n: u8) -> Result<(), String> {
- let i = locals
- .v
- .len()
- .checked_sub(usize::from(n) + 1)
- .ok_or("out of bounds")?;
- stack.v.push(locals.v[i]);
- Ok(())
-}
-fn fn_if(stack: &mut Stack, ip: &mut usize, n: i64) -> Result<(), String> {
- let f = stack.pop_int()?;
- if f != 0 {
- if n > 0 {
- *ip = ip
- .checked_add(n.try_into().unwrap())
- .ok_or("invalid offset")?;
- } else {
- *ip = ip
- .checked_sub((-n).try_into().unwrap())
- .ok_or("invalid offset")?;
+ fn alloc(&mut self, dest: Local, size: Arg) -> Result<(), String> {
+ let n = self.read_arg(size).to_int()?;
+ if n < 0 {
+ return Err(String::from("tried to allocate negative memory"));
}
+ let p = self
+ .heap
+ .alloc(usize::try_from(n).unwrap(), &mut self.locals)?;
+ self.set_arg(dest, Value::from_pointer(p));
+ Ok(())
}
- Ok(())
-}
-
-fn call(stack: &mut Stack, locals: &mut Stack, ip: &mut usize, n: u8) -> Result<(), String> {
- // Push the instruction pointer.
- locals.push_usize(*ip);
- let old_ap = locals.v.len();
- // Move n arguments to the locals stack.
- let a1_idx = stack.v.len() - usize::from(n);
- for i in a1_idx..stack.v.len() {
- locals.v.push(stack.v[i]);
- }
- stack.v.truncate(a1_idx);
- // Push the old argument pointer.
- locals.push_usize(old_ap);
- // Jump to the specified location.
- let i_addr = stack.pop_int()?;
- let i_addr_usize = match usize::try_from(i_addr) {
- Ok(x) => x,
- Err(_) => {
- return Err(String::from("invalid address"));
- }
- };
- // Subtract 1 because the interpreter will also increment the
- // instruction pointer.
- *ip = i_addr_usize - 1;
- Ok(())
-}
-
-fn ret(locals: &mut Stack, ip: &mut usize) -> Result<(), String> {
- let ap = locals.pop_usize()?;
- locals.v.truncate(ap);
- let return_address = locals.pop_usize()?;
- *ip = return_address;
- Ok(())
-}
-
-trait File: Write + Read {}
-struct FileTable<'a> {
- files: Vec<Box<dyn File + 'a>>,
-}
-
-impl<'a> FileTable<'a> {
- fn file(&mut self, f: i64) -> Result<&mut (dyn File + 'a), String> {
- if !(0 <= f && usize::try_from(f).unwrap() < self.files.len()) {
- return Err(String::from("invalid file"));
+ fn alloc_bytevector(&mut self, dest: Local, size: Arg) -> Result<(), String> {
+ let n = self.read_arg(size).to_int()?;
+ if n < 0 {
+ return Err(String::from("tried to allocate negative memory"));
}
- Ok(&mut *self.files[usize::try_from(f).unwrap()])
+ let p = self
+ .heap
+ .alloc_bytevector(usize::try_from(n).unwrap(), &mut self.locals)?;
+ self.set_arg(dest, Value::from_pointer(p));
+ Ok(())
}
-}
-
-struct Stdin<'a>(StdinLock<'a>);
-impl<'a> Write for Stdin<'a> {
- fn write(&mut self, _: &[u8]) -> std::io::Result<usize> {
- Err(std::io::Error::new(
- std::io::ErrorKind::InvalidInput,
- "can't write to stdin",
- ))
+ fn peek(&mut self, dest: Local, ptr: Arg, offset: Arg) -> Result<(), String> {
+ let p = self.read_arg(ptr).to_pointer()?;
+ let o = self.read_arg(offset).to_int()?;
+ if o < 0 {
+ return Err(String::from("pointer offset can't be negative"));
+ }
+ let result = self.heap.peek(p.offset(usize::try_from(o).unwrap()))?;
+ self.set_arg(dest, result);
+ Ok(())
}
- fn flush(&mut self) -> std::io::Result<()> {
+ fn poke(&mut self, word: Arg, ptr: Arg, offset: Arg) -> Result<(), String> {
+ let w = self.read_arg(word);
+ let p = self.read_arg(ptr).to_pointer()?;
+ let o = self.read_arg(offset).to_int()?;
+ if o < 0 {
+ return Err(String::from("pointer offset can't be negative"));
+ }
+ self.heap.poke(w, p.offset(usize::try_from(o).unwrap()))?;
Ok(())
}
-}
-impl<'a> Read for Stdin<'a> {
- fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
- let Stdin(ref mut handle) = self;
- handle.read(buf)
+ fn peek_byte(&mut self, dest: Local, ptr: Arg, offset: Arg) -> Result<(), String> {
+ let p = self.read_arg(ptr).to_pointer()?;
+ let o = self.read_arg(offset).to_int()?;
+ if o < 0 {
+ return Err(String::from("pointer offset can't be negative"));
+ }
+ let result = self.heap.peek_byte(p.offset(usize::try_from(o).unwrap()))?;
+ self.set_arg(dest, Value::from_int(i64::from(result)));
+ Ok(())
}
-}
-
-impl<'a> File for Stdin<'a> {}
-struct Out<T>(T);
-
-impl<T: Write> Write for Out<T> {
- fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
- let Out(ref mut handle) = self;
- handle.write(buf)
+ fn poke_byte(&mut self, word: Arg, ptr: Arg, offset: Arg) -> Result<(), String> {
+ let w = self.read_arg(word).to_int()?;
+ let p = self.read_arg(ptr).to_pointer()?;
+ let o = self.read_arg(offset).to_int()?;
+ if o < 0 {
+ return Err(String::from("pointer offset can't be negative"));
+ }
+ if !(0 <= w && w <= 0xff) {
+ return Err(format!("value {} is not byte-sized", w));
+ }
+ self.heap.poke_byte(
+ u8::try_from(w).unwrap(),
+ p.offset(usize::try_from(o).unwrap()),
+ )?;
+ Ok(())
}
- fn flush(&mut self) -> std::io::Result<()> {
- let Out(ref mut handle) = self;
- handle.flush()
+ fn mov(&mut self, dest: Local, src: Arg) {
+ self.set_arg(dest, self.read_arg(src));
}
-}
-impl<T> Read for Out<T> {
- fn read(&mut self, _: &mut [u8]) -> std::io::Result<usize> {
- Err(std::io::Error::new(
- std::io::ErrorKind::InvalidInput,
- "invalid file for reading",
- ))
+ fn jmp(&mut self, ip: &mut usize, addr: Arg) -> Result<(), String> {
+ let a = self.read_arg(addr).to_int()?;
+ if a < 0 {
+ return Err(String::from("can't jump to a negative address"));
+ }
+ *ip = usize::try_from(a).unwrap();
+ Ok(())
}
-}
-
-impl<T: Write> File for Out<T> {}
-fn putc(stack: &mut Stack, files: &mut FileTable) -> Result<(), String> {
- let file = stack.pop_int()?;
- let byte = stack.pop_int()?;
- match files
- .file(file)?
- .write(&vec![(byte & 0xff).try_into().unwrap()])
- {
- Ok(_) => {
- stack.push_int(-1);
+ fn jmp_if(&mut self, ip: &mut usize, cond: Arg, addr: Arg) -> Result<(), String> {
+ let c = self.read_arg(cond);
+ let a = self.read_arg(addr).to_int()?;
+ if a < 0 {
+ return Err(String::from("can't jump to a negative address"));
}
- Err(_) => {
- stack.push_int(0);
+ if c != Value::from_bool(false) {
+ *ip = usize::try_from(a).unwrap();
}
+ Ok(())
}
- Ok(())
-}
-fn getc(stack: &mut Stack, files: &mut FileTable) -> Result<(), String> {
- let file = stack.pop_int()?;
- let mut buf = vec![0];
- match files.file(file)?.read(&mut buf) {
- Ok(1) => {
- stack.push_int(buf[0].into());
- }
- Ok(0) => {
- stack.push_int(-1);
- }
- _ => {
- stack.push_int(0);
+ fn eval(&mut self, prog: &[Op]) -> Result<u8, String> {
+ let mut ip = 0;
+ loop {
+ if ip >= prog.len() {
+ return Err(String::from("invalid instruction pointer"));
+ }
+ let op = prog[ip];
+ ip += 1;
+ match op {
+ Op::Add(dest, x, y) => self.add(dest, x, y)?,
+ Op::Sub(dest, x, y) => self.sub(dest, x, y)?,
+ Op::Mul(dest, x, y) => self.mul(dest, x, y)?,
+ Op::Div(dest, x, y) => self.div(dest, x, y)?,
+ Op::Mod(dest, x, y) => self.fn_mod(dest, x, y)?,
+ Op::Alloc(dest, size) => self.alloc(dest, size)?,
+ Op::AllocBytevector(dest, size) => self.alloc_bytevector(dest, size)?,
+ Op::Peek(dest, ptr, offset) => self.peek(dest, ptr, offset)?,
+ Op::Poke(word, ptr, offset) => self.poke(word, ptr, offset)?,
+ Op::PeekByte(dest, ptr, offset) => self.peek_byte(dest, ptr, offset)?,
+ Op::PokeByte(word, ptr, offset) => self.poke_byte(word, ptr, offset)?,
+ Op::Mov(dest, src) => self.mov(dest, src),
+ Op::Jmp(addr) => self.jmp(&mut ip, addr)?,
+ Op::JmpIf(cond, addr) => self.jmp_if(&mut ip, cond, addr)?,
+ Op::Exit(code) => {
+ let n = self.read_arg(code).to_int()?;
+ return Ok((n & 0xff) as u8);
+ }
+ }
}
}
- Ok(())
}
pub fn eval(prog: &[Op]) -> Result<u8, String> {
- let mut stack = Stack { v: Vec::new() };
- let mut locals_stack = Stack { v: Vec::new() };
- let mut heap = Heap::new();
- let mut ip = 0;
- let stdin = std::io::stdin();
- let mut files = FileTable {
- files: vec![
- Box::new(Stdin(stdin.lock())),
- Box::new(Out(BufWriter::new(std::io::stdout()))),
- Box::new(Out(BufWriter::new(std::io::stderr()))),
- ],
+ let mut interpreter = Interpreter {
+ locals: vec![Value(0); 256],
+ heap: Heap::new(),
};
- loop {
- if ip >= prog.len() {
- return Err(String::from("invalid instruction pointer"));
- }
- match prog[ip] {
- Op::Const(n) => fn_const(&mut stack, n),
- Op::Add => add(&mut stack)?,
- Op::Sub => sub(&mut stack)?,
- Op::Mul => mul(&mut stack)?,
- Op::Div => div(&mut stack)?,
- Op::Mod => fn_mod(&mut stack)?,
- 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)?,
- Op::Local(n) => local(&mut stack, &locals_stack, n)?,
- Op::If(n) => fn_if(&mut stack, &mut ip, n)?,
- Op::Call(n) => call(&mut stack, &mut locals_stack, &mut ip, n)?,
- Op::Ret => ret(&mut locals_stack, &mut ip)?,
- Op::PutC => putc(&mut stack, &mut files)?,
- Op::GetC => getc(&mut stack, &mut files)?,
- Op::Exit => {
- let n = stack.pop_int()?;
- return Ok((n & 0xff) as u8);
- }
- }
- ip += 1;
- }
+ interpreter.eval(prog)
}
#[cfg(test)]
mod tests {
+ use super::Arg::*;
use super::Op::*;
use super::*;
#[test]
fn eval_const() {
- assert_eq!(Ok(5), eval(&vec![Const(5), Exit]));
+ assert_eq!(Ok(5), eval(&vec![Exit(Const(Value::from_int(5)))]));
}
#[test]
fn eval_add() {
- assert_eq!(Ok(10), eval(&vec![Const(5), Const(5), Add, Exit]));
+ assert_eq!(
+ Ok(10),
+ eval(&vec![
+ Add(
+ Local(0),
+ Const(Value::from_int(5)),
+ Const(Value::from_int(5))
+ ),
+ Exit(L(Local(0))),
+ ])
+ );
}
#[test]
fn eval_sub() {
- assert_eq!(Ok(2), eval(&vec![Const(5), Const(3), Sub, Exit]));
+ assert_eq!(
+ Ok(2),
+ eval(&vec![
+ Sub(
+ Local(0),
+ Const(Value::from_int(5)),
+ Const(Value::from_int(3))
+ ),
+ Exit(L(Local(0))),
+ ])
+ );
}
#[test]
fn eval_mul() {
- assert_eq!(Ok(25), eval(&vec![Const(5), Const(5), Mul, Exit]));
- }
-
- #[test]
- fn eval_div() {
- assert_eq!(Ok(2), eval(&vec![Const(5), Const(2), Div, Exit]));
- }
-
- #[test]
- fn eval_mod() {
- assert_eq!(Ok(1), eval(&vec![Const(5), Const(2), Mod, Exit]));
- }
-
- #[test]
- fn eval_alloc() {
- assert_eq!(Ok(0), eval(&vec![Const(10), Alloc, Pop, Const(0), Exit]));
- }
-
- #[test]
- fn eval_bytevector() {
assert_eq!(
- Ok(0),
- eval(&vec![Const(10), AllocBytevector, Pop, Const(0), Exit])
+ Ok(25),
+ eval(&vec![
+ Mul(
+ Local(0),
+ Const(Value::from_int(5)),
+ Const(Value::from_int(5))
+ ),
+ Exit(L(Local(0))),
+ ])
);
}
#[test]
- fn eval_peek() {
+ fn eval_div() {
assert_eq!(
- Ok(0),
- eval(&vec![Const(8), Alloc, Const(0), Peek, Const(0), Exit])
+ Ok(2),
+ eval(&vec![
+ Div(
+ Local(0),
+ Const(Value::from_int(5)),
+ Const(Value::from_int(2))
+ ),
+ Exit(L(Local(0))),
+ ])
);
}
#[test]
- fn eval_poke() {
+ fn eval_mod() {
assert_eq!(
- Ok(0),
+ Ok(1),
eval(&vec![
- Const(5),
- Const(8),
- Alloc,
- Const(0),
- Poke,
- Const(0),
- Exit
+ Mod(
+ Local(0),
+ Const(Value::from_int(5)),
+ Const(Value::from_int(2))
+ ),
+ Exit(L(Local(0))),
])
);
}
#[test]
- fn eval_peek_byte() {
+ fn eval_alloc() {
assert_eq!(
Ok(0),
- eval(&vec![Const(1), Alloc, Const(0), PeekByte, Exit])
+ eval(&vec![
+ Alloc(Local(0), Const(Value::from_int(10))),
+ Exit(Const(Value::from_int(0))),
+ ])
);
}
#[test]
- fn eval_poke_byte() {
+ fn eval_bytevector() {
assert_eq!(
Ok(0),
eval(&vec![
- Const(5),
- Const(1),
- Alloc,
- Const(0),
- PokeByte,
- Const(0),
- Exit
+ AllocBytevector(Local(0), Const(Value::from_int(10))),
+ Exit(Const(Value::from_int(0))),
])
);
}
#[test]
- fn eval_pop() {
- assert_eq!(Ok(5), eval(&vec![Const(5), Const(10), Pop, Exit]));
- }
-
- #[test]
- fn eval_if_true() {
+ fn eval_peek() {
assert_eq!(
Ok(5),
- eval(&vec![Const(5), Const(1), If(2), Const(5), Add, Exit])
- );
- }
-
- #[test]
- fn eval_if_false() {
- assert_eq!(
- Ok(10),
- eval(&vec![Const(5), Const(0), If(2), Const(5), Add, Exit])
+ eval(&vec![
+ Alloc(Local(0), Const(Value::from_int(1))),
+ Poke(
+ Const(Value::from_int(5)),
+ L(Local(0)),
+ Const(Value::from_int(0))
+ ),
+ Peek(Local(1), L(Local(0)), Const(Value::from_int(0))),
+ Exit(L(Local(1))),
+ ])
);
}
#[test]
- fn eval_call() {
+ fn eval_poke() {
assert_eq!(
- Ok(5),
- eval(&vec![Const(3), Const(5), Call(1), Local(1), Exit])
+ Ok(0),
+ eval(&vec![
+ Alloc(Local(0), Const(Value::from_int(1))),
+ Poke(
+ Const(Value::from_int(5)),
+ L(Local(0)),
+ Const(Value::from_int(0))
+ ),
+ Exit(Const(Value::from_int(0))),
+ ])
);
}
#[test]
- fn eval_ret() {
+ fn eval_peek_byte() {
assert_eq!(
- Ok(5),
- eval(&vec![Const(4), Const(5), Call(1), Exit, Local(1), Ret])
+ Ok(0),
+ eval(&vec![
+ AllocBytevector(Local(0), Const(Value::from_int(1))),
+ PeekByte(Local(1), L(Local(0)), Const(Value::from_int(0))),
+ Exit(L(Local(1))),
+ ])
);
}
#[test]
- fn eval_putc() {
+ fn eval_poke_byte() {
assert_eq!(
- Ok(5),
+ Ok(0),
eval(&vec![
- Const(5),
- Const(88),
- Const(1),
- PutC,
- If(2),
- Const(5),
- Add,
- Exit
+ AllocBytevector(Local(0), Const(Value::from_int(1))),
+ PokeByte(
+ Const(Value::from_int(5)),
+ L(Local(0)),
+ Const(Value::from_int(0))
+ ),
+ Exit(Const(Value::from_int(0))),
])
);
}
-
- #[test]
- fn eval_getc_err() {
- assert_eq!(Ok(0), eval(&vec![Const(1), GetC, Exit]));
- }
}