Skip to content

Commit c921fcc

Browse files
committed
rust: macros: add seq! macro
Add a macro to repeat code. Signed-off-by: Fabien Parent <fabien.parent@linaro.org>
1 parent f06e6de commit c921fcc

2 files changed

Lines changed: 154 additions & 0 deletions

File tree

rust/macros/lib.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ mod module;
1010
mod paste;
1111
mod pin_data;
1212
mod pinned_drop;
13+
mod seq;
1314
mod vtable;
1415
mod zeroable;
1516

@@ -387,6 +388,25 @@ pub fn paste(input: TokenStream) -> TokenStream {
387388
tokens.into_iter().collect()
388389
}
389390

391+
/// Repeat a fragment of code and provide a numerical index for the current repetition
392+
///
393+
/// # Examples
394+
///
395+
/// ```rust,ignore
396+
/// seq!(i in 0..10) {
397+
/// func$i() {
398+
/// }
399+
/// }
400+
///
401+
/// seq!(i in 8..=15) {
402+
/// bit$i() {
403+
/// }
404+
/// }
405+
#[proc_macro]
406+
pub fn seq(input: TokenStream) -> TokenStream {
407+
seq::expand(input)
408+
}
409+
390410
/// Derives the [`Zeroable`] trait for the given struct.
391411
///
392412
/// This can only be used for structs where every field implements the [`Zeroable`] trait.

rust/macros/seq.rs

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
use proc_macro::{
4+
Delimiter, TokenStream,
5+
TokenTree::{self, Group, Ident, Literal},
6+
};
7+
use std::ops::Range;
8+
9+
fn process_group(var: &str, i: isize, tokens: impl Iterator<Item = TokenTree>) -> Vec<TokenTree> {
10+
let mut tt = Vec::<TokenTree>::new();
11+
12+
let mut tokens = tokens.peekable();
13+
14+
while let Some(token) = tokens.next() {
15+
match token {
16+
Group(ref group) => {
17+
let group_tokens = process_group(var, i, group.stream().into_iter());
18+
let stream = FromIterator::from_iter(group_tokens.into_iter());
19+
let new_group = proc_macro::Group::new(group.delimiter(), stream);
20+
tt.push(TokenTree::Group(new_group));
21+
}
22+
TokenTree::Punct(ref punct) => {
23+
if punct.to_string() == "$" {
24+
if let Some(TokenTree::Ident(ident)) = tokens.peek() {
25+
if ident.to_string() == var {
26+
tt.push(TokenTree::Literal(proc_macro::Literal::isize_unsuffixed(i)));
27+
tokens.next();
28+
continue;
29+
}
30+
}
31+
}
32+
33+
tt.push(token);
34+
}
35+
_ => tt.push(token),
36+
}
37+
}
38+
39+
tt
40+
}
41+
42+
pub(crate) fn expand(input: TokenStream) -> TokenStream {
43+
let mut tokens = input.into_iter().peekable();
44+
45+
let var = if let Some(Ident(i)) = tokens.next() {
46+
i.to_string()
47+
} else {
48+
panic!("seq! first token should be an identifier");
49+
};
50+
51+
let token = tokens.next().expect("missing token, expecting '.'");
52+
assert!(matches!(token, TokenTree::Ident(x) if x.to_string() == "in"));
53+
54+
let token = tokens
55+
.next()
56+
.expect("seq!: missing token, expecting integer");
57+
let token = if let Group(group) = token {
58+
group
59+
.stream()
60+
.into_iter()
61+
.next()
62+
.expect("seq: missing token, expecting integer")
63+
} else {
64+
token
65+
};
66+
67+
let start = if let Literal(lit) = token {
68+
lit.to_string()
69+
.parse::<isize>()
70+
.expect("Failed to convert literal to isize")
71+
} else {
72+
panic!("seq!: unexpected token '{token}'");
73+
};
74+
75+
let token = tokens.next().expect("seq!: missing token, expecting '.'");
76+
assert!(matches!(token, TokenTree::Punct(x) if x == '.'));
77+
let token = tokens.next().expect("seq!: missing token, expecting '.'");
78+
assert!(matches!(token, TokenTree::Punct(x) if x == '.'));
79+
80+
let is_inclusive_range = if let Some(TokenTree::Punct(p)) = tokens.peek() {
81+
if p.as_char() == '=' {
82+
tokens.next();
83+
true
84+
} else {
85+
false
86+
}
87+
} else {
88+
false
89+
};
90+
91+
let token = tokens
92+
.next()
93+
.expect("seq!: missing token, expecting integer");
94+
let token = if let Group(group) = token {
95+
group
96+
.stream()
97+
.into_iter()
98+
.next()
99+
.expect("seq: missing token, expecting integer")
100+
} else {
101+
token
102+
};
103+
104+
let end = if let Literal(lit) = token {
105+
lit.to_string()
106+
.parse::<isize>()
107+
.expect("Failed to convert literal to isize")
108+
} else {
109+
panic!("seq!: unexpected token '{token}'");
110+
};
111+
let range = Range {
112+
start,
113+
end: end + if is_inclusive_range { 1 } else { 0 },
114+
};
115+
116+
let tokens = if let Some(Group(group)) = tokens.next() {
117+
if group.delimiter() != Delimiter::Brace {
118+
panic!("seq! expected brace");
119+
}
120+
121+
group.stream().into_iter()
122+
} else {
123+
panic!("seq! missing opening brace");
124+
};
125+
126+
let tokens: Vec<TokenTree> = tokens.collect();
127+
let mut tt = Vec::<TokenTree>::new();
128+
129+
for i in range {
130+
tt.extend_from_slice(&process_group(&var, i, tokens.clone().into_iter()));
131+
}
132+
133+
FromIterator::from_iter(tt)
134+
}

0 commit comments

Comments
 (0)