diff options
| author | Rose Hogenson <rhogenson@posteo.net> | 2022-07-29 22:41:17 -0700 |
|---|---|---|
| committer | Rose Hogenson <rhogenson@posteo.net> | 2022-07-29 22:41:17 -0700 |
| commit | c7ff0b98146693b60a3f075818f96764f646b2d5 (patch) | |
| tree | b8d2f409a934ae8c3858a30f42d1799b8abee5da /bytecode/src/bytecode.rs | |
| parent | 762431b0f0a6ead18a93a05c0f3269dca5b8fef1 (diff) | |
| download | chromatopelma-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.rs | 721 |
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])); - } } |
