new(generator): separate modules for each blocks (not yet implemented file io)

This commit is contained in:
testuser 2025-02-03 03:07:40 +09:00
parent cb67f9648b
commit fd106e3355
2 changed files with 159 additions and 69 deletions

View file

@ -5,11 +5,17 @@
//! # TODO //! # TODO
//! add docs (especially fields and registers) //! add docs (especially fields and registers)
use std::{
collections::HashMap,
path::{self, PathBuf},
};
use crate::{ use crate::{
type_traits::GetName, type_traits::GetName,
types::{Block, Module, ModuleBlockElements, Register}, types::{Block, Module, ModuleBlockElements, Register},
}; };
use heck::{ToSnakeCase, ToUpperCamelCase}; use heck::{ToSnakeCase, ToUpperCamelCase};
use itertools::Itertools;
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use quote::quote; use quote::quote;
use thiserror::Error; use thiserror::Error;
@ -18,6 +24,10 @@ use thiserror::Error;
pub enum CodeGenError { pub enum CodeGenError {
#[error("tokenization error: {0}")] #[error("tokenization error: {0}")]
TokenizeError(#[from] syn::Error), TokenizeError(#[from] syn::Error),
#[error("failed to create file: {0}")]
FilePathError(String),
#[error("parent is required for {module}")]
ParentMissing { module: &'static str },
} }
mod util { mod util {
@ -114,42 +124,70 @@ pub trait CodeGen {
/// `parent_name` in UpperCamelCase. /// `parent_name` in UpperCamelCase.
fn generate_register_interface( fn generate_register_interface(
self, self,
parent_name: proc_macro2::Ident, parent_name: Option<proc_macro2::Ident>,
) -> Result<proc_macro2::TokenStream, CodeGenError>; parent_path: Option<path::PathBuf>,
files: HashMap<path::PathBuf, proc_macro2::TokenStream>,
) -> Result<HashMap<path::PathBuf, proc_macro2::TokenStream>, CodeGenError>;
} }
impl CodeGen for Module { impl CodeGen for Module {
fn generate_register_interface( fn generate_register_interface(
self, self,
_: proc_macro2::Ident, _: Option<proc_macro2::Ident>,
) -> Result<proc_macro2::TokenStream, CodeGenError> { _: Option<path::PathBuf>,
let mut out = TokenStream::new(); mut files: HashMap<path::PathBuf, proc_macro2::TokenStream>,
) -> std::result::Result<HashMap<path::PathBuf, proc_macro2::TokenStream>, CodeGenError> {
if !self.elements_bitstring.is_empty() { if !self.elements_bitstring.is_empty() {
todo!("bitstring generation is not yet implemented") todo!("bitstring generation is not yet implemented")
} }
let ident_register_interface = util::parse_to_ident("RegisterInterface").unwrap();
let child_mods = self let child_mods = self
.elements_other .elements_other
.into_iter() .iter()
.map(|e| e.generate_register_interface(ident_register_interface.clone())) .map(|e| {
util::parse_to_ident(&e.get_name().to_snake_case()).map(|child_name| {
quote! {
pub mod #child_name;
}
})
})
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
for child in child_mods { let out = quote! {
out.extend(child); #(#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 { impl CodeGen for ModuleBlockElements {
fn generate_register_interface( fn generate_register_interface(
self, self,
parent: proc_macro2::Ident, parent_name: Option<proc_macro2::Ident>,
) -> Result<proc_macro2::TokenStream, CodeGenError> { parent_path: Option<path::PathBuf>,
files: HashMap<path::PathBuf, proc_macro2::TokenStream>,
) -> Result<HashMap<path::PathBuf, proc_macro2::TokenStream>, CodeGenError> {
match self { match self {
ModuleBlockElements::Block(block) => block.generate_register_interface(parent), ModuleBlockElements::Block(block) => {
ModuleBlockElements::Register(register) => register.generate_register_interface(parent), 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::Memory(_memory) => todo!(),
ModuleBlockElements::Fifo(_fifo) => todo!(), ModuleBlockElements::Fifo(_fifo) => todo!(),
} }
@ -159,8 +197,15 @@ impl CodeGen for ModuleBlockElements {
impl CodeGen for Block { impl CodeGen for Block {
fn generate_register_interface( fn generate_register_interface(
self, self,
parent: proc_macro2::Ident, parent_name: Option<proc_macro2::Ident>,
) -> Result<proc_macro2::TokenStream, CodeGenError> { parent_path: Option<path::PathBuf>,
mut files: HashMap<path::PathBuf, proc_macro2::TokenStream>,
) -> Result<HashMap<path::PathBuf, proc_macro2::TokenStream>, 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 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 upper_camel_name = util::parse_to_ident(&self.name.to_upper_camel_case())?;
let addr = util::parse_to_literal(&format!("0x{:x}", self.addr))?; let addr = util::parse_to_literal(&format!("0x{:x}", self.addr))?;
@ -191,50 +236,83 @@ impl CodeGen for Block {
out out
}; };
let code_children = self let parent_struct = if parent_name == util::parse_to_ident("RegisterInterface").unwrap() {
.elements quote! {#parent_name}
.into_iter()
.map(|e| e.generate_register_interface(upper_camel_name.clone()))
.collect::<Result<Vec<_>, _>>()?;
let parent_struct = if parent == util::parse_to_ident("RegisterInterface").unwrap() {
quote! {#parent}
} else { } else {
quote! {#parent<'a>} quote! {#parent_name<'a>}
}; };
Ok(quote! { let child_mods = self
pub mod #snake_case_name { .elements
#[doc = #desc] .iter()
.map(|e| {
use std::marker::PhantomData; util::parse_to_ident(&e.get_name().to_snake_case()).map(|child_name| {
quote! {
use super::#parent; pub mod #child_name;
#(#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,
}
} }
})
})
.collect::<Result<Vec<_>, _>>()?;
#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_register;
mod codegen_registerspec_impl; mod codegen_registerspec_impl;
@ -242,8 +320,15 @@ mod codegen_registerspec_impl;
impl CodeGen for Register { impl CodeGen for Register {
fn generate_register_interface( fn generate_register_interface(
self, self,
parent: proc_macro2::Ident, parent_name: Option<proc_macro2::Ident>,
) -> Result<proc_macro2::TokenStream, CodeGenError> { parent_path: Option<path::PathBuf>,
mut files: HashMap<path::PathBuf, proc_macro2::TokenStream>,
) -> Result<HashMap<path::PathBuf, proc_macro2::TokenStream>, 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 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 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}"))?; let reg_name = util::parse_to_ident(&format!("Reg{upper_camel_name}"))?;
@ -263,7 +348,7 @@ impl CodeGen for Register {
type_ux, type_ux,
); );
Ok(quote! { let out = quote! {
pub mod #snake_case_name { pub mod #snake_case_name {
use std::marker::PhantomData; use std::marker::PhantomData;
@ -273,7 +358,7 @@ impl CodeGen for Register {
pub struct #reg_name<'a> { pub struct #reg_name<'a> {
mem_ptr: *mut u32, mem_ptr: *mut u32,
_marker: PhantomData<&'a mut super::#parent<'a>>, _marker: PhantomData<&'a mut super::#parent_name<'a>>,
} }
impl #reg_name<'_> { impl #reg_name<'_> {
@ -289,6 +374,13 @@ impl CodeGen for Register {
#code_t_def #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)
} }
} }

View file

@ -1,7 +1,8 @@
use std::fs; use std::{collections::HashMap, fs};
use anyhow::Result; use anyhow::Result;
use endcap_sl_software_ri_generator::{generator::CodeGen, types}; use endcap_sl_software_ri_generator::{generator::CodeGen, types};
use itertools::Itertools;
fn main() -> Result<()> { fn main() -> Result<()> {
env_logger::init(); env_logger::init();
@ -21,16 +22,13 @@ fn main() -> Result<()> {
let register_map = types::Module::from_xml_dom(doc.root_element())?; let register_map = types::Module::from_xml_dom(doc.root_element())?;
println!("read: {:#?}", register_map); println!("read: {:#?}", register_map);
// println!("{}", register_map.generate_register_interface()?); println!("===========================================");
fs::write(
"testgen.rs", let files = register_map.generate_register_interface(None, None, HashMap::new())?;
register_map println!("{:#?}", files);
.generate_register_interface(proc_macro2::Ident::new( for filepath in files.keys().sorted() {
"a", println!("{}", filepath.display());
proc_macro2::Span::call_site(), }
))?
.to_string(),
)?;
Ok(()) Ok(())
} }