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/encoding.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/encoding.rs')
| -rw-r--r-- | bytecode/src/encoding.rs | 279 |
1 files changed, 112 insertions, 167 deletions
diff --git a/bytecode/src/encoding.rs b/bytecode/src/encoding.rs index deacae5..8f49ab6 100644 --- a/bytecode/src/encoding.rs +++ b/bytecode/src/encoding.rs @@ -1,225 +1,170 @@ -use crate::bytecode::Op; +// This file must stay in sync with encoding.csc. +use crate::bytecode::{Arg, Local, Op}; +use crate::data::Value; +use std::io; use std::io::Read; -fn read_tag<T: Read>(prog: &mut T) -> Result<u64, String> { +fn read_const<T: Read>(prog: &mut T) -> Result<Value, String> { let mut buf = [0; 8]; match prog.read_exact(&mut buf) { Ok(()) => (), - Err(_) => { - return Err(String::from("error reading input")); + Err(e) => { + return Err(format!("error reading input: {}", e)); } }; - Ok(u64::from_le_bytes(buf)) + Ok(Value(u64::from_le_bytes(buf))) } -fn read_i64<T: Read>(prog: &mut T) -> Result<i64, String> { - let mut buf = [0; 8]; +fn read_local<T: Read>(prog: &mut T) -> Result<Local, String> { + let mut buf = [0; 1]; match prog.read_exact(&mut buf) { Ok(()) => (), - Err(_) => { - return Err(String::from("error reading input")); + Err(e) => { + return Err(format!("error reading input: {}", e)); } }; - Ok(i64::from_le_bytes(buf)) + Ok(Local(buf[0])) } -fn read_u8<T: Read>(prog: &mut T) -> Result<u8, String> { - let mut buf = vec![0]; +fn read_arg<T: Read>(prog: &mut T, is_const: bool) -> Result<Arg, String> { + if is_const { + let c = read_const(prog)?; + return Ok(Arg::Const(c)); + } + let l = read_local(prog)?; + Ok(Arg::L(l)) +} + +fn op_decoding<T: Read>(prog: &mut T) -> Result<Option<Op>, String> { + let mut buf = [0; 1]; match prog.read_exact(&mut buf) { Ok(()) => (), - Err(_) => { - return Err(String::from("error reading input")); + Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => { + return Ok(None); + } + Err(e) => { + return Err(format!("error reading input: {}", e)); } }; - Ok(buf[0]) -} - -fn op_decoding<T: Read>(prog: &mut T) -> Result<Op, String> { - let op = match read_tag(prog)? { - 1010 => Op::Const(read_i64(prog)?), - 1020 => Op::Add, - 1030 => Op::Sub, - 1040 => Op::Mul, - 1050 => Op::Div, - 1060 => Op::Mod, - 2010 => Op::Alloc, - 2020 => Op::Peek, - 2030 => Op::Poke, - 2040 => Op::PeekByte, - 2050 => Op::PokeByte, - 3010 => Op::Pop, - 3020 => Op::Local(read_u8(prog)?), - 4010 => Op::If(read_i64(prog)?), - 4020 => Op::Call(read_u8(prog)?), - 4030 => Op::Ret, - 4040 => Op::Exit, - 5010 => Op::PutC, - 5020 => Op::GetC, + let tag = buf[0] >> 2; + let arg1_const = buf[0] & 2 != 0; + let arg2_const = buf[0] & 1 != 0; + let op = match tag { + // Note: arguments are evaluated left to right. + 0 => Op::Mov(read_local(prog)?, read_arg(prog, arg1_const)?), + 1 => Op::JmpIf(read_arg(prog, arg1_const)?, read_arg(prog, arg2_const)?), + 2 => Op::Jmp(read_arg(prog, arg1_const)?), + 3 => Op::Alloc(read_local(prog)?, read_arg(prog, arg1_const)?), + 4 => Op::Peek( + read_local(prog)?, + read_arg(prog, arg1_const)?, + read_arg(prog, arg2_const)?, + ), + 5 => Op::Poke( + read_arg(prog, arg1_const)?, + read_arg(prog, false)?, + read_arg(prog, arg2_const)?, + ), + 6 => Op::Add( + read_local(prog)?, + read_arg(prog, arg1_const)?, + read_arg(prog, arg2_const)?, + ), + 7 => Op::Sub( + read_local(prog)?, + read_arg(prog, arg1_const)?, + read_arg(prog, arg2_const)?, + ), + 8 => Op::Mul( + read_local(prog)?, + read_arg(prog, arg1_const)?, + read_arg(prog, arg2_const)?, + ), + 9 => Op::Div( + read_local(prog)?, + read_arg(prog, arg1_const)?, + read_arg(prog, arg2_const)?, + ), + 10 => Op::Mod( + read_local(prog)?, + read_arg(prog, arg1_const)?, + read_arg(prog, arg2_const)?, + ), + 11 => Op::PeekByte( + read_local(prog)?, + read_arg(prog, arg1_const)?, + read_arg(prog, arg2_const)?, + ), + 12 => Op::PokeByte( + read_arg(prog, arg1_const)?, + read_arg(prog, false)?, + read_arg(prog, arg2_const)?, + ), + 13 => Op::Exit(read_arg(prog, arg1_const)?), + 14 => Op::AllocBytevector(read_local(prog)?, read_arg(prog, arg1_const)?), _ => { return Err(String::from("invalid opcode")); } }; - Ok(op) + Ok(Some(op)) } -pub fn decode(prog: &[u8]) -> Result<Vec<Op>, String> { - let mut reader = prog; +pub fn decode<T: Read>(prog: &mut T) -> Result<Vec<Op>, String> { let mut out = Vec::new(); - while reader.len() > 0 { - out.push(op_decoding(&mut reader)?); + loop { + match op_decoding(prog)? { + Some(op) => { + out.push(op); + } + None => { + return Ok(out); + } + } } - Ok(out) } mod tests { use super::*; + use Arg::*; + use Op::*; #[test] - fn decode_const() { + fn decode_mov_const() { assert_eq!( - Ok(vec![Op::Const(10)]), - decode(&vec![0xf2, 0x3, 0, 0, 0, 0, 0, 0, 0xa, 0, 0, 0, 0, 0, 0, 0]) + Ok(vec![Mov(Local(0), Const(Value::from_int(10)))]), + decode(&mut vec![0x2, 0, 0x15, 0, 0, 0, 0, 0, 0, 0].as_slice()) ); } #[test] - fn decode_add() { + fn decode_mov_local() { assert_eq!( - Ok(vec![Op::Add]), - decode(&vec![0xfc, 0x3, 0, 0, 0, 0, 0, 0]) + Ok(vec![Mov(Local(0), L(Local(1)))]), + decode(&mut vec![0, 0, 1].as_slice()), ); } #[test] - fn decode_sub() { - assert_eq!(Ok(vec![Op::Sub]), decode(&vec![0x6, 0x4, 0, 0, 0, 0, 0, 0])); - } - - #[test] - fn decode_mul() { + fn decode_jmpif() { assert_eq!( - Ok(vec![Op::Mul]), - decode(&vec![0x10, 0x4, 0, 0, 0, 0, 0, 0]) + Ok(vec![JmpIf(L(Local(0)), L(Local(1)))]), + decode(&mut vec![4, 0, 1].as_slice()) ); } #[test] - fn decode_div() { + fn decode_jmp() { assert_eq!( - Ok(vec![Op::Div]), - decode(&vec![0x1a, 0x4, 0, 0, 0, 0, 0, 0]) - ); - } - - #[test] - fn decode_mod() { - assert_eq!( - Ok(vec![Op::Mod]), - decode(&vec![0x24, 0x4, 0, 0, 0, 0, 0, 0]) + Ok(vec![Jmp(L(Local(0)))]), + decode(&mut vec![8, 0].as_slice()) ); } #[test] fn decode_alloc() { assert_eq!( - Ok(vec![Op::Alloc]), - decode(&vec![0xda, 0x7, 0, 0, 0, 0, 0, 0]) - ); - } - - #[test] - fn decode_peek() { - assert_eq!( - Ok(vec![Op::Peek]), - decode(&vec![0xe4, 0x7, 0, 0, 0, 0, 0, 0]) - ); - } - - #[test] - fn decode_poke() { - assert_eq!( - Ok(vec![Op::Poke]), - decode(&vec![0xee, 0x7, 0, 0, 0, 0, 0, 0]) - ); - } - - #[test] - fn decode_peek_byte() { - assert_eq!( - Ok(vec![Op::PeekByte]), - decode(&vec![0xf8, 0x7, 0, 0, 0, 0, 0, 0]) - ); - } - - #[test] - fn decode_poke_byte() { - assert_eq!( - Ok(vec![Op::PokeByte]), - decode(&vec![0x2, 0x8, 0, 0, 0, 0, 0, 0]) - ); - } - - #[test] - fn decode_pop() { - assert_eq!( - Ok(vec![Op::Pop]), - decode(&vec![0xc2, 0xb, 0, 0, 0, 0, 0, 0]) - ); - } - - #[test] - fn decode_local() { - assert_eq!( - Ok(vec![Op::Local(10)]), - decode(&vec![0xcc, 0xb, 0, 0, 0, 0, 0, 0, 0xa]) - ); - } - - #[test] - fn decode_if() { - assert_eq!( - Ok(vec![Op::If(10)]), - decode(&vec![0xaa, 0xf, 0, 0, 0, 0, 0, 0, 0xa, 0, 0, 0, 0, 0, 0, 0]) - ); - } - - #[test] - fn decode_call() { - assert_eq!( - Ok(vec![Op::Call(10)]), - decode(&vec![0xb4, 0xf, 0, 0, 0, 0, 0, 0, 0xa]) - ); - } - - #[test] - fn decode_ret() { - assert_eq!( - Ok(vec![Op::Ret]), - decode(&vec![0xbe, 0xf, 0, 0, 0, 0, 0, 0]) - ); - } - - #[test] - fn decode_exit() { - assert_eq!( - Ok(vec![Op::Exit]), - decode(&vec![0xc8, 0xf, 0, 0, 0, 0, 0, 0]) - ); - } - - #[test] - fn decode_putc() { - assert_eq!( - Ok(vec![Op::PutC]), - decode(&vec![0x92, 0x13, 0, 0, 0, 0, 0, 0]) - ); - } - - #[test] - fn decode_getc() { - assert_eq!( - Ok(vec![Op::GetC]), - decode(&vec![0x9c, 0x13, 0, 0, 0, 0, 0, 0]) + Ok(vec![Alloc(Local(0), L(Local(1)))]), + decode(&mut vec![12, 0, 1].as_slice()) ); } } |
