mirror of
https://gitlab.cern.ch/wotsubo/endcap-sl-software-ri-generator.git
synced 2025-02-23 00:57:08 +09:00
new(generator): separate modules for each blocks (not yet implemented file io)
This commit is contained in:
parent
cb67f9648b
commit
fd106e3355
2 changed files with 159 additions and 69 deletions
208
src/generator.rs
208
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<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()))
|
||||
.iter()
|
||||
.map(|e| {
|
||||
util::parse_to_ident(&e.get_name().to_snake_case()).map(|child_name| {
|
||||
quote! {
|
||||
pub mod #child_name;
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
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<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,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::<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 {
|
||||
#[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::<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_registerspec_impl;
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
20
src/main.rs
20
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(())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue