diff --git a/src/generate/peripheral.rs b/src/generate/peripheral.rs index 903a69a3..5f3ed5d7 100644 --- a/src/generate/peripheral.rs +++ b/src/generate/peripheral.rs @@ -732,11 +732,15 @@ fn cluster_block( let reg_block = register_or_cluster_block(&c.children, defaults, Some(&mod_name), nightly)?; // Generate definition for each of the registers. - let registers = util::only_registers(&c.children); + let registers_cow = util::registers_with_uniq_names( + util::only_registers(&c.children) + .into_iter() + ); + let registers: Vec<&Register> = registers_cow.iter().map(|cow| &**cow).collect(); for reg in ®isters { mod_items.extend(register::render( reg, - ®isters, + ®isters[..], p, all_peripherals, defaults, diff --git a/src/generate/register.rs b/src/generate/register.rs index 782cf592..8de8f559 100644 --- a/src/generate/register.rs +++ b/src/generate/register.rs @@ -313,7 +313,7 @@ pub fn fields( } let has_reserved_variant = evs.values.len() != (1 << f.width); - let variants = evs.values + let mut variants = evs.values .iter() // filter out all reserved variants, as we should not // generate code for them @@ -330,6 +330,7 @@ pub fn fields( format!("EnumeratedValue {} has no field", ev.name) })?); + Ok(Variant { description: description, sc: sc, @@ -340,6 +341,13 @@ pub fn fields( }) .collect::>>()?; + util::rename_identifiers(&mut variants, + |variant| variant.sc.as_ref().to_owned(), + |variant, n| { + variant.sc = Ident::new(format!("{}_{}", variant.sc, n)); + variant.pc = Ident::new(format!("{}_{}", variant.pc, n)); + }); + let pc_r = &f.pc_r; if let Some(ref base) = base { let pc = base.field.to_sanitized_upper_case(); @@ -649,7 +657,7 @@ pub fn fields( } }); - let variants = evs.values + let mut variants = evs.values .iter() // filter out all reserved variants, as we should not // generate code for them @@ -676,6 +684,13 @@ pub fn fields( ) .collect::>>()?; + util::rename_identifiers(&mut variants, + |variant| variant.sc.as_ref().to_owned(), + |variant, n| { + variant.sc = Ident::new(format!("{}_{}", variant.sc, n)); + variant.pc = Ident::new(format!("{}_{}", variant.pc, n)); + }); + if variants.len() == 1 << f.width { unsafety = None; } diff --git a/src/util.rs b/src/util.rs index ab216538..90a93b7a 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,4 +1,6 @@ use std::borrow::Cow; +use std::hash::Hash; +use std::collections::{HashMap, HashSet}; use inflections::Inflect; use svd::{Access, Cluster, Register}; @@ -37,11 +39,14 @@ impl ToSanitizedSnakeCase for str { } } - let s = self.replace(BLACKLIST_CHARS, ""); + let s = santitize_underscores( + &self.replace(BLACKLIST_CHARS, "") + .to_snake_case() + ); match s.chars().next().unwrap_or('\0') { '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => { - Cow::from(format!("_{}", s.to_snake_case())) + Cow::from(format!("_{}", s)) } _ => { keywords! { @@ -109,26 +114,32 @@ impl ToSanitizedSnakeCase for str { impl ToSanitizedUpperCase for str { fn to_sanitized_upper_case(&self) -> Cow { - let s = self.replace(BLACKLIST_CHARS, ""); + let s = santitize_underscores( + &self.replace(BLACKLIST_CHARS, "") + .to_upper_case() + ); match s.chars().next().unwrap_or('\0') { '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => { - Cow::from(format!("_{}", s.to_upper_case())) + Cow::from(format!("_{}", s)) } - _ => Cow::from(s.to_upper_case()), + _ => Cow::from(s), } } } impl ToSanitizedPascalCase for str { fn to_sanitized_pascal_case(&self) -> Cow { - let s = self.replace(BLACKLIST_CHARS, ""); + let s = santitize_underscores( + &self.replace(BLACKLIST_CHARS, "") + .to_pascal_case() + ); match s.chars().next().unwrap_or('\0') { '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => { - Cow::from(format!("_{}", s.to_pascal_case())) + Cow::from(format!("_{}", s)) } - _ => Cow::from(s.to_pascal_case()), + _ => Cow::from(s), } } } @@ -137,6 +148,13 @@ pub fn respace(s: &str) -> String { s.split_whitespace().collect::>().join(" ") } +fn santitize_underscores(s: &str) -> String { + s.split('_') + .filter(|part| part.len() > 0) + .collect::>() + .join("_") +} + pub fn name_of(register: &Register) -> Cow { match *register { Register::Single(ref info) => Cow::from(&*info.name), @@ -148,6 +166,13 @@ pub fn name_of(register: &Register) -> Cow { } } +pub fn set_name_of(register: &mut Register, name: String) { + match *register { + Register::Single(ref mut info) => info.name = name, + Register::Array(ref mut info, _) => info.name = name, + } +} + pub fn access_of(register: &Register) -> Access { register.access.unwrap_or_else(|| { if let Some(ref fields) = register.fields { @@ -261,3 +286,74 @@ pub fn only_registers(ercs: &[Either]) -> Vec<&Register> { .collect(); registers } + +/// Renames registers if their name occurs multiple times +pub fn registers_with_uniq_names<'a, I: Iterator>(registers: I) -> Vec> { + let (capacity, _) = registers.size_hint(); + let mut seen = HashSet::with_capacity(capacity); + registers.map(|register| { + let mut n = 1; + let mut name = name_of(&*register); + let mut dup = false; + // Count up `n` until register name is not already present + // in `seen` + while seen.contains(&name) { + dup = true; + n += 1; + name = Cow::Owned(format!("{}_{}", name_of(&*register), n)); + } + seen.insert(name.clone()); + + if dup { + let mut register = register.clone(); + set_name_of(&mut register, name.into_owned()); + Cow::Owned(register) + } else { + Cow::Borrowed(register) + } + }).collect() +} + +fn count_occurrences<'a, K, I>(iter: I) -> HashMap +where + K: Eq + Hash, + I: Iterator, +{ + let mut counts = HashMap::new(); + for k in iter { + let count = counts.entry(k) + .or_insert(0); + *count += 1; + } + counts +} + +// Generically rename identifiers that occur multiple times into a +// series where both `sc` and `pc` end in `…_1`, `…_2`, and so on. +pub fn rename_identifiers(entries: &mut Vec, getter: G, setter: S) +where + K: Eq + Hash + Clone, + G: Fn(&E) -> K, + S: Fn(&mut E, usize), +{ + let counts = count_occurrences( + entries.iter() + .map(|entry| getter(entry)) + ); + // Rename identifiers that occur multiple times into a + // series where both `sc` and `pc` end in `…_1`, + // `…_2`, and so on. + let mut indexes = HashMap::::new(); + for entry in entries.iter_mut() { + let key = getter(entry); + match counts.get(&key) { + Some(count) if *count > 1 => { + let index = indexes.entry(key).or_insert(0); + *index += 1; + + setter(entry, *index); + } + _ => {} + } + } +}