// 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_const(prog: &mut T) -> Result { let mut buf = [0; 8]; match prog.read_exact(&mut buf) { Ok(()) => (), Err(e) => { return Err(format!("error reading input: {e}")); } }; Ok(Value(u64::from_le_bytes(buf))) } fn read_local(prog: &mut T) -> Result { let mut buf = [0; 1]; match prog.read_exact(&mut buf) { Ok(()) => (), Err(e) => { return Err(format!("error reading input: {e}")); } }; Ok(Local(buf[0])) } fn read_arg(prog: &mut T, is_const: bool) -> Result { 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(prog: &mut T) -> Result, String> { let mut buf = [0; 1]; match prog.read_exact(&mut buf) { Ok(()) => (), Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => { return Ok(None); } Err(e) => { return Err(format!("error reading input: {}", e)); } }; 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(Some(op)) } pub fn decode(prog: &mut T) -> Result, String> { let mut out = Vec::new(); loop { match op_decoding(prog)? { Some(op) => { out.push(op); } None => { return Ok(out); } } } } #[cfg(test)] mod tests { use super::*; use Arg::*; use Op::*; fn decode_vec(v: Vec) -> Vec { decode(&mut v.as_slice()).expect("decode failed") } #[test] fn decode_mov_const() { assert_eq!( vec![Mov(Local(0), Const(Value::from_int(10)))], decode_vec(vec![0x2, 0, 0x15, 0, 0, 0, 0, 0, 0, 0]) ); } #[test] fn decode_mov_local() { assert_eq!( vec![Mov(Local(0), L(Local(1)))], decode_vec(vec![0, 0, 0x1]), ); } #[test] fn decode_jmpif_local() { assert_eq!( vec![JmpIf(L(Local(0)), L(Local(1)))], decode_vec(vec![0x4, 0, 0x1]) ); } #[test] fn decode_jmpif_const() { assert_eq!( vec![JmpIf( Const(Value::from_bool(true)), Const(Value::from_int(10)), )], decode_vec(vec![ 0x7, 0xa, 0, 0, 0, 0, 0, 0, 0, 0x15, 0, 0, 0, 0, 0, 0, 0 ]) ); } #[test] fn decode_jmp_local() { assert_eq!(vec![Jmp(L(Local(0)))], decode_vec(vec![0x8, 0])); } #[test] fn decode_jmp_const() { assert_eq!( vec![Jmp(Const(Value::from_int(10)))], decode_vec(vec![0xb, 0x15, 0, 0, 0, 0, 0, 0, 0]) ); } #[test] fn decode_alloc_local() { assert_eq!( vec![Alloc(Local(0), L(Local(1)))], decode_vec(vec![0xc, 0, 0x1]) ); } #[test] fn decode_alloc_const() { assert_eq!( vec![Alloc(Local(0), Const(Value::from_int(10)))], decode_vec(vec![0xe, 0, 0x15, 0, 0, 0, 0, 0, 0, 0]) ); } #[test] fn decode_peek_local() { assert_eq!( vec![Peek(Local(0), L(Local(1)), L(Local(2)))], decode_vec(vec![0x10, 0, 0x1, 0x2]) ); } #[test] fn decode_peek_const() { assert_eq!( vec![Peek(Local(0), L(Local(1)), Const(Value::from_int(10)))], decode_vec(vec![0x11, 0, 0x1, 0x15, 0, 0, 0, 0, 0, 0, 0]) ); } #[test] fn decode_poke_local() { assert_eq!( vec![Poke(L(Local(0)), L(Local(1)), L(Local(2)))], decode_vec(vec![0x14, 0, 0x1, 0x2]) ); } #[test] fn decode_poke_const() { assert_eq!( vec![Poke( Const(Value::from_int(10)), L(Local(0)), Const(Value::from_int(0)), )], decode_vec(vec![ 0x17, 0x15, 0, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0 ]) ); } #[test] fn decode_add_local() { assert_eq!( vec![Add(Local(0), L(Local(1)), L(Local(2)))], decode_vec(vec![0x18, 0, 1, 2]) ); } #[test] fn decode_add_const() { assert_eq!( vec![Add( Local(0), Const(Value::from_int(13)), Const(Value::from_int(84)) )], decode_vec(vec![ 0x1b, 0, 0x1b, 0, 0, 0, 0, 0, 0, 0, 0xa9, 0, 0, 0, 0, 0, 0, 0 ]) ); } #[test] fn decode_sub_local() { assert_eq!( vec![Sub(Local(0), L(Local(1)), L(Local(2)))], decode_vec(vec![0x1c, 0, 1, 2]) ); } #[test] fn decode_sub_const() { assert_eq!( vec![Sub( Local(0), Const(Value::from_int(13)), Const(Value::from_int(84)) )], decode_vec(vec![ 0x1f, 0, 0x1b, 0, 0, 0, 0, 0, 0, 0, 0xa9, 0, 0, 0, 0, 0, 0, 0 ]) ); } #[test] fn decode_mul_local() { assert_eq!( vec![Mul(Local(0), L(Local(1)), L(Local(2)))], decode_vec(vec![0x20, 0, 1, 2]) ); } #[test] fn decode_mul_const() { assert_eq!( vec![Mul( Local(0), Const(Value::from_int(13)), Const(Value::from_int(84)) )], decode_vec(vec![ 0x23, 0, 0x1b, 0, 0, 0, 0, 0, 0, 0, 0xa9, 0, 0, 0, 0, 0, 0, 0 ]) ); } #[test] fn decode_div_local() { assert_eq!( vec![Div(Local(0), L(Local(1)), L(Local(2)))], decode_vec(vec![0x24, 0, 1, 2]) ); } #[test] fn decode_div_const() { assert_eq!( vec![Div( Local(0), Const(Value::from_int(5)), Const(Value::from_int(2)) )], decode_vec(vec![ 0x27, 0, 0xb, 0, 0, 0, 0, 0, 0, 0, 0x5, 0, 0, 0, 0, 0, 0, 0 ]) ); } #[test] fn decode_mod_local() { assert_eq!( vec![Mod(Local(0), L(Local(1)), L(Local(2)))], decode_vec(vec![0x28, 0, 1, 2]) ); } #[test] fn decode_mod_const() { assert_eq!( vec![Mod( Local(0), Const(Value::from_int(5)), Const(Value::from_int(2)) )], decode_vec(vec![ 0x2b, 0, 0xb, 0, 0, 0, 0, 0, 0, 0, 0x5, 0, 0, 0, 0, 0, 0, 0 ]) ); } #[test] fn decode_peekbyte_local() { assert_eq!( vec![PeekByte(Local(0), L(Local(1)), L(Local(2)))], decode_vec(vec![0x2c, 0, 1, 2]) ); } #[test] fn decode_peekbyte_const() { assert_eq!( vec![PeekByte(Local(0), L(Local(1)), Const(Value::from_int(10)))], decode_vec(vec![0x2d, 0, 1, 0x15, 0, 0, 0, 0, 0, 0, 0]) ); } #[test] fn decode_pokebyte_local() { assert_eq!( vec![PokeByte(L(Local(0)), L(Local(1)), L(Local(2)))], decode_vec(vec![0x30, 0, 1, 2]) ); } #[test] fn decode_pokebyte_const() { assert_eq!( vec![PokeByte( Const(Value::from_int(10)), L(Local(0)), Const(Value::from_int(0)) )], decode_vec(vec![ 0x33, 0x15, 0, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0 ]) ); } #[test] fn decode_exit_local() { assert_eq!(vec![Exit(L(Local(0)))], decode_vec(vec![0x34, 0])); } #[test] fn decode_exit_const() { assert_eq!( vec![Exit(Const(Value::from_int(0)))], decode_vec(vec![0x36, 0x1, 0, 0, 0, 0, 0, 0, 0]) ); } #[test] fn decode_alloc_bytevector_local() { assert_eq!( vec![AllocBytevector(Local(0), L(Local(1)))], decode_vec(vec![0x38, 0, 1]) ); } #[test] fn decode_alloc_bytevector_const() { assert_eq!( vec![AllocBytevector(Local(0), Const(Value::from_int(10)))], decode_vec(vec![0x3a, 0, 0x15, 0, 0, 0, 0, 0, 0, 0]) ); } }