From dfd28782dd0fb2297cac96453ca974365f3ea522 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Fri, 31 Jan 2025 02:17:48 +0900 Subject: [PATCH 01/45] new(generator): top level module --- Cargo.lock | 2 ++ Cargo.toml | 2 ++ src/converter.rs | 2 +- src/generator.rs | 84 ++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/main.rs | 4 ++- src/types.rs | 2 ++ 7 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 src/generator.rs diff --git a/Cargo.lock b/Cargo.lock index dfaf1ae..b826560 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,6 +80,8 @@ dependencies = [ "anyhow", "env_logger", "log", + "proc-macro2", + "quote", "roxmltree", "thiserror", ] diff --git a/Cargo.toml b/Cargo.toml index 8a02725..b5b5d77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,5 +15,7 @@ path = "src/lib.rs" anyhow = "1.0.95" env_logger = "0.11.6" log = "0.4" +proc-macro2 = "1.0.93" +quote = "1.0" roxmltree = "0.20" thiserror = "2.0" diff --git a/src/converter.rs b/src/converter.rs index c9fcabe..5483282 100644 --- a/src/converter.rs +++ b/src/converter.rs @@ -1,4 +1,4 @@ -//! Convert DOM to register interface, complementing optional parameters. +//! Convert DOM to register interface defined in [`crate::types`], complementing optional parameters. use std::{num, str}; diff --git a/src/generator.rs b/src/generator.rs new file mode 100644 index 0000000..8be3b60 --- /dev/null +++ b/src/generator.rs @@ -0,0 +1,84 @@ +//! Generate register interface rust code from types in [`crate::types`]. + +use crate::types::{Block, Module, ModuleBlockElements}; +use proc_macro2::TokenStream; +use quote::quote; + +pub trait CodeGen { + fn generate_register_interface(self) -> proc_macro2::TokenStream; +} + +impl CodeGen for Module { + fn generate_register_interface(self) -> proc_macro2::TokenStream { + let mut out = TokenStream::new(); + if !self.elements_bitstring.is_empty() { + todo!("bitstring generation is not yet implemented") + } + + let child_mods = self + .elements_other + .into_iter() + .map(|e| e.generate_register_interface()); + for child in child_mods { + out.extend(child); + } + + out + } +} + +impl CodeGen for ModuleBlockElements { + fn generate_register_interface(self) -> proc_macro2::TokenStream { + match self { + ModuleBlockElements::Block(block) => block.generate_register_interface(), + ModuleBlockElements::Register(register) => todo!(), + ModuleBlockElements::Memory(memory) => todo!(), + ModuleBlockElements::Fifo(fifo) => todo!(), + } + } +} + +impl CodeGen for Block { + fn generate_register_interface(self) -> proc_macro2::TokenStream { + let name = self.name; + let desc = self.desc.unwrap_or("".to_string()); + + quote! { + pub mod #name { + #[doc = #desc] + + use std::marker::PhantomData; + + use super::RegisterInterface; + + pub mod control; + pub mod status; + + pub(crate) const SLR0_OFFSET: usize = 0x0000; + pub(crate) const SLR0_SIZE: usize = 0x4000; + + /// Access to SLR0. + pub struct Slr0<'a> { + mem_ptr: *mut u32, + _marker: PhantomData<&'a mut RegisterInterface>, + } + + impl Slr0<'_> { + pub(crate) fn new(ptr: *mut u32) -> Self { + Slr0 { + mem_ptr: ptr, + _marker: PhantomData, + } + } + pub fn control(&self) -> control::Control { + control::Control::new(unsafe { self.mem_ptr.add(control::OFFSET) }) + } + + pub fn status(&self) -> status::Status { + status::Status::new(unsafe { self.mem_ptr.add(status::OFFSET) }) + } + } + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 477c59e..0d7723e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,3 +3,4 @@ pub mod types; pub mod parser; pub mod converter; +pub mod generator; diff --git a/src/main.rs b/src/main.rs index b042c79..79d45c4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ use std::fs; use anyhow::Result; -use endcap_sl_software_ri_generator::types; +use endcap_sl_software_ri_generator::{generator::CodeGen, types}; fn main() -> Result<()> { env_logger::init(); @@ -21,5 +21,7 @@ fn main() -> Result<()> { let register_map = types::Module::from_xml_dom(doc.root_element())?; println!("read: {:#?}", register_map); + println!("{}", register_map.generate_register_interface()); + Ok(()) } diff --git a/src/types.rs b/src/types.rs index fa4ba30..c3bfd3f 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,3 +1,5 @@ +//! Register type definition. + #[derive(Debug)] pub struct Module { pub name: String, From 6b5b5e02345104fd701c4560cd67f761c2ebc088 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Fri, 31 Jan 2025 03:26:42 +0900 Subject: [PATCH 02/45] new(CodeGen): for Block --- Cargo.lock | 7 +++++ Cargo.toml | 1 + src/generator.rs | 76 +++++++++++++++++++++++++++++++--------------- src/lib.rs | 1 + src/type_traits.rs | 30 ++++++++++++++++++ 5 files changed, 90 insertions(+), 25 deletions(-) create mode 100644 src/type_traits.rs diff --git a/Cargo.lock b/Cargo.lock index b826560..5ef333e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,6 +79,7 @@ version = "0.1.0" dependencies = [ "anyhow", "env_logger", + "heck", "log", "proc-macro2", "quote", @@ -109,6 +110,12 @@ dependencies = [ "log", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "humantime" version = "2.1.0" diff --git a/Cargo.toml b/Cargo.toml index b5b5d77..6328c5f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ path = "src/lib.rs" [dependencies] anyhow = "1.0.95" env_logger = "0.11.6" +heck = "0.5" log = "0.4" proc-macro2 = "1.0.93" quote = "1.0" diff --git a/src/generator.rs b/src/generator.rs index 8be3b60..47ebd86 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -1,6 +1,10 @@ //! Generate register interface rust code from types in [`crate::types`]. -use crate::types::{Block, Module, ModuleBlockElements}; +use crate::{ + type_traits::GetName, + types::{Block, Module, ModuleBlockElements}, +}; +use heck::{ToSnakeCase, ToUpperCamelCase}; use proc_macro2::TokenStream; use quote::quote; @@ -40,43 +44,65 @@ impl CodeGen for ModuleBlockElements { impl CodeGen for Block { fn generate_register_interface(self) -> proc_macro2::TokenStream { - let name = self.name; + let snake_case_name = self.name.to_snake_case(); + let upper_camel_name = self.name.to_upper_camel_case(); + let addr = format!("0x{:x}", self.addr); let desc = self.desc.unwrap_or("".to_string()); + let accessor_methods = { + let mut out = TokenStream::new(); + for child_name in self.elements.iter().map(|e| e.get_name()) { + let snake_case_name = child_name.to_snake_case(); + let upper_camel_name = child_name.to_upper_camel_case(); + + out.extend(quote! { + pub fn #snake_case_name(&self) -> #snake_case_name::#upper_camel_name { + #snake_case_name::#upper_camel_name::new(self.mem_ptr) + } + }); + } + + out + }; + + let child_mods = { + let mut out = TokenStream::new(); + for child_mod in self.elements.into_iter() { + let module = child_mod.generate_register_interface(); + out.extend(module); + } + + out + }; + quote! { - pub mod #name { + pub mod #snake_case_name { #[doc = #desc] - + use std::marker::PhantomData; - + use super::RegisterInterface; - - pub mod control; - pub mod status; - - pub(crate) const SLR0_OFFSET: usize = 0x0000; - pub(crate) const SLR0_SIZE: usize = 0x4000; - + + #child_mods + + const OFFSET: usize = #addr; + //pub(crate) const SIZE: usize = 0x4000; + /// Access to SLR0. - pub struct Slr0<'a> { + pub struct #upper_camel_name<'a> { mem_ptr: *mut u32, _marker: PhantomData<&'a mut RegisterInterface>, } - - impl Slr0<'_> { - pub(crate) fn new(ptr: *mut u32) -> Self { - Slr0 { - mem_ptr: ptr, + + 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, } } - pub fn control(&self) -> control::Control { - control::Control::new(unsafe { self.mem_ptr.add(control::OFFSET) }) - } - - pub fn status(&self) -> status::Status { - status::Status::new(unsafe { self.mem_ptr.add(status::OFFSET) }) - } + + #accessor_methods } } } diff --git a/src/lib.rs b/src/lib.rs index 0d7723e..863641c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ //! Root document [`types::Module::from_xml_dom`] pub mod types; +pub mod type_traits; pub mod parser; pub mod converter; pub mod generator; diff --git a/src/type_traits.rs b/src/type_traits.rs new file mode 100644 index 0000000..a31512f --- /dev/null +++ b/src/type_traits.rs @@ -0,0 +1,30 @@ +//! Util traits to get info from types in [`crate::types`]. + +use crate::types::{Block, ModuleBlockElements, Register}; + +pub(crate) trait GetName { + fn get_name(&self) -> String; +} + +impl GetName for ModuleBlockElements { + fn get_name(&self) -> String { + match self { + ModuleBlockElements::Block(block) => block.get_name(), + ModuleBlockElements::Register(register) => register.get_name(), + ModuleBlockElements::Memory(memory) => todo!(), + ModuleBlockElements::Fifo(fifo) => todo!(), + } + } +} + +impl GetName for Block { + fn get_name(&self) -> String { + self.name.clone() + } +} + +impl GetName for Register { + fn get_name(&self) -> String { + self.name.clone() + } +} From 8e0761e39ed2234a53c5be53aa70c0305f474a2a Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Fri, 31 Jan 2025 12:02:25 +0900 Subject: [PATCH 03/45] update(converter): add validation for register about mask & field ref: ea1850e csr commit --- src/converter.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/converter.rs b/src/converter.rs index 5483282..0334bb7 100644 --- a/src/converter.rs +++ b/src/converter.rs @@ -352,12 +352,21 @@ impl Register { .transpose()?; let desc = node.attribute("desc").map(str::to_string); - let children = node + let children: Vec<_> = node .children() .filter(|node| node.is_element() && node.tag_name().name().eq("field")) .map(Field::from_xml_dom) .collect::>()?; + // Validation + if mask.is_some() && !children.is_empty() { + return Err(DomConversionError::OtherError(format!( + "both mask and field are used in the same register: {} - {}", + node.document().text_pos_at(node.range().start), + node.document().text_pos_at(node.range().end), + ))); + } + Ok(Register { name, addr, @@ -424,6 +433,7 @@ impl Field { .map(Value::from_xml_dom) .collect::>()?; + // Validation if let Some(default) = default { if default & !(mask) != 0 { log::warn!( From eec855c3a1866945f7c47c656d33db56b97b7493 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Sat, 1 Feb 2025 19:30:39 +0900 Subject: [PATCH 04/45] update(Register): add type field and its completion to converter --- src/converter.rs | 36 ++++++++++++++++++++++++++++++++++++ src/types.rs | 4 +++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/converter.rs b/src/converter.rs index 0334bb7..4d0c964 100644 --- a/src/converter.rs +++ b/src/converter.rs @@ -1,4 +1,8 @@ //! Convert DOM to register interface defined in [`crate::types`], complementing optional parameters. +//! +//! root: [`Module::from_xml_dom`] +//! +//! error: [`DomConversionError`] use std::{num, str}; @@ -11,6 +15,7 @@ use crate::types::{ Value, }; +/// Possible errors in conversion, with positional information. #[derive(Debug, Error, PartialEq)] pub enum DomConversionError { #[error("attribute {attr} not found in element: {start} - {end}", start = pos.0, end = pos.1)] @@ -323,6 +328,18 @@ impl Register { .map_err(|e| DomConversionError::parse_prefixed_u32_error(e, "addr", node))?, None => 0, }; + let r#type = util::get_type(node) + .transpose()? + .map_or_else( + || { + node.ancestors() + .filter_map(util::get_type) + .next() + .transpose() + }, + |x| Ok(Some(x)), + )? + .ok_or_else(|| DomConversionError::parameter_completion_error("type", node))?; let mask = node .attribute("mask") .map(|addr| { @@ -366,10 +383,29 @@ impl Register { node.document().text_pos_at(node.range().end), ))); } + if default.is_some() && !children.is_empty() { + return Err(DomConversionError::OtherError(format!( + "both default and field are used in the same register: {} - {}", + node.document().text_pos_at(node.range().start), + node.document().text_pos_at(node.range().end), + ))); + } + if let (Some(mask), Some(default)) = (mask, default) { + if default & !(mask) != 0 { + log::warn!( + "default value {} doesn't fit mask {}: {} - {}", + default, + mask, + node.document().text_pos_at(node.range().start), + node.document().text_pos_at(node.range().end) + ) + } + } Ok(Register { name, addr, + r#type, mask, modf, multiple, diff --git a/src/types.rs b/src/types.rs index c3bfd3f..4bc205e 100644 --- a/src/types.rs +++ b/src/types.rs @@ -53,6 +53,8 @@ pub struct Register { pub name: String, /// Fill this with proper calc. pub addr: u32, + /// Fill this with proper calc. + pub r#type: DataType, pub mask: Option, /// Fill this with proper calc. pub modf: RwSpecifier, @@ -111,7 +113,7 @@ pub struct Field { pub struct Value { pub name: String, pub data: u32, - pub desc: Option + pub desc: Option, } #[derive(Debug, PartialEq)] From d104e62bf3c84480d9ed9bf68bf9ec66f6107b3a Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Sat, 1 Feb 2025 19:33:51 +0900 Subject: [PATCH 05/45] new(generator): implement register codegen (codegen_register) & fix interpolation bug also added a lot of docs, since register codegen is quite complex (This became too large commit...) --- Cargo.lock | 17 ++ Cargo.toml | 2 + src/generator.rs | 204 ++++++++++++++- src/generator/codegen_register.rs | 412 ++++++++++++++++++++++++++++++ src/lib.rs | 7 + src/main.rs | 2 +- src/parser.rs | 2 + src/types.rs | 2 +- 8 files changed, 633 insertions(+), 15 deletions(-) create mode 100644 src/generator/codegen_register.rs diff --git a/Cargo.lock b/Cargo.lock index 5ef333e..c18c3b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,6 +73,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "endcap-sl-software-ri-generator" version = "0.1.0" @@ -80,10 +86,12 @@ dependencies = [ "anyhow", "env_logger", "heck", + "itertools", "log", "proc-macro2", "quote", "roxmltree", + "syn", "thiserror", ] @@ -128,6 +136,15 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "log" version = "0.4.25" diff --git a/Cargo.toml b/Cargo.toml index 6328c5f..755bd1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,8 +15,10 @@ path = "src/lib.rs" anyhow = "1.0.95" env_logger = "0.11.6" heck = "0.5" +itertools = "0.14" log = "0.4" proc-macro2 = "1.0.93" quote = "1.0" roxmltree = "0.20" +syn = "2.0.96" thiserror = "2.0" diff --git a/src/generator.rs b/src/generator.rs index 47ebd86..d5deb1d 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -1,19 +1,126 @@ //! Generate register interface rust code from types in [`crate::types`]. +//! +//! root: [`CodeGen`] use crate::{ type_traits::GetName, - types::{Block, Module, ModuleBlockElements}, + types::{Block, Module, ModuleBlockElements, Register}, }; use heck::{ToSnakeCase, ToUpperCamelCase}; use proc_macro2::TokenStream; use quote::quote; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum CodeGenError { + #[error("tokenization error: {0}")] + TokenizeError(#[from] syn::Error), +} + +mod util { + use crate::types::DataType; + + use super::CodeGenError; + + pub(super) fn parse_to_ident(s: &str) -> Result { + Ok(syn::parse_str(s)?) + } + + pub(super) fn parse_to_literal(s: &str) -> Result { + Ok(syn::parse_str(s)?) + } + + // currently only U32 is used, so `dead_code` for Debug, PartialEq + #[allow(dead_code)] + #[derive(Debug, PartialEq)] + pub(crate) enum RustUxTypes { + Bool, + U8, + U16, + U32, + } + + impl RustUxTypes { + /// Derive appropriate rust types for `mask`ed value. + pub(super) fn from_mask(mask: u32) -> RustUxTypes { + match 32 - mask.leading_zeros() - mask.trailing_zeros() { + 0 => panic!("mask cannot be 0"), + 1 => RustUxTypes::Bool, + x if 1 < x && x <= 8 => RustUxTypes::U8, + x if 8 < x && x <= 16 => RustUxTypes::U16, + x if 16 < x && x <= 32 => RustUxTypes::U32, + _ => panic!("supposed not to be reachable"), + } + } + + pub(super) fn to_rust_type_token(&self) -> proc_macro2::Ident { + match self { + RustUxTypes::Bool => parse_to_ident("bool").unwrap(), + RustUxTypes::U8 => parse_to_ident("u8").unwrap(), + RustUxTypes::U16 => parse_to_ident("u16").unwrap(), + RustUxTypes::U32 => parse_to_ident("u32").unwrap(), + } + } + } + + impl From<&DataType> for RustUxTypes { + fn from(value: &DataType) -> Self { + match value { + DataType::D32 => Self::U32, + } + } + } + + impl DataType { + fn to_rust_type_token(&self) -> proc_macro2::Ident { + match self { + DataType::D32 => proc_macro2::Ident::new("u32", proc_macro2::Span::call_site()), + } + } + } + + #[cfg(test)] + mod test { + use super::RustUxTypes; + + #[test] + fn rustuxtypes_from_mask() { + assert_eq!(RustUxTypes::from_mask(0x1), RustUxTypes::Bool); + assert_eq!(RustUxTypes::from_mask(0x20), RustUxTypes::Bool); + assert_eq!(RustUxTypes::from_mask(0x4000), RustUxTypes::Bool); + assert_eq!(RustUxTypes::from_mask(0x80_0000), RustUxTypes::Bool); + assert_eq!(RustUxTypes::from_mask(0x100_0000), RustUxTypes::Bool); + + assert_eq!(RustUxTypes::from_mask(0x0300_0000), RustUxTypes::U8); + assert_eq!(RustUxTypes::from_mask(0x0000_01e0), RustUxTypes::U8); + + assert_eq!(RustUxTypes::from_mask(0x0000_01f0), RustUxTypes::U8); + assert_eq!(RustUxTypes::from_mask(0x000f_f000), RustUxTypes::U8); + + assert_eq!(RustUxTypes::from_mask(0x0fff_0000), RustUxTypes::U16); + assert_eq!(RustUxTypes::from_mask(0x0f0f_0000), RustUxTypes::U16); + assert_eq!(RustUxTypes::from_mask(0x010f_8000), RustUxTypes::U16); + + assert_eq!(RustUxTypes::from_mask(0xffff_f000), RustUxTypes::U32); + assert_eq!(RustUxTypes::from_mask(0x1fff_ff00), RustUxTypes::U32); + } + + #[test] + fn rustuxtypes_to_token() { + assert_eq!( + RustUxTypes::U8.to_rust_type_token(), + proc_macro2::Ident::new("u8", proc_macro2::Span::call_site()) + ) + } + } +} pub trait CodeGen { - fn generate_register_interface(self) -> proc_macro2::TokenStream; + fn generate_register_interface(self) -> Result; } impl CodeGen for Module { - fn generate_register_interface(self) -> proc_macro2::TokenStream { + fn generate_register_interface(self) -> Result { let mut out = TokenStream::new(); if !self.elements_bitstring.is_empty() { todo!("bitstring generation is not yet implemented") @@ -27,15 +134,15 @@ impl CodeGen for Module { out.extend(child); } - out + Ok(out) } } impl CodeGen for ModuleBlockElements { - fn generate_register_interface(self) -> proc_macro2::TokenStream { + fn generate_register_interface(self) -> Result { match self { ModuleBlockElements::Block(block) => block.generate_register_interface(), - ModuleBlockElements::Register(register) => todo!(), + ModuleBlockElements::Register(register) => register.generate_register_interface(), ModuleBlockElements::Memory(memory) => todo!(), ModuleBlockElements::Fifo(fifo) => todo!(), } @@ -43,10 +150,10 @@ impl CodeGen for ModuleBlockElements { } impl CodeGen for Block { - fn generate_register_interface(self) -> proc_macro2::TokenStream { - let snake_case_name = self.name.to_snake_case(); - let upper_camel_name = self.name.to_upper_camel_case(); - let addr = format!("0x{:x}", self.addr); + fn generate_register_interface(self) -> Result { + 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))?; let desc = self.desc.unwrap_or("".to_string()); let accessor_methods = { @@ -64,7 +171,7 @@ impl CodeGen for Block { out }; - + let child_mods = { let mut out = TokenStream::new(); for child_mod in self.elements.into_iter() { @@ -75,7 +182,7 @@ impl CodeGen for Block { out }; - quote! { + Ok(quote! { pub mod #snake_case_name { #[doc = #desc] @@ -105,6 +212,77 @@ impl CodeGen for Block { #accessor_methods } } - } + }) + } +} + +mod codegen_register; + +impl CodeGen for Register { + fn generate_register_interface(self) -> Result { + 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}"))?; + let addr = util::parse_to_literal(&format!("0x{:x}", self.addr))?; + + let (code_t_def, type_t, type_ux): ( + proc_macro2::TokenStream, + proc_macro2::Ident, + proc_macro2::Ident, + ) = codegen_register::reg_type_def(&self, &upper_camel_name)?; + + let code_reg_def: proc_macro2::TokenStream = todo!(); + + Ok(quote! { + pub mod #snake_case_name { + use std::marker::PhantomData; + + use crate::register_spec::{DataConversionError, Modifiable, Readable, RegisterSpec, Writable}; + + const OFFSET: usize = #addr; + + pub struct #reg_name<'a> { + mem_ptr: *mut u32, + _marker: PhantomData<&'a mut super::Debug<'a>>, + } + + impl #reg_name<'_> { + pub(crate) fn new(parent_ptr: *mut u32) -> Self { + #reg_name { + mem_ptr: unsafe { parent_ptr.add(OFFSET) }, + _marker: PhantomData, + } + } + } + + impl RegisterSpec for #reg_name<'_> { + type Ux = u32; + type T = SlId; + + fn as_ptr(&self) -> *mut Self::Ux { + self.mem_ptr + } + } + impl Readable for #reg_name<'_> {} + impl Writable for #reg_name<'_> {} + impl Modifiable for #reg_name<'_> {} + + #[derive(Debug, Clone, Copy, Default)] + pub struct SlId(pub u32); + impl TryFrom for SlId { + type Error = DataConversionError; + + fn try_from(value: u32) -> Result { + let mask = 0x0000003f; + Ok(SlId(value & mask)) + } + } + impl From for u32 { + fn from(val: SlId) -> Self { + val.0 + } + } + } + }) } } diff --git a/src/generator/codegen_register.rs b/src/generator/codegen_register.rs new file mode 100644 index 0000000..db5deee --- /dev/null +++ b/src/generator/codegen_register.rs @@ -0,0 +1,412 @@ +//! Generator for [`Register`]. +//! The entry point is [`reg_type_def`]. + +use heck::{ToShoutySnekCase, ToSnakeCase}; +use itertools::Itertools; +use proc_macro2::{Ident, TokenStream}; +use quote::quote; + +use crate::types::{DataType, Field, MultipleParams, Register}; + +use super::{ + util::{self, RustUxTypes}, + CodeGenError, +}; + +/// Generate underlying `T` and its implementation for `RegisterSpec`. +/// +/// # Returns +/// tuple of +/// - code of `T` definition and its impls +/// - [`Ident`] of `T` +/// - [`Ident`] of `Ux` +/// +/// # Cases +/// - `T` == `Ux` => [`reg_type_def_simple`] +/// - `T` is masked `Ux` => [`reg_type_def_masked`] +/// - `T` has 1+ fields => [`reg_type_def_with_field`] +pub(super) fn reg_type_def( + reg: &Register, + upper_camel_name: &Ident, +) -> Result<(TokenStream, Ident, Ident), CodeGenError> { + Ok(match reg.elements.is_empty() { + true => match reg.mask { + Some(mask) => reg_type_def_masked(®.r#type, mask, upper_camel_name), + None => reg_type_def_simple(®.r#type), + }, + false => reg_type_def_with_field(®.r#type, ®.elements, upper_camel_name)?, + }) +} + +/// Where `T` == `Ux`. +/// +/// No `T` def nor impl are required. +/// +/// # Returns +/// same as [`reg_type_def`] +fn reg_type_def_simple(basetype: &DataType) -> (TokenStream, Ident, Ident) { + let type_t_ux = { + let x: RustUxTypes = basetype.into(); + x.to_rust_type_token() + }; + + let out = quote! {}; + (out, type_t_ux.clone(), type_t_ux) +} + +/// Where `T` is masked `Ux`. +/// +/// `T` is a one-length "tuple struct" `T(pub Ux)`. +/// `impl TryFrom for T` and `impl From for Ux` are included, +/// but without `impl T`. +/// +/// # Returns +/// same as [`reg_type_def`] +fn reg_type_def_masked( + basetype: &DataType, + mask: u32, + upper_camel_name: &Ident, +) -> (TokenStream, Ident, Ident) { + let type_ux = { + let x: RustUxTypes = basetype.into(); + x.to_rust_type_token() + }; + + let out = quote! { + #[derive(Debug, Clone, Copy, Default)] + pub struct #upper_camel_name(pub #type_ux); + impl TryFrom<#type_ux> for #upper_camel_name { + type Error = DataConversionError<#type_ux, Self>; + + fn try_from(value: #type_ux) -> Result { + let mask = #mask; + Ok(SlId(value & mask)) + } + } + impl From<#upper_camel_name> for #type_ux { + fn from(value: #upper_camel_name) -> Self { + value.0 + } + } + + }; + + (out, upper_camel_name.clone(), type_ux) +} + +/// Where `T` has fields. +/// +/// `T` is a "struct struct" which has a single field `inner: Ux`. +/// Also, a bunch of "mask" constants and getter/setter are defined for each fields. +/// +/// # Returns +/// same as [`reg_type_def`] +/// +/// # Field generation +/// This function calls [`generate_field`] for each [`Field`] entries to generate field definitions. +/// See the doc of `generate_field` for more detail +fn reg_type_def_with_field( + basetype: &DataType, + fields: &[Field], + upper_camel_name: &Ident, +) -> Result<(TokenStream, Ident, Ident), CodeGenError> { + let type_ux = { + let x: RustUxTypes = basetype.into(); + x.to_rust_type_token() + }; + + let (code_masks, code_getters, code_setters): ( + Vec, + Vec, + Vec, + ) = fields + .iter() + .map(|field| generate_field(field, basetype)) + .process_results(|iter| iter.multiunzip())?; + + let out = quote! { + #(#code_masks)* + + #[derive(Debug, Clone, Copy, Default)] + pub struct #upper_camel_name { + inner: #type_ux, + } + + impl GtyDelayBank123 { + #(#code_getters)* + + #(#code_setters)* + } + impl TryFrom<#type_ux> for #upper_camel_name { + type Error = DataConversionError<#type_ux, Self>; + + fn try_from(value: #type_ux) -> Result { + Ok(Self { inner: value }) + } + } + impl From for u32 { + fn from(value: #upper_camel_name) -> Self { + value.inner + } + } + }; + + Ok((out, upper_camel_name.clone(), type_ux)) +} + +/// Generate code for each field, which consists of these three. +/// 1. mask definition +/// 2. getter method +/// 3. setter method +/// +/// # Cases +/// This function is separated into two cases. +/// See each function docs for more detail. +/// +/// 1. field is not "multiple" => [`generate_single_field`] +/// 2. field is "multiple" => [`generate_multiple_field`] +fn generate_field( + field: &Field, + basetype: &DataType, +) -> Result<(TokenStream, TokenStream, TokenStream), CodeGenError> { + let mask_name = util::parse_to_ident(&format!("{}_MASK", field.name.TO_SHOUTY_SNEK_CASE()))?; + let base_type = util::RustUxTypes::from(basetype).to_rust_type_token(); + + let snake_case_name = util::parse_to_ident(&field.name.to_snake_case())?; + let field_type = util::RustUxTypes::from_mask(field.mask); + + let (code_mask, code_getter, code_setter) = match &field.multiple { + Some(multiple_params) => generate_multiple_field( + mask_name, + base_type, + field.mask, + field_type, + snake_case_name, + multiple_params, + ), + None => generate_single_field( + mask_name, + base_type, + field.mask, + field_type, + snake_case_name, + ), + }; + + Ok((code_mask, code_getter, code_setter)) +} + +/// Generate "single" field definition (mask, getter, setter). +/// +/// # Cases +/// This function is separated into two cases. +/// +/// - field is `bool` => [`generate_single_bool_field`] +/// - field is `u8`/`u16`/`u32` => [`generate_single_ux_field`] +/// +/// For the details of types of register/field, see [`DataType`]. +/// Note that in both cases, mask definitions are the same. +fn generate_single_field( + mask_name: Ident, + base_type: Ident, + mask_val: u32, + field_type: RustUxTypes, + snake_case_name: Ident, +) -> (TokenStream, TokenStream, TokenStream) { + let mask_val = util::parse_to_literal(&format!("0x{:x}", mask_val)).unwrap(); + let code_mask = quote! { + const #mask_name: #base_type = #mask_val; + }; + let (code_getter, code_setter) = match field_type { + RustUxTypes::Bool => generate_single_bool_field(mask_name, snake_case_name), + RustUxTypes::U8 | RustUxTypes::U16 | RustUxTypes::U32 => { + generate_single_ux_field(mask_name, base_type, snake_case_name, field_type) + } + }; + + (code_mask, code_getter, code_setter) +} + +/// Generate bool "single" field definition (getter, setter). +fn generate_single_bool_field( + mask_name: Ident, + snake_case_name: Ident, +) -> (TokenStream, TokenStream) { + let setter_name = util::parse_to_ident(&format!("set_{}", snake_case_name)).unwrap(); + let code_getter = quote! { + pub fn #snake_case_name(&self) -> bool { + (self.inner & #mask_name) == #mask_name + } + }; + let code_setter = quote! { + pub fn #setter_name(self, val: bool) -> Self { + let mut inner = self.inner; + if val { + inner |= #mask_name + } else { + inner &= !#mask_name + }; + Self { inner } + } + }; + (code_getter, code_setter) +} + +/// Generate u8/u16/u32 "single" field definition (getter, setter). +fn generate_single_ux_field( + mask_name: Ident, + base_type: Ident, + snake_case_name: Ident, + field_type: RustUxTypes, +) -> (TokenStream, TokenStream) { + let field_type = field_type.to_rust_type_token(); + let setter_name = util::parse_to_ident(&format!("set_{}", snake_case_name)).unwrap(); + + let code_getter = quote! { + pub fn #snake_case_name(&self) -> #field_type { + const RIGHT_SHIFT: #base_type = #mask_name.trailing_zeros(); + ((self.inner & #mask_name) >> RIGHT_SHIFT) + .try_into() + .unwrap() + } + }; + let code_setter = quote! { + pub fn #setter_name(&self, val: #field_type) -> Self { + let update: #base_type = val & #mask_name; + let mut inner = self.inner; + inner &= !mask; + inner |= update; + Self { inner } + } + }; + (code_getter, code_setter) +} + +/// Generate "multiple" field definition (mask, getter, setter). +/// +/// # Cases +/// As "single" field cases, this function is separated into two cases; +/// +/// - fields are `bool` => [`generate_multiple_bool_field`] +/// - fields are `u8`/`u16`/`u32` => todo +fn generate_multiple_field( + mask_name: Ident, + base_type: Ident, + single_mask_val: u32, + single_field_type: RustUxTypes, + snake_case_name: Ident, + multiple_params: &MultipleParams, +) -> (TokenStream, TokenStream, TokenStream) { + let num_multiple = multiple_params.multiple; + let id_num_multiple = util::parse_to_ident(&num_multiple.to_string()).unwrap(); + let id_field_type = single_field_type.to_rust_type_token(); + let masks: Vec<_> = (0..multiple_params.multiple) + .map(|x| x * multiple_params.offset) + .map(|offset| single_mask_val << offset) + .map(|mask| util::parse_to_ident(&format!("0x{mask:x}")).unwrap()) + .collect(); + debug_assert_eq!(masks.len(), num_multiple.try_into().unwrap()); + let code_mask = quote! { + const #mask_name: [#id_field_type; #id_num_multiple] = [#(#masks),*]; + }; + + let (code_getter, code_setter) = match single_field_type { + RustUxTypes::Bool => { + generate_multiple_bool_field(mask_name, base_type, snake_case_name, masks.clone()) + } + RustUxTypes::U8 | RustUxTypes::U16 | RustUxTypes::U32 => generate_multiple_ux_field( + mask_name, + base_type, + snake_case_name, + single_field_type, + masks, + ), + }; + + (code_mask, code_getter, code_setter) +} + +/// Generate bool "multiple" field definition (getter, setter). +fn generate_multiple_bool_field( + mask_name: Ident, + base_type: Ident, + snake_case_name: Ident, + masks: Vec, +) -> (TokenStream, TokenStream) { + let num_multiple = masks.len(); + let elem_getter = masks.iter().enumerate().map(|(i, _mask)| { + quote! { + (self.inner & #mask_name[#i]) == #mask_name[#i] + } + }); + let code_getter = quote! { + pub fn #snake_case_name(&self) -> [bool; #num_multiple] { + [ + #(#elem_getter),* + ] + } + }; + + let setter_name = util::parse_to_ident(&format!("set_{}", snake_case_name)).unwrap(); + let code_setter = quote! { + pub fn #setter_name(self, val: [bool; #num_multiple]) -> Self { + let mask: #base_type = #mask_name.iter().sum(); + let update: #base_type = BANK121_GTY_CHANNEL_MASK + .iter() + .zip(val) + .filter_map(|(mask, val)| val.then_some(mask)) + .sum(); + let mut inner = self.inner; + inner &= !mask; + inner |= update; + Self { inner } + } + }; + + (code_getter, code_setter) +} + +/// Generate u8/u16/u32 "multiple" field definition (getter, setter). +fn generate_multiple_ux_field( + mask_name: Ident, + base_type: Ident, + snake_case_name: Ident, + single_field_type: RustUxTypes, + masks: Vec, +) -> (TokenStream, TokenStream) { + let field_type = single_field_type.to_rust_type_token(); + let num_multiple = masks.len(); + let elem_getter = masks.iter().enumerate().map(|(i, _mask)| { + quote! { + ((self.inner & #mask_name[#i]) + >> (#mask_name[#i].trailing_zeros())) + .try_into() + .unwrap() + } + }); + let code_getter = quote! { + pub fn #snake_case_name(&self) -> [#field_type; #num_multiple] { + [ + #(#elem_getter),* + ] + } + }; + + let setter_name = util::parse_to_ident(&format!("set_{}", snake_case_name)).unwrap(); + let code_setter = quote! { + pub fn #setter_name(&self, val: [#field_type; #num_multiple]) -> Self { + let mask: #base_type = #mask_name.iter().sum(); + let update: #base_type = #mask_name + .iter() + .zip(val) + .map(|(mask, val)| (#base_type::from(val)) << (mask.trailing_zeros())) + .sum(); + let mut inner = self.inner; + inner &= !mask; + inner |= update; + Self { inner } + } + }; + + (code_getter, code_setter) +} diff --git a/src/lib.rs b/src/lib.rs index 863641c..3c050d7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,11 @@ //! Root document [`types::Module::from_xml_dom`] +//! +//! # Overview +//! +//! 1. Convert [`roxmltree::Document`] to register map respresented with types defined in +//! [`types`], filling missing parameters. See [`converter`]. +//! 2. Generate [`proc_macro2::TokenStream`] from register map produced in the previous step. See +//! [`generator`]. pub mod types; pub mod type_traits; diff --git a/src/main.rs b/src/main.rs index 79d45c4..170859b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,7 +21,7 @@ fn main() -> Result<()> { let register_map = types::Module::from_xml_dom(doc.root_element())?; println!("read: {:#?}", register_map); - println!("{}", register_map.generate_register_interface()); + println!("{}", register_map.generate_register_interface()?); Ok(()) } diff --git a/src/parser.rs b/src/parser.rs index 41882ef..4924fd7 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,4 +1,6 @@ //! Module for converting XML custom token string to rust types. +//! +//! Unlike, [`crate::converter`], this only has string "parser". use std::any; diff --git a/src/types.rs b/src/types.rs index 4bc205e..65cc783 100644 --- a/src/types.rs +++ b/src/types.rs @@ -126,7 +126,7 @@ pub enum AmodValues { USER2, } -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub enum DataType { D32, } From e147bc513df9e57fa3de2e25fb0572e678ac943a Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Sat, 1 Feb 2025 19:41:12 +0900 Subject: [PATCH 06/45] refactor: add docs on modules overview to run rustfmt --- src/lib.rs | 13 ++++++++++--- src/parser.rs | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 3c050d7..49b6578 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,9 +6,16 @@ //! [`types`], filling missing parameters. See [`converter`]. //! 2. Generate [`proc_macro2::TokenStream`] from register map produced in the previous step. See //! [`generator`]. +//! +//! # modules +//! - [`types`]: type definitions of internal register map representation +//! - [`type_traits`]: supplemental traits for types in [`types`] +//! - [`parser`]: supplemental parser (string to custom types) +//! - [`converter`]: DOM to internal representation +//! - [`generator`]: internal representation to rust code -pub mod types; -pub mod type_traits; -pub mod parser; pub mod converter; pub mod generator; +pub mod parser; +pub mod type_traits; +pub mod types; diff --git a/src/parser.rs b/src/parser.rs index 4924fd7..87e86f9 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,5 +1,5 @@ //! Module for converting XML custom token string to rust types. -//! +//! //! Unlike, [`crate::converter`], this only has string "parser". use std::any; From a87e81ad5b4c4d78adb4d4d79385988a48a81ab2 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Sat, 1 Feb 2025 19:54:34 +0900 Subject: [PATCH 07/45] fix: changed CodeGen sig is not reflected to callers Note: TokenStream::extend accepts `Result`s, so be careful --- src/generator.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/generator.rs b/src/generator.rs index d5deb1d..4e4cfdd 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -129,7 +129,8 @@ impl CodeGen for Module { let child_mods = self .elements_other .into_iter() - .map(|e| e.generate_register_interface()); + .map(|e| e.generate_register_interface()) + .collect::, _>>()?; for child in child_mods { out.extend(child); } @@ -172,15 +173,11 @@ impl CodeGen for Block { out }; - let child_mods = { - let mut out = TokenStream::new(); - for child_mod in self.elements.into_iter() { - let module = child_mod.generate_register_interface(); - out.extend(module); - } - - out - }; + let code_children = self + .elements + .into_iter() + .map(|e| e.generate_register_interface()) + .collect::, _>>()?; Ok(quote! { pub mod #snake_case_name { @@ -190,7 +187,7 @@ impl CodeGen for Block { use super::RegisterInterface; - #child_mods + #(#code_children)* const OFFSET: usize = #addr; //pub(crate) const SIZE: usize = 0x4000; From 86d26c57cb1b0464ede5dcbc3a25745b58c21e69 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Sat, 1 Feb 2025 20:03:36 +0900 Subject: [PATCH 08/45] remove: unnecessary comment from quoted code --- src/generator.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/generator.rs b/src/generator.rs index 4e4cfdd..791285a 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -190,9 +190,7 @@ impl CodeGen for Block { #(#code_children)* const OFFSET: usize = #addr; - //pub(crate) const SIZE: usize = 0x4000; - /// Access to SLR0. pub struct #upper_camel_name<'a> { mem_ptr: *mut u32, _marker: PhantomData<&'a mut RegisterInterface>, From 48215049e807bf156654b015c86d9135483f0026 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Sat, 1 Feb 2025 20:03:59 +0900 Subject: [PATCH 09/45] update: doc on codegen_register --- src/generator/codegen_register.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/generator/codegen_register.rs b/src/generator/codegen_register.rs index db5deee..243f408 100644 --- a/src/generator/codegen_register.rs +++ b/src/generator/codegen_register.rs @@ -288,7 +288,7 @@ fn generate_single_ux_field( /// As "single" field cases, this function is separated into two cases; /// /// - fields are `bool` => [`generate_multiple_bool_field`] -/// - fields are `u8`/`u16`/`u32` => todo +/// - fields are `u8`/`u16`/`u32` => [`generate_multiple_ux_field`] fn generate_multiple_field( mask_name: Ident, base_type: Ident, From 1ed3843508772d0ce1eb2f079b3230f1abe20e72 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Sat, 1 Feb 2025 20:13:48 +0900 Subject: [PATCH 10/45] finish: impl CodeGen for Register --- src/generator.rs | 32 +++---------------- src/generator/codegen_registerspec_impl.rs | 36 ++++++++++++++++++++++ 2 files changed, 41 insertions(+), 27 deletions(-) create mode 100644 src/generator/codegen_registerspec_impl.rs diff --git a/src/generator.rs b/src/generator.rs index 791285a..d9bc458 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -212,6 +212,7 @@ impl CodeGen for Block { } mod codegen_register; +mod codegen_registerspec_impl; impl CodeGen for Register { fn generate_register_interface(self) -> Result { @@ -226,7 +227,8 @@ impl CodeGen for Register { proc_macro2::Ident, ) = codegen_register::reg_type_def(&self, &upper_camel_name)?; - let code_reg_def: proc_macro2::TokenStream = todo!(); + let code_reg_def: proc_macro2::TokenStream = + codegen_registerspec_impl::gen_registerspec_impl(reg_name.clone(), self.modf, type_t, type_ux); Ok(quote! { pub mod #snake_case_name { @@ -250,33 +252,9 @@ impl CodeGen for Register { } } - impl RegisterSpec for #reg_name<'_> { - type Ux = u32; - type T = SlId; + #code_reg_def - fn as_ptr(&self) -> *mut Self::Ux { - self.mem_ptr - } - } - impl Readable for #reg_name<'_> {} - impl Writable for #reg_name<'_> {} - impl Modifiable for #reg_name<'_> {} - - #[derive(Debug, Clone, Copy, Default)] - pub struct SlId(pub u32); - impl TryFrom for SlId { - type Error = DataConversionError; - - fn try_from(value: u32) -> Result { - let mask = 0x0000003f; - Ok(SlId(value & mask)) - } - } - impl From for u32 { - fn from(val: SlId) -> Self { - val.0 - } - } + #code_t_def } }) } diff --git a/src/generator/codegen_registerspec_impl.rs b/src/generator/codegen_registerspec_impl.rs new file mode 100644 index 0000000..a1b0edc --- /dev/null +++ b/src/generator/codegen_registerspec_impl.rs @@ -0,0 +1,36 @@ +use proc_macro2::{Ident, TokenStream}; +use quote::quote; + +use crate::types::RwSpecifier; + +pub(super) fn gen_registerspec_impl( + reg_name: Ident, + modf: RwSpecifier, + type_t: Ident, + type_ux: Ident, +) -> TokenStream { + let impl_rw = match modf { + RwSpecifier::R => quote! { + impl Readable for #reg_name<'_> {} + }, + RwSpecifier::W => quote! { + impl Writable for #reg_name<'_> {} + }, + RwSpecifier::RW => quote! { + impl Readable for #reg_name<'_> {} + impl Writable for #reg_name<'_> {} + impl Modifiable for #reg_name<'_> {} + }, + }; + quote! { + impl RegisterSpec for #reg_name<'_> { + type Ux = #type_ux; + type T = #type_t; + + fn as_ptr(&self) -> *mut Self::Ux { + self.mem_ptr + } + } + #impl_rw + } +} From 859d9c0fa3ec729f13f92e1cee5067c91e1c6016 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Sat, 1 Feb 2025 20:39:23 +0900 Subject: [PATCH 11/45] update: Add validation for get_name which rejects empty string (which is not a valid token) --- src/converter.rs | 45 ++++++++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/src/converter.rs b/src/converter.rs index 4d0c964..db5d454 100644 --- a/src/converter.rs +++ b/src/converter.rs @@ -54,8 +54,11 @@ pub enum DomConversionError { param: &'static str, pos: (TextPos, TextPos), }, - #[error("other dom conversion error: {0}")] - OtherError(String), + #[error("other dom conversion error: {comment}: {start} - {end}", start = pos.0, end = pos.1)] + OtherError { + comment: &'static str, + pos: (TextPos, TextPos), + }, } impl DomConversionError { @@ -115,6 +118,15 @@ impl DomConversionError { found, } } + + fn other_error(comment: &'static str, node: Node) -> Self { + let range = node.range(); + let doc = node.document(); + Self::OtherError { + comment, + pos: (doc.text_pos_at(range.start), doc.text_pos_at(range.end)), + } + } } mod util { @@ -126,7 +138,11 @@ mod util { pub(crate) fn get_name(node: Node) -> Result { match node.attribute("name") { - Some(name) => Ok(name.to_string()), + Some(name) if !name.is_empty() => Ok(name.to_string()), + Some(_name) => Err(DomConversionError::other_error( + "name cannot be empty", + node, + )), None => Err(DomConversionError::attr_not_found("name", node)), } } @@ -286,8 +302,9 @@ impl Block { .next() { Some(s) => Ok(s.to_string()), - None => Err(DomConversionError::OtherError( - "decoder format is not yet fixed".to_string(), + None => Err(DomConversionError::other_error( + "decoder format is not yet fixed", + node, )), }?, }; @@ -377,18 +394,16 @@ impl Register { // Validation if mask.is_some() && !children.is_empty() { - return Err(DomConversionError::OtherError(format!( - "both mask and field are used in the same register: {} - {}", - node.document().text_pos_at(node.range().start), - node.document().text_pos_at(node.range().end), - ))); + return Err(DomConversionError::other_error( + "both mask and field are used in the same register", + node, + )); } if default.is_some() && !children.is_empty() { - return Err(DomConversionError::OtherError(format!( - "both default and field are used in the same register: {} - {}", - node.document().text_pos_at(node.range().start), - node.document().text_pos_at(node.range().end), - ))); + return Err(DomConversionError::other_error( + "both default and field are used in the same register", + node, + )); } if let (Some(mask), Some(default)) = (mask, default) { if default & !(mask) != 0 { From 80ce358aee627a7f8f46b4f6af83d347864ce3fc Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Sat, 1 Feb 2025 20:41:28 +0900 Subject: [PATCH 12/45] fix(generator): mask (u32) should be Literal, not Identifier --- src/generator.rs | 7 ++++++- src/generator/codegen_register.rs | 10 +++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/generator.rs b/src/generator.rs index d9bc458..109b9a1 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -228,7 +228,12 @@ impl CodeGen for Register { ) = codegen_register::reg_type_def(&self, &upper_camel_name)?; let code_reg_def: proc_macro2::TokenStream = - codegen_registerspec_impl::gen_registerspec_impl(reg_name.clone(), self.modf, type_t, type_ux); + codegen_registerspec_impl::gen_registerspec_impl( + reg_name.clone(), + self.modf, + type_t, + type_ux, + ); Ok(quote! { pub mod #snake_case_name { diff --git a/src/generator/codegen_register.rs b/src/generator/codegen_register.rs index 243f408..c54574d 100644 --- a/src/generator/codegen_register.rs +++ b/src/generator/codegen_register.rs @@ -3,7 +3,7 @@ use heck::{ToShoutySnekCase, ToSnakeCase}; use itertools::Itertools; -use proc_macro2::{Ident, TokenStream}; +use proc_macro2::{Ident, Literal, TokenStream}; use quote::quote; use crate::types::{DataType, Field, MultipleParams, Register}; @@ -298,12 +298,12 @@ fn generate_multiple_field( multiple_params: &MultipleParams, ) -> (TokenStream, TokenStream, TokenStream) { let num_multiple = multiple_params.multiple; - let id_num_multiple = util::parse_to_ident(&num_multiple.to_string()).unwrap(); + let id_num_multiple = util::parse_to_literal(&num_multiple.to_string()).unwrap(); let id_field_type = single_field_type.to_rust_type_token(); let masks: Vec<_> = (0..multiple_params.multiple) .map(|x| x * multiple_params.offset) .map(|offset| single_mask_val << offset) - .map(|mask| util::parse_to_ident(&format!("0x{mask:x}")).unwrap()) + .map(|mask| util::parse_to_literal(&format!("0x{mask:x}")).unwrap()) .collect(); debug_assert_eq!(masks.len(), num_multiple.try_into().unwrap()); let code_mask = quote! { @@ -331,7 +331,7 @@ fn generate_multiple_bool_field( mask_name: Ident, base_type: Ident, snake_case_name: Ident, - masks: Vec, + masks: Vec, ) -> (TokenStream, TokenStream) { let num_multiple = masks.len(); let elem_getter = masks.iter().enumerate().map(|(i, _mask)| { @@ -372,7 +372,7 @@ fn generate_multiple_ux_field( base_type: Ident, snake_case_name: Ident, single_field_type: RustUxTypes, - masks: Vec, + masks: Vec, ) -> (TokenStream, TokenStream) { let field_type = single_field_type.to_rust_type_token(); let num_multiple = masks.len(); From 49a9698a329725422bb04c94a55febc4cc3f77c1 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Sun, 2 Feb 2025 02:48:56 +0900 Subject: [PATCH 13/45] refactor: remove to_shouty_snake_case & inline mask variable in reg_type_def_masked --- src/generator/codegen_register.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/generator/codegen_register.rs b/src/generator/codegen_register.rs index c54574d..4784f1c 100644 --- a/src/generator/codegen_register.rs +++ b/src/generator/codegen_register.rs @@ -1,7 +1,7 @@ //! Generator for [`Register`]. //! The entry point is [`reg_type_def`]. -use heck::{ToShoutySnekCase, ToSnakeCase}; +use heck::{ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase}; use itertools::Itertools; use proc_macro2::{Ident, Literal, TokenStream}; use quote::quote; @@ -79,8 +79,7 @@ fn reg_type_def_masked( type Error = DataConversionError<#type_ux, Self>; fn try_from(value: #type_ux) -> Result { - let mask = #mask; - Ok(SlId(value & mask)) + Ok(SlId(value & #mask)) } } impl From<#upper_camel_name> for #type_ux { @@ -169,7 +168,7 @@ fn generate_field( field: &Field, basetype: &DataType, ) -> Result<(TokenStream, TokenStream, TokenStream), CodeGenError> { - let mask_name = util::parse_to_ident(&format!("{}_MASK", field.name.TO_SHOUTY_SNEK_CASE()))?; + let mask_name = util::parse_to_ident(&format!("{}_MASK", field.name.to_shouty_snake_case()))?; let base_type = util::RustUxTypes::from(basetype).to_rust_type_token(); let snake_case_name = util::parse_to_ident(&field.name.to_snake_case())?; From f96aab4d9d3955c09fe5945308a332052ef520a9 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Sun, 2 Feb 2025 02:50:40 +0900 Subject: [PATCH 14/45] fix: add values pattern to "single" registers, introducing `FieldType` --- src/generator/codegen_register.rs | 167 ++++++++++++++++++++++++++---- 1 file changed, 146 insertions(+), 21 deletions(-) diff --git a/src/generator/codegen_register.rs b/src/generator/codegen_register.rs index 4784f1c..6e06881 100644 --- a/src/generator/codegen_register.rs +++ b/src/generator/codegen_register.rs @@ -6,7 +6,7 @@ use itertools::Itertools; use proc_macro2::{Ident, Literal, TokenStream}; use quote::quote; -use crate::types::{DataType, Field, MultipleParams, Register}; +use crate::types::{DataType, Field, MultipleParams, Register, Value}; use super::{ util::{self, RustUxTypes}, @@ -153,6 +153,11 @@ fn reg_type_def_with_field( Ok((out, upper_camel_name.clone(), type_ux)) } +enum FieldType<'a> { + RustType(RustUxTypes), + CustomValue(&'a [Value]), +} + /// Generate code for each field, which consists of these three. /// 1. mask definition /// 2. getter method @@ -172,7 +177,10 @@ fn generate_field( let base_type = util::RustUxTypes::from(basetype).to_rust_type_token(); let snake_case_name = util::parse_to_ident(&field.name.to_snake_case())?; - let field_type = util::RustUxTypes::from_mask(field.mask); + let field_type = match field.elements.is_empty() { + true => FieldType::RustType(util::RustUxTypes::from_mask(field.mask)), + false => FieldType::CustomValue(&field.elements), + }; let (code_mask, code_getter, code_setter) = match &field.multiple { Some(multiple_params) => generate_multiple_field( @@ -198,10 +206,11 @@ fn generate_field( /// Generate "single" field definition (mask, getter, setter). /// /// # Cases -/// This function is separated into two cases. +/// This function is separated into three cases based on field types [`FieldType`]. /// /// - field is `bool` => [`generate_single_bool_field`] /// - field is `u8`/`u16`/`u32` => [`generate_single_ux_field`] +/// - field is custom [`Value`] => [`generate_custom_values_mask_enumdef`] & todo /// /// For the details of types of register/field, see [`DataType`]. /// Note that in both cases, mask definitions are the same. @@ -209,17 +218,34 @@ fn generate_single_field( mask_name: Ident, base_type: Ident, mask_val: u32, - field_type: RustUxTypes, + field_type: FieldType, snake_case_name: Ident, ) -> (TokenStream, TokenStream, TokenStream) { let mask_val = util::parse_to_literal(&format!("0x{:x}", mask_val)).unwrap(); - let code_mask = quote! { - const #mask_name: #base_type = #mask_val; + let code_mask = match field_type { + FieldType::RustType(_) => { + quote! { + const #mask_name: #base_type = #mask_val; + } + } + FieldType::CustomValue(values) => { + let additional = + generate_custom_values_mask_enumdef(&base_type, &snake_case_name, values); + quote! { + const #mask_name: #base_type = #mask_val; + #additional + } + } }; let (code_getter, code_setter) = match field_type { - RustUxTypes::Bool => generate_single_bool_field(mask_name, snake_case_name), - RustUxTypes::U8 | RustUxTypes::U16 | RustUxTypes::U32 => { - generate_single_ux_field(mask_name, base_type, snake_case_name, field_type) + FieldType::RustType(field_type) => match field_type { + RustUxTypes::Bool => generate_single_bool_field(mask_name, snake_case_name), + RustUxTypes::U8 | RustUxTypes::U16 | RustUxTypes::U32 => { + generate_single_ux_field(mask_name, base_type, snake_case_name, field_type) + } + }, + FieldType::CustomValue(values) => { + generate_custom_values_field(mask_name, snake_case_name, values) } }; @@ -281,6 +307,96 @@ fn generate_single_ux_field( (code_getter, code_setter) } +fn custom_value_const_name(field_name: &Ident, value_name: &String) -> Ident { + util::parse_to_ident(&format!( + "{}_{}", + field_name.to_string().to_shouty_snake_case(), + value_name.to_shouty_snake_case() + )) + .unwrap() +} + +/// Generate const var and value enum definition. +fn generate_custom_values_mask_enumdef( + base_type: &Ident, + field_name: &Ident, + values: &[Value], +) -> TokenStream { + let masks = values.iter().map(|value| { + let const_name = custom_value_const_name(field_name, &value.name); + let val = value.data; + quote! { + const #const_name: #base_type = #val; + } + }); + let variants = values.iter().map(|value| { + let desc = value.desc.clone().unwrap_or("".to_string()); + let variant_name = util::parse_to_ident(&value.name.to_upper_camel_case()).unwrap(); + quote! { + #[doc = #desc] + #variant_name + } + }); + let value_enum_name = + util::parse_to_ident(&field_name.to_string().to_upper_camel_case()).unwrap(); + + quote! { + #(#masks)* + pub enum #value_enum_name { + #(#variants),* + } + } +} + +fn generate_custom_values_field( + mask_name: Ident, + snake_case_name: Ident, + values: &[Value], +) -> (TokenStream, TokenStream) { + let setter_name = util::parse_to_ident(&format!("set_{}", snake_case_name)).unwrap(); + let value_enum_name = + util::parse_to_ident(&snake_case_name.to_string().to_upper_camel_case()).unwrap(); + let (getter_match_arms, setter_match_arms): (Vec<_>, Vec<_>) = values + .iter() + .map(|value| { + let const_name = custom_value_const_name(&snake_case_name, &value.name); + let variant_name = util::parse_to_ident(&value.name.to_upper_camel_case()).unwrap(); + ( + quote! { + #const_name => #value_enum_name::#variant_name + }, + quote! { + #value_enum_name::#variant_name => #const_name + }, + ) + }) + .unzip(); + let code_getter = quote! { + pub fn #snake_case_name(&self) -> #value_enum_name { + match (self.inner & #mask_name) + >> #mask_name.trailing_zeros() + { + #(#getter_match_arms),*, + _ => panic!("must not reachable"), + } + } + }; + + let code_setter = quote! { + pub fn #setter_name(self, val: #value_enum_name) -> Self { + let val = match val { + #(#setter_match_arms),* + }; + let mut inner = self.inner; + inner &= !#mask_name; + inner |= val; + + Self { inner } + } + }; + (code_getter, code_setter) +} + /// Generate "multiple" field definition (mask, getter, setter). /// /// # Cases @@ -288,17 +404,21 @@ fn generate_single_ux_field( /// /// - fields are `bool` => [`generate_multiple_bool_field`] /// - fields are `u8`/`u16`/`u32` => [`generate_multiple_ux_field`] +/// - fields are custom [`Value`] => todo fn generate_multiple_field( mask_name: Ident, base_type: Ident, single_mask_val: u32, - single_field_type: RustUxTypes, + single_field_type: FieldType, snake_case_name: Ident, multiple_params: &MultipleParams, ) -> (TokenStream, TokenStream, TokenStream) { let num_multiple = multiple_params.multiple; let id_num_multiple = util::parse_to_literal(&num_multiple.to_string()).unwrap(); - let id_field_type = single_field_type.to_rust_type_token(); + let id_field_type = match single_field_type { + FieldType::RustType(ref single_field_type) => single_field_type.to_rust_type_token(), + FieldType::CustomValue(_) => todo!("HERE NEXT"), + }; let masks: Vec<_> = (0..multiple_params.multiple) .map(|x| x * multiple_params.offset) .map(|offset| single_mask_val << offset) @@ -310,21 +430,26 @@ fn generate_multiple_field( }; let (code_getter, code_setter) = match single_field_type { - RustUxTypes::Bool => { - generate_multiple_bool_field(mask_name, base_type, snake_case_name, masks.clone()) - } - RustUxTypes::U8 | RustUxTypes::U16 | RustUxTypes::U32 => generate_multiple_ux_field( - mask_name, - base_type, - snake_case_name, - single_field_type, - masks, - ), + FieldType::RustType(single_field_type) => match single_field_type { + RustUxTypes::Bool => { + generate_multiple_bool_field(mask_name, base_type, snake_case_name, masks.clone()) + } + RustUxTypes::U8 | RustUxTypes::U16 | RustUxTypes::U32 => generate_multiple_ux_field( + mask_name, + base_type, + snake_case_name, + single_field_type, + masks, + ), + }, + FieldType::CustomValue(_) => todo!("HERE NEXT"), }; (code_mask, code_getter, code_setter) } +fn generate_single_value_field() -> () {} + /// Generate bool "multiple" field definition (getter, setter). fn generate_multiple_bool_field( mask_name: Ident, From fdb87a0898693e4c5d54022a73928056f36bad53 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Sun, 2 Feb 2025 02:51:30 +0900 Subject: [PATCH 15/45] fix: ident was interpolated as string --- src/generator.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/generator.rs b/src/generator.rs index 109b9a1..f7b11b2 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -160,8 +160,8 @@ impl CodeGen for Block { let accessor_methods = { let mut out = TokenStream::new(); for child_name in self.elements.iter().map(|e| e.get_name()) { - let snake_case_name = child_name.to_snake_case(); - let upper_camel_name = child_name.to_upper_camel_case(); + let snake_case_name = util::parse_to_ident(&child_name.to_snake_case())?; + let upper_camel_name = util::parse_to_ident(&child_name.to_upper_camel_case())?; out.extend(quote! { pub fn #snake_case_name(&self) -> #snake_case_name::#upper_camel_name { From c3effa280a5a2ccf88d57ddb86f8f49fb7c41b69 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Sun, 2 Feb 2025 02:52:11 +0900 Subject: [PATCH 16/45] update(doc): note on adding "desc" as doccomments --- src/generator.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/generator.rs b/src/generator.rs index f7b11b2..3ca8f27 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -1,6 +1,9 @@ //! Generate register interface rust code from types in [`crate::types`]. //! //! root: [`CodeGen`] +//! +//! # TODO +//! add docs (especially fields and registers) use crate::{ type_traits::GetName, From dbb124ec76efcb2d553f4707ec2eec8bcfc1fdb7 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Sun, 2 Feb 2025 03:05:00 +0900 Subject: [PATCH 17/45] refactor: rename single custom generator & update doc & remove unused fn def --- src/generator/codegen_register.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/generator/codegen_register.rs b/src/generator/codegen_register.rs index 6e06881..7eb2ed2 100644 --- a/src/generator/codegen_register.rs +++ b/src/generator/codegen_register.rs @@ -210,7 +210,7 @@ fn generate_field( /// /// - field is `bool` => [`generate_single_bool_field`] /// - field is `u8`/`u16`/`u32` => [`generate_single_ux_field`] -/// - field is custom [`Value`] => [`generate_custom_values_mask_enumdef`] & todo +/// - field is custom [`Value`] => [`generate_custom_values_mask_enumdef`] & [`generate_single_custom_values_field`] /// /// For the details of types of register/field, see [`DataType`]. /// Note that in both cases, mask definitions are the same. @@ -245,7 +245,7 @@ fn generate_single_field( } }, FieldType::CustomValue(values) => { - generate_custom_values_field(mask_name, snake_case_name, values) + generate_single_custom_values_field(mask_name, snake_case_name, values) } }; @@ -307,7 +307,7 @@ fn generate_single_ux_field( (code_getter, code_setter) } -fn custom_value_const_name(field_name: &Ident, value_name: &String) -> Ident { +fn custom_value_const_name(field_name: &Ident, value_name: &str) -> Ident { util::parse_to_ident(&format!( "{}_{}", field_name.to_string().to_shouty_snake_case(), @@ -348,7 +348,7 @@ fn generate_custom_values_mask_enumdef( } } -fn generate_custom_values_field( +fn generate_single_custom_values_field( mask_name: Ident, snake_case_name: Ident, values: &[Value], @@ -448,8 +448,6 @@ fn generate_multiple_field( (code_mask, code_getter, code_setter) } -fn generate_single_value_field() -> () {} - /// Generate bool "multiple" field definition (getter, setter). fn generate_multiple_bool_field( mask_name: Ident, From 39a793462a62fc22cd6c734ac1ff37ea282085b0 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Sun, 2 Feb 2025 03:16:40 +0900 Subject: [PATCH 18/45] fix: fix mask type for multiple field register --- src/generator/codegen_register.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/generator/codegen_register.rs b/src/generator/codegen_register.rs index 7eb2ed2..225b881 100644 --- a/src/generator/codegen_register.rs +++ b/src/generator/codegen_register.rs @@ -415,10 +415,6 @@ fn generate_multiple_field( ) -> (TokenStream, TokenStream, TokenStream) { let num_multiple = multiple_params.multiple; let id_num_multiple = util::parse_to_literal(&num_multiple.to_string()).unwrap(); - let id_field_type = match single_field_type { - FieldType::RustType(ref single_field_type) => single_field_type.to_rust_type_token(), - FieldType::CustomValue(_) => todo!("HERE NEXT"), - }; let masks: Vec<_> = (0..multiple_params.multiple) .map(|x| x * multiple_params.offset) .map(|offset| single_mask_val << offset) @@ -426,7 +422,7 @@ fn generate_multiple_field( .collect(); debug_assert_eq!(masks.len(), num_multiple.try_into().unwrap()); let code_mask = quote! { - const #mask_name: [#id_field_type; #id_num_multiple] = [#(#masks),*]; + const #mask_name: [#base_type; #id_num_multiple] = [#(#masks),*]; }; let (code_getter, code_setter) = match single_field_type { From 30279dc2b3d6583e66a438c9f525b5e3a93d5f75 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Sun, 2 Feb 2025 04:07:32 +0900 Subject: [PATCH 19/45] refactor(generator): rename to generate_custom_values_const_enumdef they are not masks, but consts --- src/generator/codegen_register.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/generator/codegen_register.rs b/src/generator/codegen_register.rs index 225b881..0e1d811 100644 --- a/src/generator/codegen_register.rs +++ b/src/generator/codegen_register.rs @@ -230,7 +230,7 @@ fn generate_single_field( } FieldType::CustomValue(values) => { let additional = - generate_custom_values_mask_enumdef(&base_type, &snake_case_name, values); + generate_custom_values_const_enumdef(&base_type, &snake_case_name, values); quote! { const #mask_name: #base_type = #mask_val; #additional @@ -317,12 +317,12 @@ fn custom_value_const_name(field_name: &Ident, value_name: &str) -> Ident { } /// Generate const var and value enum definition. -fn generate_custom_values_mask_enumdef( +fn generate_custom_values_const_enumdef( base_type: &Ident, field_name: &Ident, values: &[Value], ) -> TokenStream { - let masks = values.iter().map(|value| { + let consts = values.iter().map(|value| { let const_name = custom_value_const_name(field_name, &value.name); let val = value.data; quote! { @@ -341,7 +341,7 @@ fn generate_custom_values_mask_enumdef( util::parse_to_ident(&field_name.to_string().to_upper_camel_case()).unwrap(); quote! { - #(#masks)* + #(#consts)* pub enum #value_enum_name { #(#variants),* } From 9728af3551989de28d7703d57f2a77ac12f85399 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Sun, 2 Feb 2025 04:08:36 +0900 Subject: [PATCH 20/45] new(codegen_register): add multiple custom value fields generator --- src/generator/codegen_register.rs | 88 ++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 1 deletion(-) diff --git a/src/generator/codegen_register.rs b/src/generator/codegen_register.rs index 0e1d811..0195490 100644 --- a/src/generator/codegen_register.rs +++ b/src/generator/codegen_register.rs @@ -421,8 +421,15 @@ fn generate_multiple_field( .map(|mask| util::parse_to_literal(&format!("0x{mask:x}")).unwrap()) .collect(); debug_assert_eq!(masks.len(), num_multiple.try_into().unwrap()); + let value_const_enumdefs = match single_field_type { + FieldType::RustType(_) => quote! {}, + FieldType::CustomValue(values) => { + generate_custom_values_const_enumdef(&base_type, &snake_case_name, values) + } + }; let code_mask = quote! { const #mask_name: [#base_type; #id_num_multiple] = [#(#masks),*]; + #value_const_enumdefs }; let (code_getter, code_setter) = match single_field_type { @@ -438,7 +445,13 @@ fn generate_multiple_field( masks, ), }, - FieldType::CustomValue(_) => todo!("HERE NEXT"), + FieldType::CustomValue(values) => generate_multiple_custom_values_field( + mask_name, + base_type, + snake_case_name, + masks, + values, + ), }; (code_mask, code_getter, code_setter) @@ -528,3 +541,76 @@ fn generate_multiple_ux_field( (code_getter, code_setter) } + +fn generate_multiple_custom_values_field( + mask_name: Ident, + base_type: Ident, + snake_case_name: Ident, + masks: Vec, + values: &[Value], +) -> (TokenStream, TokenStream) { + let value_enum_name = + util::parse_to_ident(&snake_case_name.to_string().to_upper_camel_case()).unwrap(); + let num_multiple = masks.len(); + let (getter_match_arms, setter_match_arms): (Vec<_>, Vec<_>) = values + .iter() + .map(|value| { + let const_name = custom_value_const_name(&snake_case_name, &value.name); + let variant_name = util::parse_to_ident(&value.name.to_upper_camel_case()).unwrap(); + ( + quote! { + #const_name => #value_enum_name::#variant_name + }, + quote! { + #value_enum_name::#variant_name => #const_name + }, + ) + }) + .unzip(); + + let elem_getter = masks.iter().enumerate().map(|(i, _mask)| { + quote! { + match ((self.inner & #mask_name[#i])) + >> (#mask_name[#i].trailing_zeros()) + { + #(#getter_match_arms),*, + _ => panic!("must not reachable"), + } + } + }); + let code_getter = quote! { + pub fn #snake_case_name(&self) -> [#value_enum_name; #num_multiple] { + [ + #(#elem_getter),* + ] + } + }; + + let setter_name = util::parse_to_ident(&format!("set_{}", snake_case_name)).unwrap(); + let elem_setter = masks.iter().enumerate().map(|(i, _mask)| { + quote! { + match val[#i] { + #(#setter_match_arms),* + } + } + }); + let code_setter = quote! { + pub fn #setter_name(&self, val: [#value_enum_name; #num_multiple]) -> Self { + let val: [#base_type; #num_multiple] = [ + #(#elem_setter),* + ]; + let mask: #base_type = #mask_name.iter().sum(); + let update: #base_type = #mask_name + .iter() + .zip(val) + .map(|(mask, val)| (#base_type::from(val)) << (mask.trailing_zeros())) + .sum(); + let mut inner = self.inner; + inner &= !mask; + inner |= update; + Self { inner } + } + }; + + (code_getter, code_setter) +} From 7c1a0856a07393d9e9f317d6b7ce38766ad9dfa2 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Sun, 2 Feb 2025 04:09:39 +0900 Subject: [PATCH 21/45] refactor: add _ prefix to unused vals (impl for Memory & Fifo) --- src/converter.rs | 4 ++-- src/generator.rs | 5 ++--- src/type_traits.rs | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/converter.rs b/src/converter.rs index db5d454..426ba4d 100644 --- a/src/converter.rs +++ b/src/converter.rs @@ -432,13 +432,13 @@ impl Register { } impl Memory { - pub(crate) fn from_xml_dom(node: Node) -> Result { + pub(crate) fn from_xml_dom(_node: Node) -> Result { todo!() } } impl Fifo { - pub(crate) fn from_xml_dom(node: Node) -> Result { + pub(crate) fn from_xml_dom(_node: Node) -> Result { todo!() } } diff --git a/src/generator.rs b/src/generator.rs index 3ca8f27..4135610 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -81,7 +81,6 @@ mod util { } } } - #[cfg(test)] mod test { use super::RustUxTypes; @@ -147,8 +146,8 @@ impl CodeGen for ModuleBlockElements { match self { ModuleBlockElements::Block(block) => block.generate_register_interface(), ModuleBlockElements::Register(register) => register.generate_register_interface(), - ModuleBlockElements::Memory(memory) => todo!(), - ModuleBlockElements::Fifo(fifo) => todo!(), + ModuleBlockElements::Memory(_memory) => todo!(), + ModuleBlockElements::Fifo(_fifo) => todo!(), } } } diff --git a/src/type_traits.rs b/src/type_traits.rs index a31512f..d68da02 100644 --- a/src/type_traits.rs +++ b/src/type_traits.rs @@ -11,8 +11,8 @@ impl GetName for ModuleBlockElements { match self { ModuleBlockElements::Block(block) => block.get_name(), ModuleBlockElements::Register(register) => register.get_name(), - ModuleBlockElements::Memory(memory) => todo!(), - ModuleBlockElements::Fifo(fifo) => todo!(), + ModuleBlockElements::Memory(_memory) => todo!(), + ModuleBlockElements::Fifo(_fifo) => todo!(), } } } From 4131ce1880a430b2e5587ffda9b1466069f5c463 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Sun, 2 Feb 2025 04:10:25 +0900 Subject: [PATCH 22/45] refactor(generator): remove impl DataType (use DataType -> RustUxTypes -> Ident) --- src/generator.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/generator.rs b/src/generator.rs index 4135610..d1d1b4f 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -74,13 +74,6 @@ mod util { } } - impl DataType { - fn to_rust_type_token(&self) -> proc_macro2::Ident { - match self { - DataType::D32 => proc_macro2::Ident::new("u32", proc_macro2::Span::call_site()), - } - } - } #[cfg(test)] mod test { use super::RustUxTypes; From 28b2b198a84c4a2b118d4de4bd9b7b41bfd4dd09 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Sun, 2 Feb 2025 04:25:29 +0900 Subject: [PATCH 23/45] update: docs in generator & codegen_register --- src/generator.rs | 1 + src/generator/codegen_register.rs | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/generator.rs b/src/generator.rs index d1d1b4f..7cb33fc 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -209,6 +209,7 @@ impl CodeGen for Block { mod codegen_register; mod codegen_registerspec_impl; +/// Internally calls functions in [`codegen_register`] impl CodeGen for Register { fn generate_register_interface(self) -> Result { let snake_case_name = util::parse_to_ident(&self.name.to_snake_case())?; diff --git a/src/generator/codegen_register.rs b/src/generator/codegen_register.rs index 0195490..73d9c0d 100644 --- a/src/generator/codegen_register.rs +++ b/src/generator/codegen_register.rs @@ -210,7 +210,7 @@ fn generate_field( /// /// - field is `bool` => [`generate_single_bool_field`] /// - field is `u8`/`u16`/`u32` => [`generate_single_ux_field`] -/// - field is custom [`Value`] => [`generate_custom_values_mask_enumdef`] & [`generate_single_custom_values_field`] +/// - field is custom [`Value`] => [`generate_custom_values_const_enumdef`] & [`generate_single_custom_values_field`] /// /// For the details of types of register/field, see [`DataType`]. /// Note that in both cases, mask definitions are the same. @@ -317,6 +317,8 @@ fn custom_value_const_name(field_name: &Ident, value_name: &str) -> Ident { } /// Generate const var and value enum definition. +/// +/// Both for single & multiple. fn generate_custom_values_const_enumdef( base_type: &Ident, field_name: &Ident, @@ -348,6 +350,7 @@ fn generate_custom_values_const_enumdef( } } +/// Generate custom [`Value`] "single" field definition (getter, setter). fn generate_single_custom_values_field( mask_name: Ident, snake_case_name: Ident, @@ -404,7 +407,7 @@ fn generate_single_custom_values_field( /// /// - fields are `bool` => [`generate_multiple_bool_field`] /// - fields are `u8`/`u16`/`u32` => [`generate_multiple_ux_field`] -/// - fields are custom [`Value`] => todo +/// - fields are custom [`Value`] => [`generate_custom_values_const_enumdef`] & [`generate_multiple_custom_values_field`] fn generate_multiple_field( mask_name: Ident, base_type: Ident, @@ -542,6 +545,7 @@ fn generate_multiple_ux_field( (code_getter, code_setter) } +/// Generate custom [`Value`] "single" field definition (getter, setter). fn generate_multiple_custom_values_field( mask_name: Ident, base_type: Ident, From 9a882fdab10f48d499dcf799631c9c66d8d4a8e1 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Sun, 2 Feb 2025 16:48:15 +0900 Subject: [PATCH 24/45] new: printto file in main --- src/main.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 170859b..f438176 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,7 +21,16 @@ fn main() -> Result<()> { let register_map = types::Module::from_xml_dom(doc.root_element())?; println!("read: {:#?}", register_map); - println!("{}", register_map.generate_register_interface()?); + // 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(), + )?; Ok(()) } From cc0fe59b28cb60c3f3a115a95860b833d8391376 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Sun, 2 Feb 2025 16:48:53 +0900 Subject: [PATCH 25/45] fix: missing interpolation (reg_type_def_masked, `SlId`) --- src/generator/codegen_register.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/generator/codegen_register.rs b/src/generator/codegen_register.rs index 73d9c0d..dcce736 100644 --- a/src/generator/codegen_register.rs +++ b/src/generator/codegen_register.rs @@ -79,7 +79,7 @@ fn reg_type_def_masked( type Error = DataConversionError<#type_ux, Self>; fn try_from(value: #type_ux) -> Result { - Ok(SlId(value & #mask)) + Ok(Self(value & #mask)) } } impl From<#upper_camel_name> for #type_ux { From 71c25dd379ca2af27397df17c95b6387b45591a4 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Sun, 2 Feb 2025 16:49:54 +0900 Subject: [PATCH 26/45] fix: missing interpolation (reg_type_def_with_field, `GtyDelayBank123`) --- src/generator/codegen_register.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/generator/codegen_register.rs b/src/generator/codegen_register.rs index dcce736..b20487f 100644 --- a/src/generator/codegen_register.rs +++ b/src/generator/codegen_register.rs @@ -131,7 +131,7 @@ fn reg_type_def_with_field( inner: #type_ux, } - impl GtyDelayBank123 { + impl #upper_camel_name { #(#code_getters)* #(#code_setters)* @@ -143,7 +143,7 @@ fn reg_type_def_with_field( Ok(Self { inner: value }) } } - impl From for u32 { + impl From<#upper_camel_name> for u32 { fn from(value: #upper_camel_name) -> Self { value.inner } From fe6ce5a0ddc487ccff6e9f4b6b6f3fa3f33ced81 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Sun, 2 Feb 2025 16:50:33 +0900 Subject: [PATCH 27/45] fix: missing interpolation (generate_multiple_bool_field, `BANK121_GTY_CHANNEL_MASK`) --- src/generator/codegen_register.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/generator/codegen_register.rs b/src/generator/codegen_register.rs index b20487f..baefbc3 100644 --- a/src/generator/codegen_register.rs +++ b/src/generator/codegen_register.rs @@ -485,7 +485,7 @@ fn generate_multiple_bool_field( let code_setter = quote! { pub fn #setter_name(self, val: [bool; #num_multiple]) -> Self { let mask: #base_type = #mask_name.iter().sum(); - let update: #base_type = BANK121_GTY_CHANNEL_MASK + let update: #base_type = #mask_name .iter() .zip(val) .filter_map(|(mask, val)| val.then_some(mask)) From 5f7db47fcf32f224e386e77f62e470b7e0f04315 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Sun, 2 Feb 2025 16:51:12 +0900 Subject: [PATCH 28/45] fix: missing interpolation (generate_single_ux_field, `mask` -> `#mask_name`) --- src/generator/codegen_register.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/generator/codegen_register.rs b/src/generator/codegen_register.rs index baefbc3..78029b0 100644 --- a/src/generator/codegen_register.rs +++ b/src/generator/codegen_register.rs @@ -299,7 +299,7 @@ fn generate_single_ux_field( pub fn #setter_name(&self, val: #field_type) -> Self { let update: #base_type = val & #mask_name; let mut inner = self.inner; - inner &= !mask; + inner &= !#mask_name; inner |= update; Self { inner } } From 9e7a83242b68dd798f4408aad195e813ad0ff6b9 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Sun, 2 Feb 2025 16:55:37 +0900 Subject: [PATCH 29/45] fix: add parent_name to CodeGen to properly refer to parent name, and add `Reg` to register children One more: if parent is RegisterInterface, lifetime param is omitted --- src/generator.rs | 58 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/src/generator.rs b/src/generator.rs index 7cb33fc..16ba2de 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -111,20 +111,28 @@ mod util { } pub trait CodeGen { - fn generate_register_interface(self) -> Result; + /// `parent_name` in UpperCamelCase. + fn generate_register_interface( + self, + parent_name: proc_macro2::Ident, + ) -> Result; } impl CodeGen for Module { - fn generate_register_interface(self) -> Result { + fn generate_register_interface( + self, + _: proc_macro2::Ident, + ) -> Result { let mut out = TokenStream::new(); 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()) + .map(|e| e.generate_register_interface(ident_register_interface.clone())) .collect::, _>>()?; for child in child_mods { out.extend(child); @@ -135,10 +143,13 @@ impl CodeGen for Module { } impl CodeGen for ModuleBlockElements { - fn generate_register_interface(self) -> Result { + fn generate_register_interface( + self, + parent: proc_macro2::Ident, + ) -> Result { match self { - ModuleBlockElements::Block(block) => block.generate_register_interface(), - ModuleBlockElements::Register(register) => register.generate_register_interface(), + ModuleBlockElements::Block(block) => block.generate_register_interface(parent), + ModuleBlockElements::Register(register) => register.generate_register_interface(parent), ModuleBlockElements::Memory(_memory) => todo!(), ModuleBlockElements::Fifo(_fifo) => todo!(), } @@ -146,7 +157,10 @@ impl CodeGen for ModuleBlockElements { } impl CodeGen for Block { - fn generate_register_interface(self) -> Result { + fn generate_register_interface( + self, + parent: proc_macro2::Ident, + ) -> Result { 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))?; @@ -154,13 +168,22 @@ impl CodeGen for Block { let accessor_methods = { let mut out = TokenStream::new(); - for child_name in self.elements.iter().map(|e| e.get_name()) { + for e in self.elements.iter() { + let child_name = e.get_name(); let snake_case_name = util::parse_to_ident(&child_name.to_snake_case())?; - let upper_camel_name = util::parse_to_ident(&child_name.to_upper_camel_case())?; + let child_type_name = match e { + ModuleBlockElements::Block(_) => &child_name.to_upper_camel_case(), + ModuleBlockElements::Register(_) => { + &format!("Reg{}", child_name.to_upper_camel_case()) + } + ModuleBlockElements::Memory(_) => todo!(), + ModuleBlockElements::Fifo(_) => todo!(), + }; + let child_upper_camel_name = util::parse_to_ident(child_type_name)?; out.extend(quote! { - pub fn #snake_case_name(&self) -> #snake_case_name::#upper_camel_name { - #snake_case_name::#upper_camel_name::new(self.mem_ptr) + pub fn #snake_case_name(&self) -> #snake_case_name::#child_upper_camel_name { + #snake_case_name::#child_upper_camel_name::new(self.mem_ptr) } }); } @@ -171,9 +194,15 @@ impl CodeGen for Block { let code_children = self .elements .into_iter() - .map(|e| e.generate_register_interface()) + .map(|e| e.generate_register_interface(upper_camel_name.clone())) .collect::, _>>()?; + let parent_struct = if parent == util::parse_to_ident("RegisterInterface").unwrap() { + quote! {#parent} + } else { + quote! {#parent<'a>} + }; + Ok(quote! { pub mod #snake_case_name { #[doc = #desc] @@ -211,7 +240,10 @@ mod codegen_registerspec_impl; /// Internally calls functions in [`codegen_register`] impl CodeGen for Register { - fn generate_register_interface(self) -> Result { + fn generate_register_interface( + self, + parent: proc_macro2::Ident, + ) -> Result { 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}"))?; From cba4b3af055c20a0326b3ed472b5eeaaba6bac19 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Sun, 2 Feb 2025 16:57:07 +0900 Subject: [PATCH 30/45] fix: missing interpolation (impl CodeGen for Block, `RegisterInterface`) --- src/generator.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/generator.rs b/src/generator.rs index 16ba2de..972d0a0 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -209,7 +209,7 @@ impl CodeGen for Block { use std::marker::PhantomData; - use super::RegisterInterface; + use super::#parent; #(#code_children)* @@ -217,7 +217,7 @@ impl CodeGen for Block { pub struct #upper_camel_name<'a> { mem_ptr: *mut u32, - _marker: PhantomData<&'a mut RegisterInterface>, + _marker: PhantomData<&'a mut #parent_struct>, } impl #upper_camel_name<'_> { From d2f9cd603feab0cb7b0f4653d0acc52fa6fd7371 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Sun, 2 Feb 2025 16:57:37 +0900 Subject: [PATCH 31/45] fix: missing interpolation (impl CodeGen for Register, `Debug`) --- src/generator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/generator.rs b/src/generator.rs index 972d0a0..d742c3e 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -273,7 +273,7 @@ impl CodeGen for Register { pub struct #reg_name<'a> { mem_ptr: *mut u32, - _marker: PhantomData<&'a mut super::Debug<'a>>, + _marker: PhantomData<&'a mut super::#parent<'a>>, } impl #reg_name<'_> { From cb67f9648b241e7e6a7a64c79c976d6bff5be76e Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Sun, 2 Feb 2025 16:58:02 +0900 Subject: [PATCH 32/45] fix: add explicit casting in single Ux field generation (Now, it atually passed compiling!) --- src/generator/codegen_register.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/generator/codegen_register.rs b/src/generator/codegen_register.rs index 78029b0..0cff61d 100644 --- a/src/generator/codegen_register.rs +++ b/src/generator/codegen_register.rs @@ -297,7 +297,7 @@ fn generate_single_ux_field( }; let code_setter = quote! { pub fn #setter_name(&self, val: #field_type) -> Self { - let update: #base_type = val & #mask_name; + let update: #base_type = (val as #base_type) & #mask_name; let mut inner = self.inner; inner &= !#mask_name; inner |= update; From fd106e3355484d88be90a27d956c08561d161867 Mon Sep 17 00:00:00 2001 From: testuser Date: Mon, 3 Feb 2025 03:07:40 +0900 Subject: [PATCH 33/45] new(generator): separate modules for each blocks (not yet implemented file io) --- src/generator.rs | 208 ++++++++++++++++++++++++++++++++++------------- src/main.rs | 20 ++--- 2 files changed, 159 insertions(+), 69 deletions(-) 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(()) } From 6ff047563ca23e7741b3a2419798a8e1085586c9 Mon Sep 17 00:00:00 2001 From: testuser Date: Mon, 3 Feb 2025 04:39:01 +0900 Subject: [PATCH 34/45] new(main): add filesystem io --- src/main.rs | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 64236d5..cfc8c41 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,8 @@ -use std::{collections::HashMap, fs}; +use std::fs::DirBuilder; +use std::io::Write; +use std::{collections::HashMap, fs, io::BufWriter}; -use anyhow::Result; +use anyhow::{anyhow, Context, Result}; use endcap_sl_software_ri_generator::{generator::CodeGen, types}; use itertools::Itertools; @@ -25,10 +27,30 @@ fn main() -> Result<()> { println!("==========================================="); let files = register_map.generate_register_interface(None, None, HashMap::new())?; - println!("{:#?}", files); + // println!("{:#?}", files); for filepath in files.keys().sorted() { println!("{}", filepath.display()); } + if !std::fs::read_dir("register_interface") + .context("register_interface not found")? + .collect_vec() + .is_empty() + { + return Err(anyhow!("dir out is not empty")); + }; + for (file, code) in files { + DirBuilder::new() + .recursive(true) + .create(file.parent().context("no parent")?)?; + let file = fs::OpenOptions::new() + .create(true) + .truncate(true) + .write(true) + .open(file) + .context("opening file")?; + let mut writer = BufWriter::new(file); + write!(writer, "{}", code)?; + } Ok(()) } From 76310c64a39c0413cf11de30fe70775b249940b6 Mon Sep 17 00:00:00 2001 From: testuser Date: Mon, 3 Feb 2025 04:44:31 +0900 Subject: [PATCH 35/45] clippy lint --- src/generator.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/generator.rs b/src/generator.rs index 4e6bc52..8820846 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -15,7 +15,6 @@ use crate::{ types::{Block, Module, ModuleBlockElements, Register}, }; use heck::{ToSnakeCase, ToUpperCamelCase}; -use itertools::Itertools; use proc_macro2::TokenStream; use quote::quote; use thiserror::Error; @@ -201,10 +200,8 @@ impl CodeGen for Block { 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 parent_name = parent_name.ok_or(CodeGenError::ParentMissing { module: "Block" })?; + let parent_path = parent_path.ok_or(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())?; @@ -324,10 +321,8 @@ impl CodeGen for Register { 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 parent_name = parent_name.ok_or(CodeGenError::ParentMissing { module: "Block" })?; + let parent_path = parent_path.ok_or(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())?; From 3e72513b759ef346157c392e4cafbef75d338c1b Mon Sep 17 00:00:00 2001 From: testuser Date: Mon, 3 Feb 2025 17:51:15 +0900 Subject: [PATCH 36/45] fix(generator): syntax for top level doccment --- src/generator.rs | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/generator.rs b/src/generator.rs index 8820846..20b0507 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -252,7 +252,7 @@ impl CodeGen for Block { .collect::, _>>()?; let out = quote! { - #[doc = #desc] + #![doc = #desc] use std::marker::PhantomData; @@ -343,32 +343,34 @@ impl CodeGen for Register { type_ux, ); + let desc = self.desc.unwrap_or("".to_string()); + let out = quote! { - pub mod #snake_case_name { - use std::marker::PhantomData; + #![doc = #desc] - use crate::register_spec::{DataConversionError, Modifiable, Readable, RegisterSpec, Writable}; + use std::marker::PhantomData; - const OFFSET: usize = #addr; + use crate::register_spec::{DataConversionError, Modifiable, Readable, RegisterSpec, Writable}; - pub struct #reg_name<'a> { - mem_ptr: *mut u32, - _marker: PhantomData<&'a mut super::#parent_name<'a>>, - } + const OFFSET: usize = #addr; - impl #reg_name<'_> { - pub(crate) fn new(parent_ptr: *mut u32) -> Self { - #reg_name { - mem_ptr: unsafe { parent_ptr.add(OFFSET) }, - _marker: PhantomData, - } + pub struct #reg_name<'a> { + mem_ptr: *mut u32, + _marker: PhantomData<&'a mut super::#parent_name<'a>>, + } + + impl #reg_name<'_> { + pub(crate) fn new(parent_ptr: *mut u32) -> Self { + #reg_name { + mem_ptr: unsafe { parent_ptr.add(OFFSET) }, + _marker: PhantomData, } } - - #code_reg_def - - #code_t_def } + + #code_reg_def + + #code_t_def }; let (out_path, _next_parent_path) = mod_file_path(parent_path, &snake_case_name); From b5a189f9a128a38804edb8c7cbda4f6d0746565e Mon Sep 17 00:00:00 2001 From: testuser Date: Mon, 3 Feb 2025 17:51:54 +0900 Subject: [PATCH 37/45] new(codegen_register): add docs --- src/generator/codegen_register.rs | 53 +++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/src/generator/codegen_register.rs b/src/generator/codegen_register.rs index 0cff61d..4445086 100644 --- a/src/generator/codegen_register.rs +++ b/src/generator/codegen_register.rs @@ -190,6 +190,7 @@ fn generate_field( field_type, snake_case_name, multiple_params, + &field.desc.clone().unwrap_or("".to_string()), ), None => generate_single_field( mask_name, @@ -197,6 +198,7 @@ fn generate_field( field.mask, field_type, snake_case_name, + &field.desc.clone().unwrap_or("".to_string()), ), }; @@ -220,6 +222,7 @@ fn generate_single_field( mask_val: u32, field_type: FieldType, snake_case_name: Ident, + desc: &str, ) -> (TokenStream, TokenStream, TokenStream) { let mask_val = util::parse_to_literal(&format!("0x{:x}", mask_val)).unwrap(); let code_mask = match field_type { @@ -239,13 +242,13 @@ fn generate_single_field( }; let (code_getter, code_setter) = match field_type { FieldType::RustType(field_type) => match field_type { - RustUxTypes::Bool => generate_single_bool_field(mask_name, snake_case_name), + RustUxTypes::Bool => generate_single_bool_field(mask_name, snake_case_name, desc), RustUxTypes::U8 | RustUxTypes::U16 | RustUxTypes::U32 => { - generate_single_ux_field(mask_name, base_type, snake_case_name, field_type) + generate_single_ux_field(mask_name, base_type, snake_case_name, field_type, desc) } }, FieldType::CustomValue(values) => { - generate_single_custom_values_field(mask_name, snake_case_name, values) + generate_single_custom_values_field(mask_name, snake_case_name, values, desc) } }; @@ -256,14 +259,19 @@ fn generate_single_field( fn generate_single_bool_field( mask_name: Ident, snake_case_name: Ident, + desc: &str, ) -> (TokenStream, TokenStream) { let setter_name = util::parse_to_ident(&format!("set_{}", snake_case_name)).unwrap(); + let getter_doc = format!("Getter. {}", desc); let code_getter = quote! { + #[doc = #getter_doc] pub fn #snake_case_name(&self) -> bool { (self.inner & #mask_name) == #mask_name } }; + let setter_doc = format!("Setter. {}", desc); let code_setter = quote! { + #[doc = #setter_doc] pub fn #setter_name(self, val: bool) -> Self { let mut inner = self.inner; if val { @@ -283,11 +291,14 @@ fn generate_single_ux_field( base_type: Ident, snake_case_name: Ident, field_type: RustUxTypes, + desc: &str, ) -> (TokenStream, TokenStream) { let field_type = field_type.to_rust_type_token(); let setter_name = util::parse_to_ident(&format!("set_{}", snake_case_name)).unwrap(); + let getter_doc = format!("Getter. {}", desc); let code_getter = quote! { + #[doc = #getter_doc] pub fn #snake_case_name(&self) -> #field_type { const RIGHT_SHIFT: #base_type = #mask_name.trailing_zeros(); ((self.inner & #mask_name) >> RIGHT_SHIFT) @@ -295,7 +306,9 @@ fn generate_single_ux_field( .unwrap() } }; + let setter_doc = format!("Setter. {}", desc); let code_setter = quote! { + #[doc = #setter_doc] pub fn #setter_name(&self, val: #field_type) -> Self { let update: #base_type = (val as #base_type) & #mask_name; let mut inner = self.inner; @@ -355,6 +368,7 @@ fn generate_single_custom_values_field( mask_name: Ident, snake_case_name: Ident, values: &[Value], + desc: &str, ) -> (TokenStream, TokenStream) { let setter_name = util::parse_to_ident(&format!("set_{}", snake_case_name)).unwrap(); let value_enum_name = @@ -374,7 +388,10 @@ fn generate_single_custom_values_field( ) }) .unzip(); + + let getter_doc = format!("Getter. {}", desc); let code_getter = quote! { + #[doc = #getter_doc] pub fn #snake_case_name(&self) -> #value_enum_name { match (self.inner & #mask_name) >> #mask_name.trailing_zeros() @@ -385,7 +402,9 @@ fn generate_single_custom_values_field( } }; + let setter_doc = format!("Setter. {}", desc); let code_setter = quote! { + #[doc = #setter_doc] pub fn #setter_name(self, val: #value_enum_name) -> Self { let val = match val { #(#setter_match_arms),* @@ -415,6 +434,7 @@ fn generate_multiple_field( single_field_type: FieldType, snake_case_name: Ident, multiple_params: &MultipleParams, + desc: &str, ) -> (TokenStream, TokenStream, TokenStream) { let num_multiple = multiple_params.multiple; let id_num_multiple = util::parse_to_literal(&num_multiple.to_string()).unwrap(); @@ -437,15 +457,20 @@ fn generate_multiple_field( let (code_getter, code_setter) = match single_field_type { FieldType::RustType(single_field_type) => match single_field_type { - RustUxTypes::Bool => { - generate_multiple_bool_field(mask_name, base_type, snake_case_name, masks.clone()) - } + RustUxTypes::Bool => generate_multiple_bool_field( + mask_name, + base_type, + snake_case_name, + masks.clone(), + desc, + ), RustUxTypes::U8 | RustUxTypes::U16 | RustUxTypes::U32 => generate_multiple_ux_field( mask_name, base_type, snake_case_name, single_field_type, masks, + desc, ), }, FieldType::CustomValue(values) => generate_multiple_custom_values_field( @@ -454,6 +479,7 @@ fn generate_multiple_field( snake_case_name, masks, values, + desc, ), }; @@ -466,6 +492,7 @@ fn generate_multiple_bool_field( base_type: Ident, snake_case_name: Ident, masks: Vec, + desc: &str, ) -> (TokenStream, TokenStream) { let num_multiple = masks.len(); let elem_getter = masks.iter().enumerate().map(|(i, _mask)| { @@ -473,7 +500,9 @@ fn generate_multiple_bool_field( (self.inner & #mask_name[#i]) == #mask_name[#i] } }); + let getter_doc = format!("Getter. {}", desc); let code_getter = quote! { + #[doc = #getter_doc] pub fn #snake_case_name(&self) -> [bool; #num_multiple] { [ #(#elem_getter),* @@ -482,7 +511,9 @@ fn generate_multiple_bool_field( }; let setter_name = util::parse_to_ident(&format!("set_{}", snake_case_name)).unwrap(); + let setter_doc = format!("Setter. {}", desc); let code_setter = quote! { + #[doc = #setter_doc] pub fn #setter_name(self, val: [bool; #num_multiple]) -> Self { let mask: #base_type = #mask_name.iter().sum(); let update: #base_type = #mask_name @@ -507,6 +538,7 @@ fn generate_multiple_ux_field( snake_case_name: Ident, single_field_type: RustUxTypes, masks: Vec, + desc: &str, ) -> (TokenStream, TokenStream) { let field_type = single_field_type.to_rust_type_token(); let num_multiple = masks.len(); @@ -518,7 +550,9 @@ fn generate_multiple_ux_field( .unwrap() } }); + let getter_doc = format!("Getter. {}", desc); let code_getter = quote! { + #[doc = #getter_doc] pub fn #snake_case_name(&self) -> [#field_type; #num_multiple] { [ #(#elem_getter),* @@ -527,7 +561,9 @@ fn generate_multiple_ux_field( }; let setter_name = util::parse_to_ident(&format!("set_{}", snake_case_name)).unwrap(); + let setter_doc = format!("Setter. {}", desc); let code_setter = quote! { + #[doc = #setter_doc] pub fn #setter_name(&self, val: [#field_type; #num_multiple]) -> Self { let mask: #base_type = #mask_name.iter().sum(); let update: #base_type = #mask_name @@ -552,6 +588,7 @@ fn generate_multiple_custom_values_field( snake_case_name: Ident, masks: Vec, values: &[Value], + desc: &str, ) -> (TokenStream, TokenStream) { let value_enum_name = util::parse_to_ident(&snake_case_name.to_string().to_upper_camel_case()).unwrap(); @@ -582,7 +619,9 @@ fn generate_multiple_custom_values_field( } } }); + let getter_doc = format!("Getter. {}", desc); let code_getter = quote! { + #[doc = #getter_doc] pub fn #snake_case_name(&self) -> [#value_enum_name; #num_multiple] { [ #(#elem_getter),* @@ -598,7 +637,9 @@ fn generate_multiple_custom_values_field( } } }); + let setter_doc = format!("Setter. {}", desc); let code_setter = quote! { + #[doc = #setter_doc] pub fn #setter_name(&self, val: [#value_enum_name; #num_multiple]) -> Self { let val: [#base_type; #num_multiple] = [ #(#elem_setter),* From 7aba746503ba23bb26af0129fb33a62c4e568a6f Mon Sep 17 00:00:00 2001 From: testuser Date: Mon, 3 Feb 2025 17:53:35 +0900 Subject: [PATCH 38/45] fix(main): error dir name was changed --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index cfc8c41..9b443de 100644 --- a/src/main.rs +++ b/src/main.rs @@ -36,7 +36,7 @@ fn main() -> Result<()> { .collect_vec() .is_empty() { - return Err(anyhow!("dir out is not empty")); + return Err(anyhow!("dir register_interface is not empty")); }; for (file, code) in files { DirBuilder::new() From d85642dcec1aef6cb6f8dede18fcccffae597a90 Mon Sep 17 00:00:00 2001 From: testuser Date: Mon, 3 Feb 2025 19:09:35 +0900 Subject: [PATCH 39/45] new(generator): multiple register --- src/generator.rs | 64 +++++++++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 23 deletions(-) diff --git a/src/generator.rs b/src/generator.rs index 20b0507..b5eb016 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -208,30 +208,48 @@ impl CodeGen for Block { let addr = util::parse_to_literal(&format!("0x{:x}", self.addr))?; let desc = self.desc.unwrap_or("".to_string()); - let accessor_methods = { - let mut out = TokenStream::new(); - for e in self.elements.iter() { + let accessors_methods = self.elements.iter().map(|e| { let child_name = e.get_name(); let snake_case_name = util::parse_to_ident(&child_name.to_snake_case())?; - let child_type_name = match e { - ModuleBlockElements::Block(_) => &child_name.to_upper_camel_case(), - ModuleBlockElements::Register(_) => { - &format!("Reg{}", child_name.to_upper_camel_case()) - } - ModuleBlockElements::Memory(_) => todo!(), - ModuleBlockElements::Fifo(_) => todo!(), - }; - let child_upper_camel_name = util::parse_to_ident(child_type_name)?; - - out.extend(quote! { - pub fn #snake_case_name(&self) -> #snake_case_name::#child_upper_camel_name { - #snake_case_name::#child_upper_camel_name::new(self.mem_ptr) - } - }); - } - - out - }; + match e { + ModuleBlockElements::Block(_) => { + let child_upper_camel_name = util::parse_to_ident(&child_name.to_upper_camel_case())?; + Ok(quote! { + pub fn #snake_case_name(&self) -> #snake_case_name::#child_upper_camel_name { + #snake_case_name::#child_upper_camel_name::new(self.mem_ptr) + } + }) + }, + ModuleBlockElements::Register(register) => { + let child_upper_camel_name = util::parse_to_ident(&format!("Reg{}", child_name.to_upper_camel_case()))?; + match ®ister.multiple { + None => { + Ok(quote! { + pub fn #snake_case_name(&self) -> #snake_case_name::#child_upper_camel_name { + #snake_case_name::#child_upper_camel_name::new(self.mem_ptr) + } + }) + }, + Some(multiple_param) => { + let num_multiple = multiple_param.multiple; + let elements = (0..num_multiple).map(|i| { + let offset = multiple_param.offset * i; + quote! { + #snake_case_name::#child_upper_camel_name::new(unsafe { self.mem_ptr.add(#offset) } ) + } + }); + Ok(quote! { + pub fn #snake_case_name(&self) -> [#snake_case_name::#child_upper_camel_name; #num_multiple] { + [ #(#elements),* ] + } + }) + }, + } + }, + ModuleBlockElements::Memory(_memory) => todo!(), + ModuleBlockElements::Fifo(_fifo) => todo!(), + } + }).collect::, CodeGenError>>()?; let parent_struct = if parent_name == util::parse_to_ident("RegisterInterface").unwrap() { quote! {#parent_name} @@ -275,7 +293,7 @@ impl CodeGen for Block { } } - #accessor_methods + #(#accessors_methods)* } }; let (out_path, next_parent_path) = mod_file_path(parent_path, &snake_case_name); From ae01a30cb001e67c1f9d36b956691122fc1f5d6b Mon Sep 17 00:00:00 2001 From: testuser Date: Mon, 3 Feb 2025 19:41:03 +0900 Subject: [PATCH 40/45] fix(generator): add explicit cast to change types of array size & ptr offset --- src/generator.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/generator.rs b/src/generator.rs index b5eb016..362e464 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -231,9 +231,9 @@ impl CodeGen for Block { }) }, Some(multiple_param) => { - let num_multiple = multiple_param.multiple; + let num_multiple = multiple_param.multiple as usize; let elements = (0..num_multiple).map(|i| { - let offset = multiple_param.offset * i; + let offset = (multiple_param.offset as usize) * i; quote! { #snake_case_name::#child_upper_camel_name::new(unsafe { self.mem_ptr.add(#offset) } ) } From 472723016cbd23cdd2fb3376f90286996db3a744 Mon Sep 17 00:00:00 2001 From: testuser Date: Mon, 3 Feb 2025 19:55:05 +0900 Subject: [PATCH 41/45] update(generator): return error when block has multiple --- src/generator.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/generator.rs b/src/generator.rs index 362e464..2ab6d5c 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -15,7 +15,6 @@ use crate::{ types::{Block, Module, ModuleBlockElements, Register}, }; use heck::{ToSnakeCase, ToUpperCamelCase}; -use proc_macro2::TokenStream; use quote::quote; use thiserror::Error; @@ -27,6 +26,8 @@ pub enum CodeGenError { FilePathError(String), #[error("parent is required for {module}")] ParentMissing { module: &'static str }, + #[error("Unsupported structure: {}", name)] + UnsupportedStructure { name: &'static str }, } mod util { @@ -200,6 +201,12 @@ impl CodeGen for Block { parent_path: Option, mut files: HashMap, ) -> Result, CodeGenError> { + if self.multiple.is_some() { + // Plan: expand automatically, or same as register? + return Err(CodeGenError::UnsupportedStructure { + name: "multiple in block", + }); + } let parent_name = parent_name.ok_or(CodeGenError::ParentMissing { module: "Block" })?; let parent_path = parent_path.ok_or(CodeGenError::ParentMissing { module: "Block" })?; From a6c56ef9d6b58ce9fba1fef99adb8ad3513240eb Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Thu, 6 Feb 2025 18:54:11 +0900 Subject: [PATCH 42/45] refactor: make only top level codegen is public and CodeGen private --- src/generator.rs | 8 +++++++- src/main.rs | 6 +++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/generator.rs b/src/generator.rs index 2ab6d5c..ba137cc 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -120,7 +120,13 @@ mod util { } } -pub trait CodeGen { +impl Module { + pub fn generate_code(self) -> Result, CodeGenError> { + self.generate_register_interface(None, None, HashMap::new()) + } +} + +trait CodeGen { /// `parent_name` in UpperCamelCase. fn generate_register_interface( self, diff --git a/src/main.rs b/src/main.rs index 9b443de..3a43e59 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,9 @@ use std::fs::DirBuilder; use std::io::Write; -use std::{collections::HashMap, fs, io::BufWriter}; +use std::{fs, io::BufWriter}; use anyhow::{anyhow, Context, Result}; -use endcap_sl_software_ri_generator::{generator::CodeGen, types}; +use endcap_sl_software_ri_generator::types; use itertools::Itertools; fn main() -> Result<()> { @@ -26,7 +26,7 @@ fn main() -> Result<()> { println!("==========================================="); - let files = register_map.generate_register_interface(None, None, HashMap::new())?; + let files = register_map.generate_code()?; // println!("{:#?}", files); for filepath in files.keys().sorted() { println!("{}", filepath.display()); From 76c19d194d1c5680752f2bef8ae5d960fd883bc0 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Thu, 6 Feb 2025 20:58:59 +0900 Subject: [PATCH 43/45] new: file IO & formatting & update docs with an example --- Cargo.lock | 11 +++++++++++ Cargo.toml | 1 + src/generator.rs | 24 +++++++++++++++++------- src/io.rs | 27 ++++++++++++++++++++++++++ src/lib.rs | 35 +++++++++++++++++++++++++++++----- src/main.rs | 49 +++++++++++++++++------------------------------- 6 files changed, 103 insertions(+), 44 deletions(-) create mode 100644 src/io.rs diff --git a/Cargo.lock b/Cargo.lock index c18c3b5..dff8e1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -88,6 +88,7 @@ dependencies = [ "heck", "itertools", "log", + "prettyplease", "proc-macro2", "quote", "roxmltree", @@ -163,6 +164,16 @@ version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +[[package]] +name = "prettyplease" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" version = "1.0.93" diff --git a/Cargo.toml b/Cargo.toml index 755bd1c..6bd8bcc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ env_logger = "0.11.6" heck = "0.5" itertools = "0.14" log = "0.4" +prettyplease = "0.2" proc-macro2 = "1.0.93" quote = "1.0" roxmltree = "0.20" diff --git a/src/generator.rs b/src/generator.rs index ba137cc..a4c6d95 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -1,9 +1,9 @@ //! Generate register interface rust code from types in [`crate::types`]. //! -//! root: [`CodeGen`] +//! root: [`Module::generate_code`] //! -//! # TODO -//! add docs (especially fields and registers) +//! # For developers +//! Pass `--document-private-items` to see non-public items. use std::{ collections::HashMap, @@ -15,13 +15,14 @@ use crate::{ types::{Block, Module, ModuleBlockElements, Register}, }; use heck::{ToSnakeCase, ToUpperCamelCase}; +use itertools::Itertools; use quote::quote; use thiserror::Error; #[derive(Debug, Error)] pub enum CodeGenError { - #[error("tokenization error: {0}")] - TokenizeError(#[from] syn::Error), + #[error("tokenization(syn) error: {0}")] + SynError(#[from] syn::Error), #[error("failed to create file: {0}")] FilePathError(String), #[error("parent is required for {module}")] @@ -121,8 +122,17 @@ mod util { } impl Module { - pub fn generate_code(self) -> Result, CodeGenError> { - self.generate_register_interface(None, None, HashMap::new()) + pub fn generate_code(self) -> Result, CodeGenError> { + let files = self.generate_register_interface(None, None, HashMap::new())?; + Ok(files + .into_iter() + .map( + |(path, tokens)| -> Result<(PathBuf, syn::File), syn::Error> { + let file: syn::File = syn::parse2(tokens)?; + Ok((path, file)) + }, + ) + .process_results(|kv| HashMap::from_iter(kv))?) } } diff --git a/src/io.rs b/src/io.rs new file mode 100644 index 0000000..2f345da --- /dev/null +++ b/src/io.rs @@ -0,0 +1,27 @@ +//! File IO for generated codes. + +use std::{collections::HashMap, fs, io, path}; + +/// Write formatted codes generated with [`Module::generate_code`](crate::types::Module::generate_code). +pub fn write_to_files( + files: HashMap, + out_path: &path::Path, +) -> io::Result<()> { + if !out_path.is_dir() { + return Err(io::Error::from(io::ErrorKind::NotADirectory)); + } + if fs::read_dir(out_path)?.next().is_some() { + return Err(io::Error::new( + io::ErrorKind::AlreadyExists, + "out path is not empty", + )); + } + for (file_path, code) in files { + fs::DirBuilder::new() + .recursive(true) + .create(out_path.join(&file_path).parent().unwrap())?; + + fs::write(out_path.join(&file_path), prettyplease::unparse(&code))?; + } + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index 49b6578..4129c60 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,27 @@ -//! Root document [`types::Module::from_xml_dom`] +//! Generate register interface software from register map in XML. +//! +//! # Example +//! +//! Here's a typical usage: +//! ```no_run +//! use endcap_sl_software_ri_generator::types; +//! +//! let xmlfile = std::fs::read_to_string("./csr.xml")?; +//! let doc = roxmltree::Document::parse_with_options( +//! &xmlfile, +//! roxmltree::ParsingOptions { +//! allow_dtd: true, +//! nodes_limit: u32::MAX, +//! }, +//! )?; +//! +//! let register_map = types::Module::from_xml_dom(doc.root_element())?; +//! +//! let files = register_map.generate_code()?; +//! endcap_sl_software_ri_generator::write_to_files(files, &std::path::PathBuf::from("out"))?; +//! +//! # Ok::<(), anyhow::Error>(()) +//! ``` //! //! # Overview //! @@ -9,13 +32,15 @@ //! //! # modules //! - [`types`]: type definitions of internal register map representation -//! - [`type_traits`]: supplemental traits for types in [`types`] -//! - [`parser`]: supplemental parser (string to custom types) //! - [`converter`]: DOM to internal representation //! - [`generator`]: internal representation to rust code +//! - [`io`]: formatting and printing pub mod converter; pub mod generator; -pub mod parser; -pub mod type_traits; +pub mod io; +mod parser; +mod type_traits; pub mod types; + +pub use io::write_to_files; diff --git a/src/main.rs b/src/main.rs index 3a43e59..a3a63bf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,13 @@ -use std::fs::DirBuilder; -use std::io::Write; -use std::{fs, io::BufWriter}; +use std::fs; -use anyhow::{anyhow, Context, Result}; +use anyhow::Result; use endcap_sl_software_ri_generator::types; use itertools::Itertools; fn main() -> Result<()> { env_logger::init(); + log::debug!("logger enabled"); - println!("Hello, world!"); let xmlfile = fs::read_to_string("./csr.xml")?; let doc = roxmltree::Document::parse_with_options( &xmlfile, @@ -18,39 +16,26 @@ fn main() -> Result<()> { nodes_limit: u32::MAX, }, )?; - // println!("Parsed: {:#?}", doc); - // println!("Root: {:?}", doc.root_element()); + log::debug!("Parsed: {:#?}", doc); let register_map = types::Module::from_xml_dom(doc.root_element())?; - println!("read: {:#?}", register_map); - - println!("==========================================="); + log::info!("read: {:?}", register_map); + log::debug!("read: {:#?}", register_map); let files = register_map.generate_code()?; - // println!("{:#?}", files); - for filepath in files.keys().sorted() { - println!("{}", filepath.display()); + if log::log_enabled!(log::Level::Debug) { + for (path, code) in &files { + log::debug!("path: {:?}", path); + log::debug!("{}", prettyplease::unparse(code)); + } } - if !std::fs::read_dir("register_interface") - .context("register_interface not found")? - .collect_vec() - .is_empty() - { - return Err(anyhow!("dir register_interface is not empty")); - }; - for (file, code) in files { - DirBuilder::new() - .recursive(true) - .create(file.parent().context("no parent")?)?; - let file = fs::OpenOptions::new() - .create(true) - .truncate(true) - .write(true) - .open(file) - .context("opening file")?; - let mut writer = BufWriter::new(file); - write!(writer, "{}", code)?; + if log::log_enabled!(log::Level::Info) { + for filepath in files.keys().sorted() { + log::info!("{}", filepath.display()); + } } + endcap_sl_software_ri_generator::write_to_files(files, &std::path::PathBuf::from("out"))?; + Ok(()) } From d41bc7c1b0b77855363eb5cb6826f9b60c771cc2 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Fri, 7 Feb 2025 17:47:49 +0900 Subject: [PATCH 44/45] add: dependencies --- Cargo.lock | 697 +++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 8 + 2 files changed, 705 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index dff8e1c..77d25ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "0.6.18" @@ -67,12 +82,229 @@ version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "camino" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8769706aad5d996120af43197bf46ef6ad0fda35216b4505f926a365a232d924" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cc" +version = "1.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "755717a7de9ec452bf7f3f1a3099085deabd7f2962b861dae91ecd7a365903d2" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets", +] + [[package]] name = "colorchoice" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "either" version = "1.13.0" @@ -84,16 +316,21 @@ name = "endcap-sl-software-ri-generator" version = "0.1.0" dependencies = [ "anyhow", + "chrono", "env_logger", "heck", + "hex", "itertools", "log", "prettyplease", "proc-macro2", "quote", "roxmltree", + "sha2", "syn", "thiserror", + "typenum", + "vergen-gitcl", ] [[package]] @@ -119,18 +356,69 @@ dependencies = [ "log", ] +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core 0.52.0", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -146,6 +434,28 @@ dependencies = [ "either", ] +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + [[package]] name = "log" version = "0.4.25" @@ -158,12 +468,51 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + [[package]] name = "once_cell" version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "prettyplease" version = "0.2.29" @@ -192,6 +541,26 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "regex" version = "1.11.1" @@ -227,6 +596,91 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" + +[[package]] +name = "ryu" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" + +[[package]] +name = "semver" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" +dependencies = [ + "serde", +] + +[[package]] +name = "serde" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.138" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "syn" version = "2.0.96" @@ -238,6 +692,20 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sysinfo" +version = "0.33.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fc858248ea01b66f19d8e8a6d55f41deaf91e9d495246fd01368d99935c6c01" +dependencies = [ + "core-foundation-sys", + "libc", + "memchr", + "ntapi", + "rayon", + "windows", +] + [[package]] name = "thiserror" version = "2.0.11" @@ -258,6 +726,45 @@ dependencies = [ "syn", ] +[[package]] +name = "time" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +dependencies = [ + "deranged", + "itoa", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + [[package]] name = "unicode-ident" version = "1.0.16" @@ -270,6 +777,196 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "vergen" +version = "9.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0d2f179f8075b805a43a2a21728a46f0cc2921b3c58695b28fa8817e103cd9a" +dependencies = [ + "anyhow", + "cargo_metadata", + "derive_builder", + "regex", + "rustc_version", + "rustversion", + "sysinfo", + "time", + "vergen-lib", +] + +[[package]] +name = "vergen-gitcl" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2f89d70a58a4506a6079cedf575c64cf51649ccbb4e02a63dac539b264b7711" +dependencies = [ + "anyhow", + "derive_builder", + "rustversion", + "time", + "vergen", + "vergen-lib", +] + +[[package]] +name = "vergen-lib" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b07e6010c0f3e59fcb164e0163834597da68d1f864e2b8ca49f74de01e9c166" +dependencies = [ + "anyhow", + "derive_builder", + "rustversion", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" +dependencies = [ + "windows-core 0.57.0", + "windows-targets", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-core" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-targets", +] + +[[package]] +name = "windows-implement" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.59.0" diff --git a/Cargo.toml b/Cargo.toml index 6bd8bcc..5b26b33 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ name = "endcap-sl-software-ri-generator" version = "0.1.0" edition = "2021" +build = "build.rs" [[bin]] name = "endcap-sl-software-ri-generator" @@ -13,13 +14,20 @@ path = "src/lib.rs" [dependencies] anyhow = "1.0.95" +chrono = "0.4.39" env_logger = "0.11.6" heck = "0.5" +hex = "0.4.3" itertools = "0.14" log = "0.4" prettyplease = "0.2" proc-macro2 = "1.0.93" quote = "1.0" roxmltree = "0.20" +sha2 = "0.10" syn = "2.0.96" thiserror = "2.0" +typenum = "1.17.0" + +[build-dependencies] +vergen-gitcl = { version = "1.0.0", features = ["build", "cargo", "rustc", "si"] } From b0ceb39278d2f9a272da65bfab9329ea5fbb7ef3 Mon Sep 17 00:00:00 2001 From: Wataru Otsubo Date: Fri, 7 Feb 2025 17:49:34 +0900 Subject: [PATCH 45/45] new: embed generation metadata (generator & xml) --- build.rs | 18 ++++++++++++++++++ src/converter.rs | 2 ++ src/generator.rs | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/main.rs | 1 + src/meta.rs | 9 +++++++++ src/types.rs | 1 + 7 files changed, 76 insertions(+) create mode 100644 build.rs create mode 100644 src/meta.rs diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..2e8f364 --- /dev/null +++ b/build.rs @@ -0,0 +1,18 @@ +use vergen_gitcl::{BuildBuilder, Emitter, GitclBuilder}; + +fn main() -> Result<(), Box> { + // Get build metadata + let build = BuildBuilder::default().build_timestamp(true).build()?; + let gitcl = GitclBuilder::default() + .commit_date(true) + .describe(false, true, None) + .commit_timestamp(true) + .sha(false) + .build()?; + Emitter::default() + .add_instructions(&build)? + .add_instructions(&gitcl)? + .emit()?; + + Ok(()) +} diff --git a/src/converter.rs b/src/converter.rs index 426ba4d..3ccc699 100644 --- a/src/converter.rs +++ b/src/converter.rs @@ -7,6 +7,7 @@ use std::{num, str}; use roxmltree::{Node, TextPos}; +use sha2::{Digest, Sha256}; use thiserror::Error; use crate::parser::{ParseCustomBool, ParseEnumError, ParsePrefixedU32, ParsePrefixedU32Error}; @@ -210,6 +211,7 @@ impl Module { desc: node.attribute("desc").map(str::to_string), elements_bitstring: child_bitstrings, elements_other: child_other, + xmlhash: Sha256::digest(node.document().input_text()).into(), }) } } diff --git a/src/generator.rs b/src/generator.rs index a4c6d95..7e9c2bc 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -11,9 +11,14 @@ use std::{ }; use crate::{ + meta::{ + GENERATOR_BUILD_TIMESTAMP, GENERATOR_GIT_COMMIT_TIMESTAMP, GENERATOR_GIT_DESCRIBE, + GENERATOR_GIT_SHA, + }, type_traits::GetName, types::{Block, Module, ModuleBlockElements, Register}, }; +use chrono::Local; use heck::{ToSnakeCase, ToUpperCamelCase}; use itertools::Itertools; use quote::quote; @@ -123,11 +128,50 @@ mod util { impl Module { pub fn generate_code(self) -> Result, CodeGenError> { + let build_metadata = format!( + " +# Build metadata + +- timestamp: {} + +## CSR XML + +- sha256: {} +- git describe: TODO (after building step is fixed) +- git commit timestamp: TODO +- git SHA: TODO + +## Generator + +- build timestamp: {} +- git describe: {} +- git commit timestamp: {} +- git SHA: {} +", + Local::now().to_rfc3339_opts(chrono::SecondsFormat::Nanos, false), + hex::encode(self.xmlhash), + GENERATOR_BUILD_TIMESTAMP, + GENERATOR_GIT_DESCRIBE, + GENERATOR_GIT_COMMIT_TIMESTAMP, + GENERATOR_GIT_SHA, + ); let files = self.generate_register_interface(None, None, HashMap::new())?; Ok(files .into_iter() .map( |(path, tokens)| -> Result<(PathBuf, syn::File), syn::Error> { + let tokens = if path + .file_name() + .is_some_and(|file| file == "register_interface.rs") + { + quote! { + #![doc = #build_metadata] + + #tokens + } + } else { + tokens + }; let file: syn::File = syn::parse2(tokens)?; Ok((path, file)) }, diff --git a/src/lib.rs b/src/lib.rs index 4129c60..8430a7c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,6 +39,7 @@ pub mod converter; pub mod generator; pub mod io; +pub mod meta; mod parser; mod type_traits; pub mod types; diff --git a/src/main.rs b/src/main.rs index a3a63bf..41b0963 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,6 +27,7 @@ fn main() -> Result<()> { for (path, code) in &files { log::debug!("path: {:?}", path); log::debug!("{}", prettyplease::unparse(code)); + log::trace!("{}", prettyplease::unparse(code)); } } if log::log_enabled!(log::Level::Info) { diff --git a/src/meta.rs b/src/meta.rs new file mode 100644 index 0000000..c9d5d66 --- /dev/null +++ b/src/meta.rs @@ -0,0 +1,9 @@ +pub(crate) const GENERATOR_BUILD_TIMESTAMP: &str = + env!("VERGEN_BUILD_TIMESTAMP", "Failed to get build timestamp"); +pub(crate) const GENERATOR_GIT_DESCRIBE: &str = + env!("VERGEN_GIT_DESCRIBE", "Failed to get git describe"); +pub(crate) const GENERATOR_GIT_COMMIT_TIMESTAMP: &str = env!( + "VERGEN_GIT_COMMIT_TIMESTAMP", + "Failed to get git commit timestamp" +); +pub(crate) const GENERATOR_GIT_SHA: &str = env!("VERGEN_GIT_SHA", "Failed to get git sha"); diff --git a/src/types.rs b/src/types.rs index 65cc783..e074786 100644 --- a/src/types.rs +++ b/src/types.rs @@ -10,6 +10,7 @@ pub struct Module { pub desc: Option, pub elements_bitstring: Vec, pub elements_other: Vec, + pub xmlhash: [u8; 32], } #[derive(Debug)]