diff --git a/src/config.rs b/src/config.rs index 644e59a1..0c24116f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -3,6 +3,7 @@ use std::path::{Path, PathBuf}; #[cfg_attr(feature = "serde", derive(serde::Deserialize), serde(default))] #[derive(Clone, PartialEq, Eq, Debug, Default)] +#[non_exhaustive] pub struct Config { pub target: Target, pub atomics: bool, @@ -13,7 +14,6 @@ pub struct Config { pub ignore_groups: bool, pub keep_list: bool, pub strict: bool, - pub pascal_enum_values: bool, pub feature_group: bool, pub feature_peripheral: bool, pub max_cluster_size: bool, @@ -135,6 +135,7 @@ pub enum Case { #[derive(Clone, Debug, Default, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize), serde(default))] pub struct IdentFormat { + // Ident case. `None` means don't change pub case: Option, pub prefix: String, pub suffix: String, @@ -153,8 +154,8 @@ impl IdentFormat { self.case = Some(Case::Pascal); self } - pub fn scake_case(mut self) -> Self { - self.case = Some(Case::Pascal); + pub fn snake_case(mut self) -> Self { + self.case = Some(Case::Snake); self } pub fn prefix(mut self, prefix: &str) -> Self { @@ -177,24 +178,38 @@ pub struct IdentFormats { pub enum_value: IdentFormat, pub interrupt: IdentFormat, pub cluster: IdentFormat, + pub cluster_accessor: IdentFormat, + //pub cluster_mod: IdentFormat, pub register: IdentFormat, pub register_spec: IdentFormat, + pub register_accessor: IdentFormat, + //pub register_mod: IdentFormat, pub peripheral: IdentFormat, + pub peripheral_sigleton: IdentFormat, + //pub peripheral_mod: IdentFormat, + pub peripheral_feature: IdentFormat, } impl Default for IdentFormats { fn default() -> Self { Self { - field_reader: IdentFormat::default().constant_case().suffix("_R"), - field_writer: IdentFormat::default().constant_case().suffix("_W"), - enum_name: IdentFormat::default().constant_case().suffix("_A"), - enum_write_name: IdentFormat::default().constant_case().suffix("_AW"), - enum_value: IdentFormat::default().constant_case(), - interrupt: IdentFormat::default().constant_case(), - cluster: IdentFormat::default().constant_case(), - register: IdentFormat::default().constant_case(), - register_spec: IdentFormat::default().constant_case().suffix("_SPEC"), - peripheral: IdentFormat::default().constant_case(), + field_reader: IdentFormat::default().pascal_case().suffix("R"), + field_writer: IdentFormat::default().pascal_case().suffix("W"), + enum_name: IdentFormat::default().pascal_case().suffix("A"), + enum_write_name: IdentFormat::default().pascal_case().suffix("AW"), + enum_value: IdentFormat::default().pascal_case(), + interrupt: IdentFormat::default(), + cluster: IdentFormat::default().pascal_case(), + cluster_accessor: IdentFormat::default().snake_case(), + //cluster_mod: IdentFormat::default().snake_case(), + register: IdentFormat::default().pascal_case(), + register_spec: IdentFormat::default().pascal_case().suffix("Spec"), + register_accessor: IdentFormat::default().snake_case(), + //register_mod: IdentFormat::default().snake_case(), + peripheral: IdentFormat::default().pascal_case(), + peripheral_sigleton: IdentFormat::default().snake_case(), + //peripheral_mod: IdentFormat::default().snake_case(), + peripheral_feature: IdentFormat::default().snake_case(), } } } diff --git a/src/generate/device.rs b/src/generate/device.rs index 5251c7ba..9dbf69df 100644 --- a/src/generate/device.rs +++ b/src/generate/device.rs @@ -8,7 +8,7 @@ use std::io::Write; use std::path::Path; use crate::config::{Config, Target}; -use crate::util::{self, ident, ToSanitizedCase}; +use crate::util::{self, ident}; use anyhow::{Context, Result}; use crate::generate::{interrupt, peripheral}; @@ -226,7 +226,10 @@ pub fn render(d: &Device, config: &Config, device_x: &mut String) -> Result Result { let p_name = util::name_of(p, config.ignore_groups); - let p_snake = p_name.to_sanitized_snake_case(); + let p_feature = config.ident_formats.peripheral_feature.apply(&p_name); let p_ty = ident(&p_name, &config.ident_formats.peripheral, span); + let p_singleton = ident(&p_name, &config.ident_formats.peripheral_sigleton, span); if config.feature_peripheral { - feature_attribute.extend(quote! { #[cfg(feature = #p_snake)] }) + feature_attribute.extend(quote! { #[cfg(feature = #p_feature)] }) }; fields.extend(quote! { #[doc = #p_name] #feature_attribute - pub #p_ty: #p_ty, + pub #p_singleton: #p_ty, }); - exprs.extend(quote!(#feature_attribute #p_ty: #p_ty { _marker: PhantomData },)); + exprs.extend( + quote!(#feature_attribute #p_singleton: #p_ty { _marker: PhantomData },), + ); } Peripheral::Array(p, dim_element) => { for p_name in names(p, dim_element) { - let p_snake = p_name.to_sanitized_snake_case(); + let p_feature = config.ident_formats.peripheral_feature.apply(&p_name); let p_ty = ident(&p_name, &config.ident_formats.peripheral, span); + let p_singleton = + ident(&p_name, &config.ident_formats.peripheral_sigleton, span); if config.feature_peripheral { - feature_attribute.extend(quote! { #[cfg(feature = #p_snake)] }) + feature_attribute.extend(quote! { #[cfg(feature = #p_feature)] }) }; fields.extend(quote! { #[doc = #p_name] #feature_attribute - pub #p_ty: #p_ty, + pub #p_singleton: #p_ty, }); - exprs.extend(quote!(#feature_attribute #p_ty: #p_ty { _marker: PhantomData },)); + exprs.extend( + quote!(#feature_attribute #p_singleton: #p_ty { _marker: PhantomData },), + ); } } } diff --git a/src/generate/peripheral.rs b/src/generate/peripheral.rs index 5f1cae85..66f48907 100644 --- a/src/generate/peripheral.rs +++ b/src/generate/peripheral.rs @@ -77,18 +77,18 @@ pub fn render(p_original: &Peripheral, index: &Index, config: &Config) -> Result match &p { Peripheral::Array(p, dim) => { - let mut snake_names = Vec::with_capacity(dim.dim as _); + let mut feature_names = Vec::with_capacity(dim.dim as _); for pi in svd::peripheral::expand(p, dim) { let name = &pi.name; let description = pi.description.as_deref().unwrap_or(&p.name); let p_ty = ident(name, &config.ident_formats.peripheral, span); let name_str = p_ty.to_string(); let address = util::hex(pi.base_address + config.base_address_shift); - let p_snake = name.to_sanitized_snake_case(); - snake_names.push(p_snake.to_string()); + let feature_name = config.ident_formats.peripheral_feature.apply(name); + feature_names.push(feature_name.to_string()); let mut feature_attribute_n = feature_attribute.clone(); if config.feature_peripheral { - feature_attribute_n.extend(quote! { #[cfg(feature = #p_snake)] }) + feature_attribute_n.extend(quote! { #[cfg(feature = #feature_name)] }) }; // Insert the peripherals structure out.extend(quote! { @@ -132,7 +132,7 @@ pub fn render(p_original: &Peripheral, index: &Index, config: &Config) -> Result }); } - let feature_any_attribute = quote! {#[cfg(any(#(feature = #snake_names),*))]}; + let feature_any_attribute = quote! {#[cfg(any(#(feature = #feature_names),*))]}; // Derived peripherals may not require re-implementation, and will instead // use a single definition of the non-derived version. @@ -147,9 +147,9 @@ pub fn render(p_original: &Peripheral, index: &Index, config: &Config) -> Result } } Peripheral::Single(_) => { - let p_snake = name.to_sanitized_snake_case(); + let feature_name = config.ident_formats.peripheral_feature.apply(&name); if config.feature_peripheral { - feature_attribute.extend(quote! { #[cfg(feature = #p_snake)] }) + feature_attribute.extend(quote! { #[cfg(feature = #feature_name)] }) }; // Insert the peripheral structure out.extend(quote! { @@ -981,12 +981,13 @@ fn expand_cluster(cluster: &Cluster, config: &Config) -> Result { let doc = make_comment(cluster_size, info.address_offset, &description); - let name: Ident = info.name.to_snake_case_ident(Span::call_site()); + let name: Ident = ident(&info.name, &config.ident_formats.cluster_accessor, span); let syn_field = new_syn_field(name.clone(), ty.clone()); cluster_expanded.push(RegisterBlockField { syn_field, @@ -1029,12 +1030,15 @@ fn expand_cluster(cluster: &Cluster, config: &Config) -> Result Result Result Result Result Result { let doc = make_comment(register_size, info.address_offset, &description); - let ty = name_to_ty(&ty_name); - let name = ty_name.to_snake_case_ident(Span::call_site()); + let span = Span::call_site(); + let ty = name_to_ty(ident(&ty_name, &config.ident_formats.register, span)); + let name: Ident = ident(&ty_name, &config.ident_formats.register_accessor, span); let syn_field = new_syn_field(name.clone(), ty.clone()); register_expanded.push(RegisterBlockField { syn_field, @@ -1210,15 +1215,18 @@ fn expand_register( }; let array_convertible = ac && sequential_addresses; let array_proxy_convertible = ac && disjoint_sequential_addresses; - let ty = name_to_ty(&ty_name); + let span = Span::call_site(); + let ty = name_to_ty(ident(&ty_name, &config.ident_formats.register, span)); if array_convertible || array_proxy_convertible { - let span = Span::call_site(); - let nb_name_sc = if let Some(dim_name) = array_info.dim_name.as_ref() { - util::fullname(dim_name, &info.alternate_group, config.ignore_groups) - .to_snake_case_ident(span) + let accessor_name = if let Some(dim_name) = array_info.dim_name.as_ref() { + ident( + &util::fullname(dim_name, &info.alternate_group, config.ignore_groups), + &config.ident_formats.register, + span, + ) } else { - ty_name.to_snake_case_ident(span) + ident(&ty_name, &config.ident_formats.register, span) }; let doc = make_comment( register_size * array_info.dim, @@ -1229,7 +1237,7 @@ fn expand_register( accessors.push(if array_convertible { ArrayAccessor { doc, - name: nb_name_sc.clone(), + name: accessor_name.clone(), ty: ty.clone(), offset: unsuffixed(info.address_offset), dim: unsuffixed(array_info.dim), @@ -1239,7 +1247,7 @@ fn expand_register( } else { RawArrayAccessor { doc, - name: nb_name_sc.clone(), + name: accessor_name.clone(), ty: ty.clone(), offset: unsuffixed(info.address_offset), dim: unsuffixed(array_info.dim), @@ -1263,7 +1271,7 @@ fn expand_register( doc, name: idx_name, ty: ty.clone(), - basename: nb_name_sc.clone(), + basename: accessor_name.clone(), i, } .into(), @@ -1275,7 +1283,7 @@ fn expand_register( } else { zst_type() }; - let syn_field = new_syn_field(nb_name_sc, array_ty); + let syn_field = new_syn_field(accessor_name, array_ty); register_expanded.push(RegisterBlockField { syn_field, offset: info.address_offset, @@ -1293,7 +1301,7 @@ fn expand_register( info.address_offset, ri.description.as_deref().unwrap_or(&ri.name), ); - let name = ri.name.to_snake_case_ident(Span::call_site()); + let name = ident(&ri.name, &config.ident_formats.register, span); let syn_field = new_syn_field(name.clone(), ty.clone()); register_expanded.push(RegisterBlockField { diff --git a/src/generate/register.rs b/src/generate/register.rs index 21fb1746..a04364a1 100644 --- a/src/generate/register.rs +++ b/src/generate/register.rs @@ -12,6 +12,7 @@ use std::{borrow::Cow, collections::BTreeMap}; use svd_parser::expand::{ derive_enumerated_values, derive_field, BlockPath, EnumPath, FieldPath, Index, RegisterPath, }; +use util::IdentFormat; use crate::config::Config; use crate::util::{ @@ -469,8 +470,8 @@ fn render_register_mod_debug( if field_access.can_read() && f.read_action.is_none() { if let Field::Array(_, de) = &f { for suffix in de.indexes() { - let f_name_n = util::replace_suffix(&f.name, &suffix) - .to_snake_case_ident(Span::call_site()); + let f_name_n = + util::replace_suffix(&f.name, &suffix).to_snake_case_ident(span); let f_name_n_s = format!("{f_name_n}"); r_debug_impl.extend(quote! { .field(#f_name_n_s, &format_args!("{}", self.#f_name_n().#bit_or_bits())) @@ -826,16 +827,7 @@ pub fn fields( // for each variant defined, we generate an `is_variant` function. for v in &variants { let pc = &v.pc; - let sc = &v.nksc; - - let is_variant = Ident::new( - &if sc.to_string().starts_with('_') { - format!("is{sc}") - } else { - format!("is_{sc}") - }, - span, - ); + let is_variant = &v.is_sc; let doc = util::escape_special_chars(&util::respace(&v.doc)); enum_items.extend(quote! { @@ -848,16 +840,7 @@ pub fn fields( } if let Some(v) = def.as_ref() { let pc = &v.pc; - let sc = &v.nksc; - - let is_variant = Ident::new( - &if sc.to_string().starts_with('_') { - format!("is{sc}") - } else { - format!("is_{sc}") - }, - span, - ); + let is_variant = &v.is_sc; let doc = util::escape_special_chars(&util::respace(&v.doc)); enum_items.extend(quote! { @@ -1274,7 +1257,7 @@ fn unsafety(write_constraint: Option<&WriteConstraint>, width: u32) -> bool { struct Variant { doc: String, pc: Ident, - nksc: Ident, + is_sc: Ident, sc: Ident, value: u64, } @@ -1296,16 +1279,34 @@ impl Variant { } fn from_value(value: u64, ev: &EnumeratedValue, config: &Config) -> Result { let span = Span::call_site(); - let nksc = ev.name.to_sanitized_not_keyword_snake_case(); - let sc = util::sanitize_keyword(nksc.clone()); + let case = IdentFormat::default().snake_case(); + let nksc = case.apply(&ev.name); + let is_sc = Ident::new( + &if nksc.to_string().starts_with('_') { + format!("is{nksc}") + } else { + format!("is_{nksc}") + }, + span, + ); + let sc = case.sanitize(&ev.name); + const INTERNALS: [&str; 4] = ["set_bit", "clear_bit", "bit", "bits"]; + let sc = Ident::new( + &(if INTERNALS.contains(&sc.as_ref()) { + sc + "_" + } else { + sc + }), + span, + ); Ok(Variant { doc: ev .description .clone() .unwrap_or_else(|| format!("`{value:b}`")), pc: ident(&ev.name, &config.ident_formats.enum_value, span), - nksc: Ident::new(&nksc, span), - sc: Ident::new(&sc, span), + is_sc, + sc, value, }) } diff --git a/src/main.rs b/src/main.rs index 6c366fea..f8ace936 100755 --- a/src/main.rs +++ b/src/main.rs @@ -171,14 +171,6 @@ fn run() -> Result<()> { .action(ArgAction::SetTrue) .help("Make advanced checks due to parsing SVD"), ) - // TODO: deprecate - .arg( - Arg::new("pascal_enum_values") - .long("pascal-enum-values") - .alias("pascal_enum_values") - .action(ArgAction::SetTrue) - .help("Use PascalCase in stead of CONSTANT_CASE for enumerated values"), - ) .arg( Arg::new("source_type") .long("source-type") @@ -260,9 +252,6 @@ Ignore this option if you are not building your own FPGA based soft-cores."), config.source_type = SourceType::from_path(file) } let path = config.output_dir.as_deref().unwrap_or(Path::new(".")); - if config.pascal_enum_values { - config.ident_formats.enum_value.case = Some(svd2rust::config::Case::Pascal); - } info!("Parsing device from SVD file"); let device = load_from(input, &config)?; diff --git a/src/util.rs b/src/util.rs index fd80df55..37f17e10 100644 --- a/src/util.rs +++ b/src/util.rs @@ -54,13 +54,41 @@ fn sanitize(s: &str) -> Cow<'_, str> { } pub fn ident(name: &str, fmt: &IdentFormat, span: Span) -> Ident { + Ident::new(&fmt.sanitize(name), span) +} + +impl IdentFormat { + pub fn apply<'a>(&self, name: &'a str) -> Cow<'a, str> { + let name = match &self.case { + Some(case) => case.sanitize(name), + _ => sanitize(name), + }; + if self.prefix.is_empty() && self.suffix.is_empty() { + name + } else { + format!("{}{}{}", self.prefix, name, self.suffix).into() + } + } + pub fn sanitize<'a>(&self, name: &'a str) -> Cow<'a, str> { + let s = self.apply(name); + let s = if s.as_bytes().first().unwrap_or(&0).is_ascii_digit() { + Cow::from(format!("_{}", s)) + } else { + s + }; + match self.case { + Some(Case::Snake) | None => sanitize_keyword(s), + _ => s, + } + } +} + +pub fn ident_str(name: &str, fmt: &IdentFormat) -> String { let name = match &fmt.case { - Some(Case::Constant) => name.to_sanitized_constant_case(), - Some(Case::Pascal) => name.to_sanitized_pascal_case(), - Some(Case::Snake) => name.to_sanitized_snake_case(), + Some(case) => case.sanitize(name), _ => sanitize(name), }; - Ident::new(&format!("{}{}{}", fmt.prefix, name, fmt.suffix), span) + format!("{}{}{}", fmt.prefix, name, fmt.suffix) } /// Convert self string into specific case without overlapping to svd2rust internal names @@ -281,10 +309,9 @@ pub fn zst_type() -> Type { }) } -pub fn name_to_ty(name: &str) -> Type { - let span = Span::call_site(); +pub fn name_to_ty(name: Ident) -> Type { let mut segments = Punctuated::new(); - segments.push(path_segment(name.to_constant_case_ident(span))); + segments.push(path_segment(name)); syn::Type::Path(type_path(segments)) }