diff options
| author | Cody <cody@codyq.dev> | 2023-04-08 20:36:04 -0500 |
|---|---|---|
| committer | Cody <cody@codyq.dev> | 2023-04-08 20:36:04 -0500 |
| commit | 637dcc8c495c272a66788a650c09d20178d57f55 (patch) | |
| tree | c7b163ed55f73ec41bdba841ac73cd4788effed2 | |
| parent | a990b1f869385a2cf9da38176b709261bd2b30cf (diff) | |
| download | sloth-637dcc8c495c272a66788a650c09d20178d57f55.tar.gz | |
Finnish 🇫🇮 the disassemble portion of macro
| -rw-r--r-- | crates/sloth_bytecode/macros/src/lib.rs | 87 | ||||
| -rw-r--r-- | crates/sloth_bytecode/src/lib.rs | 186 |
2 files changed, 93 insertions, 180 deletions
diff --git a/crates/sloth_bytecode/macros/src/lib.rs b/crates/sloth_bytecode/macros/src/lib.rs index e07a027..41035b9 100644 --- a/crates/sloth_bytecode/macros/src/lib.rs +++ b/crates/sloth_bytecode/macros/src/lib.rs @@ -1,9 +1,11 @@ use proc_macro2::{Ident, TokenStream}; -use quote::{format_ident, quote}; +use quote::quote; use syn::parse::Parse; use syn::punctuated::Punctuated; use syn::{bracketed, parse_macro_input, LitInt, LitStr, Token}; +// TODO: Rename args to operands? + struct DslInstructionInput { opcode: LitInt, name: Ident, @@ -53,9 +55,16 @@ fn into_enum_field(instruction: &DslInstructionInput) -> TokenStream { let args = args.iter(); - quote! { - #[doc = #description] - #name ( #( #args ),* ) = #opcode + if args.len() > 0 { + quote! { + #[doc = #description] + #name ( #( #args ),* ) = #opcode + } + } else { + quote! { + #[doc = #description] + #name = #opcode + } } } @@ -67,23 +76,44 @@ fn into_bytecode_parser(instruction: &DslInstructionInput) -> TokenStream { description: _, } = instruction; - let args = args.iter().map(|arg| { - let read_ident = format_ident!("read_{}", arg); - - let _chunk_codes = arg; + if args.is_empty() { + return quote! { + #opcode => Self :: #name + }; + } - quote! { - { - let a: #arg = (chunk.code[*offset] << 56) + (chunk) - cursor . #read_ident ::<byteorder::LittleEndian>().unwrap() - } + let mut arg_params = Vec::new(); + for arg in args { + let size = match arg.to_string().as_str() { + "u128" => 128, + "u64" => 64, + "u32" => 32, + "u16" => 16, + "u8" => 8, + typ => panic!("Unsupported instruction arg type '{typ}'"), + } as usize; + + let bytes = size / 8; + + let mut chunks = Vec::new(); + for byte in 0..bytes { + let shift_amount = size - (byte + 1) * bytes; + chunks.push(quote! { + ((chunk.code[*offset + #byte] as #arg) << #shift_amount) + }); } - }); + + arg_params.push(quote! { + let value = #( #chunks )+* ; + *offset += #bytes ; + value + }); + } quote! { #opcode => { - Self:: #name ( - #( #args ),* + Self :: #name ( + #( { #arg_params } ),* ) } } @@ -109,9 +139,9 @@ pub fn instructions(input: proc_macro::TokenStream) -> proc_macro::TokenStream { .collect::<Vec<_>>(); // Building out the expanded code - let expanded = quote! { + quote! { #[repr(u8)] - #[derive(Clone, Debug)] + #[derive(Clone, Debug, Eq, PartialEq)] enum #enum_name { #( #enum_fields ),* } @@ -122,7 +152,7 @@ pub fn instructions(input: proc_macro::TokenStream) -> proc_macro::TokenStream { *offset += 1; let instruction = match opcode { - #( #bytecode_parsers ),* + #( #bytecode_parsers , )* _ => panic!("Unknown bytecode encountered"), }; @@ -133,21 +163,6 @@ pub fn instructions(input: proc_macro::TokenStream) -> proc_macro::TokenStream { // } } - - // impl #enum_name { - // fn from_bytecode(cursor: &mut Cursor<Vec<u8>>) -> Self { - // let bytecode = cursor.read_u8().unwrap(); - // - // let instruction = match bytecode { - // #( #bytecode_parsers ),* - // _ => panic!("Unknown bytecode encountered"), - // }; - // - // instruction - // } - // } - }; - - // Returning the proc_macro version of TokenStream - expanded.into() + } + .into() } diff --git a/crates/sloth_bytecode/src/lib.rs b/crates/sloth_bytecode/src/lib.rs index dbf53ae..f0262f6 100644 --- a/crates/sloth_bytecode/src/lib.rs +++ b/crates/sloth_bytecode/src/lib.rs @@ -7,164 +7,62 @@ unused_lifetimes )] -use std::io::Cursor; - -use byteorder::ReadBytesExt; -// use sloth_bytecode_macros::instructions; +use sloth_bytecode_macros::instructions; pub struct Chunk { pub code: Vec<u8>, pub constants: Vec<u64>, } -// instructions! { -// Instructions; -// -// 0x00 Constant [u64] "Push a constant value onto the stack", -// -// 0x01 Pop [] "Pop a value from the stack", -// 0x02 Dup [] "Duplicate a value on the stack", -// -// 0x10 Add [] "Add the last 2 values on the stack", -// 0x11 Sub [] "Subtract the last 2 values on the stack", -// 0x12 Mul [] "Multiply the last 2 values on the stack", -// 0x13 Div [] "Divide the last 2 values on the stack", -// 0x14 Mod [] "Modulo the last 2 values on the stack" -// } - -// impl Instructions { -// fn disassemble(chunk: &Chunk, offset: &mut usize) { -// // -// } -// -// fn assemble(chunk: &mut Chunk) { -// // -// } -// } - -// #[test] -// fn test() { -// let mut cursor = Cursor::new(vec![0, 1, 0, 0, 1, 0, 0, 0, 0]); -// let instruction = Instructions::from_bytecode(&mut cursor); -// println!("{instruction:?}"); -// assert!(1 == 0); -// } - -// macro_rules! instructions { -// ( $( $opcode:literal $name:ident [ $( $v_type:ident ),* ] $doc:literal -// ),* ) => { #[repr(u8)] -// enum Instruction { -// $( -// #[doc = $doc] -// $name ( $( $v_type ),* ) = $opcode -// ),* -// } -// -// impl Instruction { -// fn opcode(&self) -> u8 { -// match self { -// $( -// Self::$name ( $( _ ${ignore(v_type)} ),* ) => $opcode -// ),* -// } -// } -// -// fn from_bytecode(bytecode: &[u8]) -> Option<Self> { -// if bstecode.is_empty() { -// return None; -// } -// -// let opcode = bytecode[0]; -// let instruction = match opcode { -// $( -// $opcode => { -// // TODO: Get the actual values -// Some(Self::$name ( $( 0 ${ignore(v_type)} ),* )) -// } -// ),*, -// _ => None, -// }; -// -// instruction -// } -// } -// } -// } - -// instructions! { -// Instructions; -// -// 0x00 Constant [u64] "Push a constant value onto the stack", -// -// 0x01 Pop [] "Pop a value from the stack", -// 0x02 Dup [] "Duplicate a value on the stack", -// -// 0x10 Add [] "Add the last 2 values on the stack", -// 0x11 Sub [] "Subtract the last 2 values on the stack", -// 0x12 Mul [] "Multiply the last 2 values on the stack", -// 0x13 Div [] "Divide the last 2 values on the stack", -// 0x14 Mod [] "Modulo the last 2 values on the stack" -// } - pub enum Error { UnknownOpcode(u8), InvalidArguments, Eof, } -pub enum Instruction { - Constant(u64), +instructions! { + Instructions; - Pop(), - Dup(), + 0x00 Constant [u64] "Push a constant value onto the stack", - Add(), - Sub(), - Mul(), - Div(), - Mod(), -} - -// fn parse_bytecode(pos: usize, bc: &[u8]) -> Result<Bytecode, BytecodeError> { -// let Some(opcode) = bc.get(pos) else { -// return Err(BytecodeError::Eof); -// }; -// -// let instruction = match opcode { -// 0x00 => { -// // let arg0: [u8; 8] = bc.get(1..1+size_of::<u64>()).unwrap(); -// let arg0 = u64::from_ne_bytes(arg0); -// } -// _ => return Err(BytecodeError::UnknownOpcode(opcode)), -// } -// -// todo!() -// } - -fn parse_bytecode(cursor: &mut Cursor<&[u8]>) -> Result<Instruction, Error> { - let Ok(opcode) = cursor.read_u8() else { - return Err(Error::Eof); - }; + 0x01 Pop [] "Pop a value from the stack", + 0x02 Dup [] "Duplicate a value on the stack", - let instruction = match opcode { - 0x00 => { - let arg0 = cursor - .read_u64::<byteorder::LittleEndian>() - .map_err(|_| Error::InvalidArguments)?; - - Instruction::Constant(arg0) - } - _ => return Err(Error::UnknownOpcode(opcode)), - }; - - Ok(instruction) + 0x10 Add [] "Add the last 2 values on the stack", + 0x11 Sub [] "Subtract the last 2 values on the stack", + 0x12 Mul [] "Multiply the last 2 values on the stack", + 0x13 Div [] "Divide the last 2 values on the stack", + 0x14 Mod [] "Modulo the last 2 values on the stack" } -// impl<T: Iterator<Item = u8>> TryFrom<T> for Bytecode { -// type Error = BytecodeError; -// -// fn try_from(value: T) -> Result<Self, Self::Error> { -// todo!() -// // -// } -// } +#[cfg(test)] +mod tests { + use crate::{Chunk, Instructions}; + + #[test] + #[rustfmt::skip] + fn decompile_basic_instructions() { + let code = vec![ + // Load constant 0 + 0x00, 0, 0, 0, 0, 0, 0, 0, 0, + // Pop, Dup, Add, Sub, Mul, Div, Mod + 0x01, 0x02, 0x10, 0x11, 0x12, 0x13, 0x14, + ]; + + let chunk = Chunk { + code, + constants: Vec::new(), + }; + + let mut offset = 0; + + assert_eq!(Instructions::disassemble(&chunk, &mut offset), Instructions::Constant(0)); + assert_eq!(Instructions::disassemble(&chunk, &mut offset), Instructions::Pop); + assert_eq!(Instructions::disassemble(&chunk, &mut offset), Instructions::Dup); + assert_eq!(Instructions::disassemble(&chunk, &mut offset), Instructions::Add); + assert_eq!(Instructions::disassemble(&chunk, &mut offset), Instructions::Sub); + assert_eq!(Instructions::disassemble(&chunk, &mut offset), Instructions::Mul); + assert_eq!(Instructions::disassemble(&chunk, &mut offset), Instructions::Div); + assert_eq!(Instructions::disassemble(&chunk, &mut offset), Instructions::Mod); + } +} |
