diff --git a/src/generator.rs b/src/generator.rs index d742c3e..4e6bc52 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -5,11 +5,17 @@ //! # TODO //! add docs (especially fields and registers) +use std::{ + collections::HashMap, + path::{self, PathBuf}, +}; + use crate::{ type_traits::GetName, types::{Block, Module, ModuleBlockElements, Register}, }; use heck::{ToSnakeCase, ToUpperCamelCase}; +use itertools::Itertools; use proc_macro2::TokenStream; use quote::quote; use thiserror::Error; @@ -18,6 +24,10 @@ use thiserror::Error; pub enum CodeGenError { #[error("tokenization error: {0}")] TokenizeError(#[from] syn::Error), + #[error("failed to create file: {0}")] + FilePathError(String), + #[error("parent is required for {module}")] + ParentMissing { module: &'static str }, } mod util { @@ -114,42 +124,70 @@ pub trait CodeGen { /// `parent_name` in UpperCamelCase. fn generate_register_interface( self, - parent_name: proc_macro2::Ident, - ) -> Result; + parent_name: Option, + parent_path: Option, + files: HashMap, + ) -> Result, CodeGenError>; } impl CodeGen for Module { fn generate_register_interface( self, - _: proc_macro2::Ident, - ) -> Result { - let mut out = TokenStream::new(); + _: Option, + _: Option, + mut files: HashMap, + ) -> std::result::Result, CodeGenError> { if !self.elements_bitstring.is_empty() { todo!("bitstring generation is not yet implemented") } - let ident_register_interface = util::parse_to_ident("RegisterInterface").unwrap(); let child_mods = self .elements_other - .into_iter() - .map(|e| e.generate_register_interface(ident_register_interface.clone())) + .iter() + .map(|e| { + util::parse_to_ident(&e.get_name().to_snake_case()).map(|child_name| { + quote! { + pub mod #child_name; + } + }) + }) .collect::, _>>()?; - for child in child_mods { - out.extend(child); - } + let out = quote! { + #(#child_mods)* + }; + files.insert(PathBuf::from("./register_interface.rs"), out); - Ok(out) + let ident_register_interface = util::parse_to_ident("RegisterInterface").unwrap(); + let register_interface_mod = PathBuf::from("register_interface"); + let files = self + .elements_other + .into_iter() + .try_fold(files, |files, e| { + e.generate_register_interface( + Some(ident_register_interface.clone()), + Some(register_interface_mod.clone()), + files, + ) + })?; + + Ok(files) } } impl CodeGen for ModuleBlockElements { fn generate_register_interface( self, - parent: proc_macro2::Ident, - ) -> Result { + parent_name: Option, + parent_path: Option, + files: HashMap, + ) -> Result, CodeGenError> { match self { - ModuleBlockElements::Block(block) => block.generate_register_interface(parent), - ModuleBlockElements::Register(register) => register.generate_register_interface(parent), + ModuleBlockElements::Block(block) => { + block.generate_register_interface(parent_name, parent_path, files) + } + ModuleBlockElements::Register(register) => { + register.generate_register_interface(parent_name, parent_path, files) + } ModuleBlockElements::Memory(_memory) => todo!(), ModuleBlockElements::Fifo(_fifo) => todo!(), } @@ -159,8 +197,15 @@ impl CodeGen for ModuleBlockElements { impl CodeGen for Block { fn generate_register_interface( self, - parent: proc_macro2::Ident, - ) -> Result { + parent_name: Option, + parent_path: Option, + mut files: HashMap, + ) -> Result, CodeGenError> { + let parent_name = + parent_name.ok_or_else(|| CodeGenError::ParentMissing { module: "Block" })?; + let parent_path = + parent_path.ok_or_else(|| CodeGenError::ParentMissing { module: "Block" })?; + let snake_case_name = util::parse_to_ident(&self.name.to_snake_case())?; let upper_camel_name = util::parse_to_ident(&self.name.to_upper_camel_case())?; let addr = util::parse_to_literal(&format!("0x{:x}", self.addr))?; @@ -191,50 +236,83 @@ impl CodeGen for Block { out }; - let code_children = self - .elements - .into_iter() - .map(|e| e.generate_register_interface(upper_camel_name.clone())) - .collect::, _>>()?; - - let parent_struct = if parent == util::parse_to_ident("RegisterInterface").unwrap() { - quote! {#parent} + let parent_struct = if parent_name == util::parse_to_ident("RegisterInterface").unwrap() { + quote! {#parent_name} } else { - quote! {#parent<'a>} + quote! {#parent_name<'a>} }; - Ok(quote! { - pub mod #snake_case_name { - #[doc = #desc] - - use std::marker::PhantomData; - - use super::#parent; - - #(#code_children)* - - const OFFSET: usize = #addr; - - pub struct #upper_camel_name<'a> { - mem_ptr: *mut u32, - _marker: PhantomData<&'a mut #parent_struct>, - } - - impl #upper_camel_name<'_> { - pub(crate) fn new(parent_ptr: *mut u32) -> Self { - #upper_camel_name { - mem_ptr: unsafe { parent_ptr.add(OFFSET) }, - _marker: PhantomData, - } + let child_mods = self + .elements + .iter() + .map(|e| { + util::parse_to_ident(&e.get_name().to_snake_case()).map(|child_name| { + quote! { + pub mod #child_name; } + }) + }) + .collect::, _>>()?; - #accessor_methods - } + let out = quote! { + #[doc = #desc] + + use std::marker::PhantomData; + + use super::#parent_name; + + #(#child_mods)* + + const OFFSET: usize = #addr; + + pub struct #upper_camel_name<'a> { + mem_ptr: *mut u32, + _marker: PhantomData<&'a mut #parent_struct>, } - }) + + impl #upper_camel_name<'_> { + pub(crate) fn new(parent_ptr: *mut u32) -> Self { + #upper_camel_name { + mem_ptr: unsafe { parent_ptr.add(OFFSET) }, + _marker: PhantomData, + } + } + + #accessor_methods + } + }; + let (out_path, next_parent_path) = mod_file_path(parent_path, &snake_case_name); + log::info!("{:?}", out_path); + if let Some(old_out) = files.insert(out_path.clone(), out.clone()) { + log::error!("path {}", out_path.display()); + log::error!("old {}", old_out.to_string()); + log::error!("new {}", out.to_string()); + return Err(CodeGenError::FilePathError(snake_case_name.to_string())); + }; + + let files = self.elements.into_iter().try_fold(files, |files, e| { + e.generate_register_interface( + Some(upper_camel_name.clone()), + Some(next_parent_path.clone()), + files, + ) + })?; + + Ok(files) } } +/// Get filepath to write the `mod_snake_case_name` and next parent_path. +fn mod_file_path( + parent_path: path::PathBuf, + mod_snake_case_name: &proc_macro2::Ident, +) -> (path::PathBuf, path::PathBuf) { + ( + parent_path.join(format!("{}.rs", mod_snake_case_name)), + parent_path.join(mod_snake_case_name.to_string()), + ) +} + mod codegen_register; mod codegen_registerspec_impl; @@ -242,8 +320,15 @@ mod codegen_registerspec_impl; impl CodeGen for Register { fn generate_register_interface( self, - parent: proc_macro2::Ident, - ) -> Result { + parent_name: Option, + parent_path: Option, + mut files: HashMap, + ) -> Result, CodeGenError> { + let parent_name = + parent_name.ok_or_else(|| CodeGenError::ParentMissing { module: "Block" })?; + let parent_path = + parent_path.ok_or_else(|| CodeGenError::ParentMissing { module: "Block" })?; + let snake_case_name = util::parse_to_ident(&self.name.to_snake_case())?; let upper_camel_name = util::parse_to_ident(&self.name.to_upper_camel_case())?; let reg_name = util::parse_to_ident(&format!("Reg{upper_camel_name}"))?; @@ -263,7 +348,7 @@ impl CodeGen for Register { type_ux, ); - Ok(quote! { + let out = quote! { pub mod #snake_case_name { use std::marker::PhantomData; @@ -273,7 +358,7 @@ impl CodeGen for Register { pub struct #reg_name<'a> { mem_ptr: *mut u32, - _marker: PhantomData<&'a mut super::#parent<'a>>, + _marker: PhantomData<&'a mut super::#parent_name<'a>>, } impl #reg_name<'_> { @@ -289,6 +374,13 @@ impl CodeGen for Register { #code_t_def } - }) + }; + + let (out_path, _next_parent_path) = mod_file_path(parent_path, &snake_case_name); + log::info!("{:?}", out_path); + if files.insert(out_path, out).is_some() { + return Err(CodeGenError::FilePathError(snake_case_name.to_string())); + } + Ok(files) } } diff --git a/src/main.rs b/src/main.rs index f438176..64236d5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,8 @@ -use std::fs; +use std::{collections::HashMap, fs}; use anyhow::Result; use endcap_sl_software_ri_generator::{generator::CodeGen, types}; +use itertools::Itertools; fn main() -> Result<()> { env_logger::init(); @@ -21,16 +22,13 @@ fn main() -> Result<()> { let register_map = types::Module::from_xml_dom(doc.root_element())?; println!("read: {:#?}", register_map); - // println!("{}", register_map.generate_register_interface()?); - fs::write( - "testgen.rs", - register_map - .generate_register_interface(proc_macro2::Ident::new( - "a", - proc_macro2::Span::call_site(), - ))? - .to_string(), - )?; + println!("==========================================="); + + let files = register_map.generate_register_interface(None, None, HashMap::new())?; + println!("{:#?}", files); + for filepath in files.keys().sorted() { + println!("{}", filepath.display()); + } Ok(()) }