diff options
Diffstat (limited to 'crates/sloth_bytecode')
| -rw-r--r-- | crates/sloth_bytecode/Cargo.toml | 5 | ||||
| -rw-r--r-- | crates/sloth_bytecode/macros/Cargo.toml | 13 | ||||
| -rw-r--r-- | crates/sloth_bytecode/macros/src/lib.rs | 153 | ||||
| -rw-r--r-- | crates/sloth_bytecode/src/lib.rs | 203 |
4 files changed, 326 insertions, 48 deletions
diff --git a/crates/sloth_bytecode/Cargo.toml b/crates/sloth_bytecode/Cargo.toml index a302c81..981b6ee 100644 --- a/crates/sloth_bytecode/Cargo.toml +++ b/crates/sloth_bytecode/Cargo.toml @@ -2,3 +2,8 @@ name = "sloth_bytecode" version = "0.1.0" edition = "2021" + +[dependencies] +sloth_bytecode_macros = { path = "./macros" } + +byteorder = "1.4.3" diff --git a/crates/sloth_bytecode/macros/Cargo.toml b/crates/sloth_bytecode/macros/Cargo.toml new file mode 100644 index 0000000..c75bc58 --- /dev/null +++ b/crates/sloth_bytecode/macros/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "sloth_bytecode_macros" +version = "0.1.0" +edition = "2021" + +[dependencies] +proc-macro2 = "1.0.54" +quote = "1.0.26" +syn = "2.0.12" + +[lib] +proc-macro = true + diff --git a/crates/sloth_bytecode/macros/src/lib.rs b/crates/sloth_bytecode/macros/src/lib.rs new file mode 100644 index 0000000..e07a027 --- /dev/null +++ b/crates/sloth_bytecode/macros/src/lib.rs @@ -0,0 +1,153 @@ +use proc_macro2::{Ident, TokenStream}; +use quote::{format_ident, quote}; +use syn::parse::Parse; +use syn::punctuated::Punctuated; +use syn::{bracketed, parse_macro_input, LitInt, LitStr, Token}; + +struct DslInstructionInput { + opcode: LitInt, + name: Ident, + args: Punctuated<Ident, Token![,]>, + description: LitStr, +} + +impl Parse for DslInstructionInput { + fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { + let args_content; + Ok(Self { + opcode: input.parse()?, + name: input.parse()?, + args: { + bracketed!(args_content in input); + args_content.parse_terminated(Ident::parse, Token![,])? + }, + description: input.parse()?, + }) + } +} + +struct DslInstructionsInput { + name: Ident, + instructions: Punctuated<DslInstructionInput, Token![,]>, +} + +impl Parse for DslInstructionsInput { + fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { + Ok(Self { + name: input.parse()?, + instructions: { + input.parse::<Token![;]>()?; + input.parse_terminated(DslInstructionInput::parse, Token![,])? + }, + }) + } +} + +fn into_enum_field(instruction: &DslInstructionInput) -> TokenStream { + let DslInstructionInput { + opcode, + name, + args, + description, + } = instruction; + + let args = args.iter(); + + quote! { + #[doc = #description] + #name ( #( #args ),* ) = #opcode + } +} + +fn into_bytecode_parser(instruction: &DslInstructionInput) -> TokenStream { + let DslInstructionInput { + opcode, + name, + args, + description: _, + } = instruction; + + let args = args.iter().map(|arg| { + let read_ident = format_ident!("read_{}", arg); + + let _chunk_codes = arg; + + quote! { + { + let a: #arg = (chunk.code[*offset] << 56) + (chunk) + cursor . #read_ident ::<byteorder::LittleEndian>().unwrap() + } + } + }); + + quote! { + #opcode => { + Self:: #name ( + #( #args ),* + ) + } + } +} + +#[proc_macro] +pub fn instructions(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = parse_macro_input!(input as DslInstructionsInput); + + // Getting values to construct the enum + let enum_name = input.name; + let enum_fields = input + .instructions + .iter() + .map(into_enum_field) + .collect::<Vec<_>>(); + + // Getting the values to parse bytecode + let bytecode_parsers = input + .instructions + .iter() + .map(into_bytecode_parser) + .collect::<Vec<_>>(); + + // Building out the expanded code + let expanded = quote! { + #[repr(u8)] + #[derive(Clone, Debug)] + enum #enum_name { + #( #enum_fields ),* + } + + impl #enum_name { + fn disassemble(chunk: &Chunk, offset: &mut usize) -> #enum_name { + let opcode = chunk.code[*offset]; + *offset += 1; + + let instruction = match opcode { + #( #bytecode_parsers ),* + _ => panic!("Unknown bytecode encountered"), + }; + + instruction + } + + fn assemble(chunk: &mut Chunk) { + // + } + } + + // 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() +} diff --git a/crates/sloth_bytecode/src/lib.rs b/crates/sloth_bytecode/src/lib.rs index f814f86..dbf53ae 100644 --- a/crates/sloth_bytecode/src/lib.rs +++ b/crates/sloth_bytecode/src/lib.rs @@ -1,4 +1,3 @@ -#![feature(macro_metavar_expr)] #![allow(dead_code)] #![warn( clippy::wildcard_imports, @@ -8,56 +7,164 @@ unused_lifetimes )] -macro_rules! instructions { - ( $( $opcode:literal $name:ident [ $( $v_type:ident ),* ] $doc:literal ),* ) => { - #[repr(u8)] - enum Instruction { - $( - #[doc = $doc] - $name ( $( $v_type ),* ) = $opcode - ),* - } +use std::io::Cursor; - impl Instruction { - fn opcode(&self) -> u8 { - match self { - $( - Self::$name ( $( _ ${ignore(v_type)} ),* ) => $opcode - ),* - } - } - - fn from_bytecode(bytecode: &[u8]) -> Option<Self> { - if bytecode.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 - } - } - } +use byteorder::ReadBytesExt; +// 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, } -instructions! { - 0x00 Constant [u64] "Push a constant value onto the stack", +pub enum Instruction { + Constant(u64), - 0x01 Pop [] "Pop a value from the stack", - 0x02 Dup [] "Duplicate a value on the stack", + Pop(), + Dup(), - 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" + 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); + }; + + 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) +} + +// impl<T: Iterator<Item = u8>> TryFrom<T> for Bytecode { +// type Error = BytecodeError; +// +// fn try_from(value: T) -> Result<Self, Self::Error> { +// todo!() +// // +// } +// } |
