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
//! 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<proc_macro2::TokenStream, CodeGenError>;
parent_name: Option<proc_macro2::Ident>,
parent_path: Option<path::PathBuf>,
files: HashMap<path::PathBuf, proc_macro2::TokenStream>,
) -> Result<HashMap<path::PathBuf, proc_macro2::TokenStream>, CodeGenError>;
}
impl CodeGen for Module {
fn generate_register_interface(
self,
_: proc_macro2::Ident,
) -> Result<proc_macro2::TokenStream, CodeGenError> {
let mut out = TokenStream::new();
_: Option<proc_macro2::Ident>,
_: Option<path::PathBuf>,
mut files: HashMap<path::PathBuf, proc_macro2::TokenStream>,
) -> std::result::Result<HashMap<path::PathBuf, proc_macro2::TokenStream>, 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()))
.collect::<Result<Vec<_>, _>>()?;
for child in child_mods {
out.extend(child);
.iter()
.map(|e| {
util::parse_to_ident(&e.get_name().to_snake_case()).map(|child_name| {
quote! {
pub mod #child_name;
}
})
})
.collect::<Result<Vec<_>, _>>()?;
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<proc_macro2::TokenStream, CodeGenError> {
parent_name: Option<proc_macro2::Ident>,
parent_path: Option<path::PathBuf>,
files: HashMap<path::PathBuf, proc_macro2::TokenStream>,
) -> Result<HashMap<path::PathBuf, proc_macro2::TokenStream>, 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<proc_macro2::TokenStream, CodeGenError> {
parent_name: Option<proc_macro2::Ident>,
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 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,27 +236,32 @@ impl CodeGen for Block {
out
};
let code_children = self
.elements
.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}
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 {
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::<Result<Vec<_>, _>>()?;
let out = quote! {
#[doc = #desc]
use std::marker::PhantomData;
use super::#parent;
use super::#parent_name;
#(#code_children)*
#(#child_mods)*
const OFFSET: usize = #addr;
@ -230,9 +280,37 @@ impl CodeGen for Block {
#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;
@ -242,8 +320,15 @@ mod codegen_registerspec_impl;
impl CodeGen for Register {
fn generate_register_interface(
self,
parent: proc_macro2::Ident,
) -> Result<proc_macro2::TokenStream, CodeGenError> {
parent_name: Option<proc_macro2::Ident>,
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 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)
}
}

View file

@ -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(())
}