Skip to content

Commit 50fe8e8

Browse files
committed
Generate (subjectively) better names for unions
If a RegisterBlock is comprised of a single union, then the RegisterBlock is emitted as that union.
1 parent 64bd867 commit 50fe8e8

File tree

1 file changed

+88
-10
lines changed

1 file changed

+88
-10
lines changed

src/generate/peripheral.rs

Lines changed: 88 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,67 @@ struct Region {
119119
end: u32,
120120
}
121121

122+
impl Region {
123+
/// Find the longest common prefix of the identifier names
124+
/// for the fields in this region, if any.
125+
fn common_prefix(&self) -> Option<String> {
126+
let mut char_vecs = Vec::new();
127+
let mut min_len = usize::max_value();
128+
129+
for f in &self.fields {
130+
match f.field.ident {
131+
None => return None,
132+
Some(ref ident) => {
133+
let chars : Vec<char> = ident.as_ref().chars().collect();
134+
min_len = min_len.min(chars.len());
135+
char_vecs.push(chars);
136+
}
137+
}
138+
}
139+
140+
let mut result = String::new();
141+
142+
for i in 0..min_len {
143+
let c = char_vecs[0][i];
144+
for v in &char_vecs {
145+
if v[i] != c {
146+
break;
147+
}
148+
}
149+
150+
// This character position is the same across
151+
// all variants, so emit it into the result
152+
result.push(c);
153+
}
154+
155+
if result.is_empty() {
156+
None
157+
} else {
158+
Some(result)
159+
}
160+
}
161+
162+
/// Return a description of this region
163+
fn description(&self) -> String {
164+
let mut result = String::new();
165+
for f in &self.fields {
166+
// In the Atmel SVDs the union variants all tend to
167+
// have the same description. Rather than emitting
168+
// the same text three times over, only join in the
169+
// text from the other variants if it is different.
170+
// This isn't a foolproof way of emitting the most
171+
// reasonable short description, but it's good enough.
172+
if f.description != result {
173+
if result.len() > 0 {
174+
result.push(' ');
175+
}
176+
result.push_str(&f.description);
177+
}
178+
}
179+
result
180+
}
181+
}
182+
122183
/// FieldRegions keeps track of overlapping field regions,
123184
/// merging fields into appropriate regions as we process them.
124185
/// This allows us to reason about when to create a union
@@ -213,6 +274,8 @@ fn register_or_cluster_block(
213274
regions.add(reg_block_field);
214275
}
215276

277+
let block_is_union = regions.regions.len() == 1 && regions.regions[0].fields.len() > 1;
278+
216279
// The end of the region from the prior iteration of the loop
217280
let mut last_end = None;
218281

@@ -249,24 +312,33 @@ fn register_or_cluster_block(
249312
Ident::new(",").to_tokens(&mut region_fields);
250313
}
251314

252-
if region.fields.len() > 1 {
253-
// TODO: come up with nicer naming. Right now we're using the
254-
// region index as a unique-within-this-block identifier counter.
255-
let name = Ident::new(format!("u{}", i));
315+
if region.fields.len() > 1 && !block_is_union {
316+
let (type_name, name) = match region.common_prefix() {
317+
Some(prefix) => {
318+
(Ident::new(format!("{}_union", prefix)),
319+
Ident::new(prefix))
320+
}
321+
// If we can't find a common prefix for the name, fall back to the
322+
// region index as a unique-within-this-block identifier counter.
323+
None => {
324+
let ident = Ident::new(format!("u{}", i));
325+
(ident.clone(), ident)
326+
}
327+
};
256328

257-
// TODO: if we only have a single region and that region is a
258-
// union, the overall RegisterBlock could be a union
329+
let description = region.description();
259330

260331
helper_types.append(quote! {
261-
/// Union container for a set of overlapping registers
332+
#[doc = #description]
262333
#[repr(C)]
263-
pub union #name {
334+
pub union #type_name {
264335
#region_fields
265336
}
266337
});
267338

268339
fields.append(quote! {
269-
#name: #name
340+
#[doc = #description]
341+
pub #name: #type_name
270342
});
271343
Ident::new(",").to_tokens(&mut fields);
272344

@@ -280,10 +352,16 @@ fn register_or_cluster_block(
280352
None => "RegisterBlock".into(),
281353
});
282354

355+
let block_type = if block_is_union {
356+
Ident::new("union")
357+
} else {
358+
Ident::new("struct")
359+
};
360+
283361
Ok(quote! {
284362
/// Register block
285363
#[repr(C)]
286-
pub struct #name {
364+
pub #block_type #name {
287365
#fields
288366
}
289367

0 commit comments

Comments
 (0)