mirror of
https://gitlab.cern.ch/wotsubo/endcap-sl-software-ri-generator.git
synced 2025-02-23 00:57:08 +09:00
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...)
This commit is contained in:
parent
eec855c3a1
commit
d104e62bf3
8 changed files with 633 additions and 15 deletions
17
Cargo.lock
generated
17
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
204
src/generator.rs
204
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<proc_macro2::Ident, CodeGenError> {
|
||||
Ok(syn::parse_str(s)?)
|
||||
}
|
||||
|
||||
pub(super) fn parse_to_literal(s: &str) -> Result<proc_macro2::Literal, CodeGenError> {
|
||||
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<proc_macro2::TokenStream, CodeGenError>;
|
||||
}
|
||||
|
||||
impl CodeGen for Module {
|
||||
fn generate_register_interface(self) -> proc_macro2::TokenStream {
|
||||
fn generate_register_interface(self) -> Result<proc_macro2::TokenStream, CodeGenError> {
|
||||
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<proc_macro2::TokenStream, CodeGenError> {
|
||||
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<proc_macro2::TokenStream, CodeGenError> {
|
||||
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<proc_macro2::TokenStream, CodeGenError> {
|
||||
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<u32> for SlId {
|
||||
type Error = DataConversionError<u32, Self>;
|
||||
|
||||
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||
let mask = 0x0000003f;
|
||||
Ok(SlId(value & mask))
|
||||
}
|
||||
}
|
||||
impl From<SlId> for u32 {
|
||||
fn from(val: SlId) -> Self {
|
||||
val.0
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
412
src/generator/codegen_register.rs
Normal file
412
src/generator/codegen_register.rs
Normal file
|
@ -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<Ux> for T` and `impl From<T> 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<Self, Self::Error> {
|
||||
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<TokenStream>,
|
||||
Vec<TokenStream>,
|
||||
Vec<TokenStream>,
|
||||
) = 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<Self, Self::Error> {
|
||||
Ok(Self { inner: value })
|
||||
}
|
||||
}
|
||||
impl From<GtyDelayBank123> 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<Ident>,
|
||||
) -> (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<Ident>,
|
||||
) -> (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)
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -126,7 +126,7 @@ pub enum AmodValues {
|
|||
USER2,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum DataType {
|
||||
D32,
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue