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
|
//! # 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
20
src/main.rs
20
src/main.rs
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue