summaryrefslogtreecommitdiffstats
path: root/bytecode/src/encoding.rs
diff options
context:
space:
mode:
authorRose Hogenson <rosehogenson@posteo.net>2024-02-04 16:46:20 -0800
committerRose Hogenson <rosehogenson@posteo.net>2024-02-04 16:46:20 -0800
commit17face3633686e374aa0859271ccf462a48e60aa (patch)
tree6a5482717ef1b8194ccf347a6d8af06d664902cd /bytecode/src/encoding.rs
parentd940187fd8720e0ab3c00e5a6a7ae8f181c7752d (diff)
downloadsml-17face3633686e374aa0859271ccf462a48e60aa.tar.zst
Rewrite the bytecode interpreter in Rust.
Diffstat (limited to 'bytecode/src/encoding.rs')
-rw-r--r--bytecode/src/encoding.rs139
1 files changed, 139 insertions, 0 deletions
diff --git a/bytecode/src/encoding.rs b/bytecode/src/encoding.rs
new file mode 100644
index 0000000..e37696d
--- /dev/null
+++ b/bytecode/src/encoding.rs
@@ -0,0 +1,139 @@
+use crate::heap;
+use crate::value::Value;
+use std::error::Error;
+
+pub enum Arg {
+ Local(u8),
+ Const(Value),
+}
+
+pub struct Alloc {
+ pub out: u8,
+ pub size: Arg,
+}
+
+pub struct Poke {
+ pub offset: usize,
+ pub ptr: u8,
+ pub val: Arg,
+}
+
+pub struct Peek {
+ pub out: u8,
+ pub offset: usize,
+ pub val: Arg,
+}
+
+pub struct Shuf {
+ pub out: u8,
+ pub val: Arg,
+}
+
+pub struct Exit {
+ pub val: Arg,
+}
+
+pub enum Op {
+ Alloc(Alloc),
+ Call,
+ Poke(Poke),
+ Peek(Peek),
+ Shuf(Shuf),
+ Exit(Exit),
+}
+
+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<u8, Box<dyn Error>> {
+ 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<u8, Box<dyn Error>> {
+ 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_u64(&mut self) -> Result<u64, Box<dyn Error>> {
+ 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(u64::from_le_bytes(res))
+ }
+
+ fn parse_arg(&mut self, is_const: bool) -> Result<Arg, Box<dyn Error>> {
+ if is_const {
+ Ok(Arg::Const(Value(self.parse_u64()?)))
+ } else {
+ Ok(Arg::Local(self.parse_local()?))
+ }
+ }
+}
+
+impl Op {
+ pub fn parse(data: &[u8]) -> Result<(usize, Op), Box<dyn Error>> {
+ 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 offset = usize::try_from(r.parse_u64()?)?;
+ 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 offset = usize::try_from(r.parse_u64()?)?;
+ 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 })
+ }
+ _ => {
+ return Err(Box::from(format!("invalid code {}", code)));
+ }
+ };
+
+ Ok((r.n, op))
+ }
+}