use crate::heap; use crate::value::Value; use std::error::Error; #[derive(Debug, Clone, Copy)] pub enum Arg { Local(u8), Const(Value), } #[derive(Debug, Clone, Copy)] pub struct Alloc { pub out: u8, pub size: Arg, } #[derive(Debug, Clone, Copy)] pub struct Poke { pub offset: usize, pub ptr: u8, pub val: Arg, } #[derive(Debug, Clone, Copy)] pub struct Peek { pub out: u8, pub offset: usize, pub val: Arg, } #[derive(Debug, Clone, Copy)] pub struct Shuf { pub out: u8, pub val: Arg, } #[derive(Debug, Clone, Copy)] pub struct Exit { pub val: Arg, } #[derive(Debug, Clone, Copy)] pub struct Add { pub out: u8, pub val1: Arg, pub val2: Arg, } #[derive(Debug, Clone, Copy)] pub struct Sub { pub out: u8, pub val1: Arg, pub val2: Arg, } #[derive(Debug, Clone, Copy)] pub struct Mul { pub out: u8, pub val1: Arg, pub val2: Arg, } #[derive(Debug, Clone, Copy)] pub struct Div { pub out: u8, pub val1: Arg, pub val2: Arg, } #[derive(Debug, Clone, Copy)] pub struct Less { pub out: u8, pub val1: Arg, pub val2: Arg, } #[derive(Debug, Clone, Copy)] pub struct Eq { pub out: u8, pub val1: Arg, pub val2: Arg, } #[derive(Debug, Clone, Copy)] pub struct If { pub test: Arg, pub target: usize, } #[derive(Debug, Clone, Copy)] pub struct Read { pub out: u8, pub ptr: u8, pub off: Arg, pub len: Arg, } #[derive(Debug, Clone, Copy)] pub struct Write { pub ptr: u8, pub off: Arg, pub len: Arg, } #[derive(Debug, Clone, Copy)] pub struct WriteErr { pub ptr: u8, pub off: Arg, pub len: Arg, } #[derive(Debug, Clone, Copy)] pub enum Op { Alloc(Alloc), Call, Poke(Poke), Peek(Peek), Shuf(Shuf), Exit(Exit), Add(Add), Sub(Sub), Mul(Mul), Div(Div), Less(Less), Eq(Eq), If(If), Read(Read), Write(Write), WriteErr(WriteErr), } struct Reader<'a> { data: &'a [u8], n: usize, } impl Reader<'_> { fn advance(&mut self, size: usize) { self.data = &self.data[size..]; self.n += size; } fn parse_byte(&mut self) -> Result> { if self.data.is_empty() { return Err(Box::from("read byte: no data")); } let res = self.data[0]; self.advance(1); Ok(res) } fn parse_local(&mut self) -> Result> { if self.data.is_empty() { return Err(Box::from("local: no data")); } let res = self.data[0]; if usize::from(res) >= heap::NUM_LOCALS { return Err(Box::from(format!("invalid local (out of range): {}", res))); } self.advance(1); Ok(res) } fn parse_value(&mut self) -> Result> { if self.data.len() < 8 { return Err(Box::from("value: no data")); } let mut res = [0; 8]; res.copy_from_slice(&self.data[..8]); self.advance(8); Ok(Value(u64::from_le_bytes(res))) } fn parse_arg(&mut self, is_const: bool) -> Result> { if is_const { Ok(Arg::Const(self.parse_value()?)) } else { Ok(Arg::Local(self.parse_local()?)) } } } impl Op { pub fn parse(data: &[u8]) -> Result<(usize, Op), Box> { let mut r = Reader { data, n: 0 }; let code_byte = r.parse_byte()?; let code = code_byte >> 2; let arg1_const = code_byte & 2 != 0; let arg2_const = code_byte & 1 != 0; let op = match code { 1 => { let out = r.parse_local()?; let size = r.parse_arg(arg1_const)?; Op::Alloc(Alloc { out, size }) } 2 => Op::Call, 3 => { let Some(ioffset) = r.parse_value()?.to_int() else { return Err(Box::from("invalid offset")); }; let offset = usize::try_from(ioffset)?; let ptr = r.parse_local()?; let val = r.parse_arg(arg1_const)?; Op::Poke(Poke { offset, ptr, val }) } 4 => { let out = r.parse_local()?; let Some(ioffset) = r.parse_value()?.to_int() else { return Err(Box::from("invalid offset")); }; let offset = usize::try_from(ioffset)?; let val = r.parse_arg(arg1_const)?; Op::Peek(Peek { out, offset, val }) } 5 => { let out = r.parse_local()?; let val = r.parse_arg(arg1_const)?; Op::Shuf(Shuf { out, val }) } 6 => { let val = r.parse_arg(arg1_const)?; Op::Exit(Exit { val }) } 7 => { let out = r.parse_local()?; let val1 = r.parse_arg(arg1_const)?; let val2 = r.parse_arg(arg2_const)?; Op::Add(Add { out, val1, val2 }) } 8 => { let out = r.parse_local()?; let val1 = r.parse_arg(arg1_const)?; let val2 = r.parse_arg(arg2_const)?; Op::Sub(Sub { out, val1, val2 }) } 9 => { let out = r.parse_local()?; let val1 = r.parse_arg(arg1_const)?; let val2 = r.parse_arg(arg2_const)?; Op::Mul(Mul { out, val1, val2 }) } 10 => { let out = r.parse_local()?; let val1 = r.parse_arg(arg1_const)?; let val2 = r.parse_arg(arg2_const)?; Op::Div(Div { out, val1, val2 }) } 11 => { let out = r.parse_local()?; let val1 = r.parse_arg(arg1_const)?; let val2 = r.parse_arg(arg2_const)?; Op::Less(Less { out, val1, val2 }) } 12 => { let out = r.parse_local()?; let val1 = r.parse_arg(arg1_const)?; let val2 = r.parse_arg(arg2_const)?; Op::Eq(Eq { out, val1, val2 }) } 13 => { let test = r.parse_arg(arg1_const)?; let Some(itarget) = r.parse_value()?.to_int() else { return Err(Box::from("invalid target")); }; let target = usize::try_from(itarget)?; Op::If(If { test, target }) } 14 => { let out = r.parse_local()?; let ptr = r.parse_local()?; let off = r.parse_arg(arg1_const)?; let len = r.parse_arg(arg2_const)?; Op::Read(Read { out, ptr, off, len }) } 15 => { let ptr = r.parse_local()?; let off = r.parse_arg(arg1_const)?; let len = r.parse_arg(arg2_const)?; Op::Write(Write { ptr, off, len }) } 16 => { let ptr = r.parse_local()?; let off = r.parse_arg(arg1_const)?; let len = r.parse_arg(arg2_const)?; Op::WriteErr(WriteErr { ptr, off, len }) } _ => { return Err(Box::from(format!("invalid code {}", code))); } }; Ok((r.n, op)) } }