1use core::convert::Into;
2use core::iter::Iterator;
3
4use proc_macro2::{Delimiter, Ident, Literal};
5use proc_macro2::{Span, TokenTree};
6use quote::quote;
7
8macro_rules! err {
9 ($msg:expr) => {
10 return format!("compile_error!(\"{}\");", $msg).parse().unwrap()
11 };
12}
13
14macro_rules! unwrap {
15 ($args:expr, $t:ident) => {{
16 let Some(nx) = $args.next() else { err!(format!("Unexpected end of token stream, wanted {}", stringify!($t))) };
17 let TokenTree:: $t (val) = nx else { err!(format!("Expected '{}' found '{nx}'", stringify!($t))) };
18 val
19 }};
20}
21
22macro_rules! try_item {
23 ($args:expr, $t:ident) => {{
24 if let Some(nx) = $args.peek() {
25 if let TokenTree:: $t (_) = nx {
26 Some(unwrap!($args, $t))
27 }
28 else { None }
29 } else { None }
30 }};
31}
32
33macro_rules! try_punct {
34 ($args:expr, $p:literal) => {{
35 if let Some(TokenTree::Punct(p)) = $args.peek() {
36 if p.as_char() == $p {
37 $args.next();
38 true
39 } else { false }
40 } else {
41 false
42 }
43 }};
44}
45
46macro_rules! expect_punct {
47 ($args:expr, $p:literal) => {{
48 let c = unwrap!($args, Punct).as_char();
49 if c != $p {
50 err!(format!("Expected punctuation to be '{}' but found '{c}'", $p));
51 }
52 }};
53}
54
55macro_rules! expect_delimited {
56 ($args:expr, $d:ident) => {{
57 let it = unwrap!($args, Group);
58 if !matches!(it.delimiter(), Delimiter :: $d) {
59 err!(format!("Expected delimiter {}", stringify!($d)));
60 }
61 it.stream()
62 }};
63}
64
65macro_rules! try_delimited {
66 ($args:expr, $d:ident) => {{
67 let it = try_item!($args, Group);
68
69 if it.as_ref().is_some_and(|i| matches!(i.delimiter(), Delimiter :: $d)) {
70 Some(it.unwrap().stream())
71 } else {
72 None
73 }
74 }};
75}
76
77macro_rules! expect_ident {
78 ($args:expr, $p:literal) => {{
79 let c = unwrap!($args, Ident).to_string();
80 if c != $p {
81 err!(format!("Expected Ident to be '{}' but found '{c}'", $p));
82 }
83 }};
84}
85
86fn make_field_multi_byte(ty: &Ident, name: Ident, start: Literal, end: Literal, mutable: bool) -> proc_macro2::TokenStream {
87 let get_ident = proc_macro2::Ident::new(&format!("get_{name}"), Span::call_site());
88 let mut r = quote! {
89 #[inline]
90 pub fn #get_ident (&self) -> #ty {
91 bitfields::BitField::get_bit_range(self, #start..=#end)
92 }
93 };
94
95 if mutable {
96 let set_ident = proc_macro2::Ident::new(&format!("set_{name}"), Span::call_site());
97 r = quote! {
98 #r
99
100 #[inline]
101 pub fn #set_ident (&mut self, val: #ty) {
102 bitfields::BitField::set_bit_range(self, #start..=#end, val)
103 }
104 }
105 }
106
107 r
108}
109
110fn make_field_single_byte(name: Ident, index: Literal, mutable: bool) -> proc_macro2::TokenStream {
111 let get_ident = proc_macro2::Ident::new(&format!("get_{name}"), Span::call_site());
112 let mut r = quote! {
113 #[inline]
114 pub fn #get_ident (&self) -> bool {
115 bitfields::BitField::get_bit(self, #index)
116 }
117 };
118
119 if mutable {
120 let set_ident = proc_macro2::Ident::new(&format!("set_{name}"), Span::call_site());
121 let clear_ident = proc_macro2::Ident::new(&format!("clear_{name}"), Span::call_site());
122 r = quote! {
123 #r
124
125 #[inline]
126 pub fn #set_ident (&mut self) {
127 bitfields::BitField::set_bit(self, #index)
128 }
129
130 #[inline]
131 pub fn #clear_ident (&mut self) {
132 bitfields::BitField::clear_bit(self, #index)
133 }
134 }
135 }
136
137 r
138}
139
140fn parse_bitfield(ts: &mut impl Iterator<Item = TokenTree>) -> proc_macro2::TokenStream {
141 let name = unwrap!(ts, Ident);
142
143 expect_punct!(ts, '=');
144
145 let ty = unwrap!(ts, Ident);
146
147 let ts = expect_delimited!(ts, Brace);
148 let mut ts = ts.into_iter().peekable();
149
150 let mut fields = vec![];
151
152 while ts.peek().is_some() {
153 let name = unwrap!(ts, Ident);
154
155 expect_punct!(ts, ':');
156
157 let start = unwrap!(ts, Literal);
158
159 #[allow(clippy::collapsible_match)]
160 let end = if try_punct!(ts, '-') {
161 Some(unwrap!(ts, Literal))
162 } else {
163 None
164 };
165
166 let mut mutable = true;
167
168 #[allow(clippy::collapsible_match)]
169 if let Some(inner) = try_delimited!(ts, Bracket) {
170 let mut inner = inner.into_iter().peekable();
171
172 expect_ident!(inner, "mut");
173 expect_punct!(inner, '=');
174
175 mutable = match unwrap!(inner, Ident).to_string().as_str() {
176 "true" => true,
177 "false" => false,
178 _ => panic!()
179 };
180 }
181
182 expect_punct!(ts, ';');
183 fields.push((name, start, end, mutable));
184 }
185
186 let fields = fields.into_iter().map(|(name, start, end, mutable)| {
187 match end {
188 Some(end) => make_field_multi_byte(&ty, name, start, end, mutable),
189 None => make_field_single_byte(name, start, mutable),
190 }
191 });
192
193 quote! {
194 #[repr(transparent)]
195 pub struct #name(#ty);
196
197 impl bitfields::BitField<#ty> for #name {
198 #[inline(always)]
199 fn set_bit(&mut self, i: #ty) {
200 self.0.set_bit(i);
201 }
202
203 #[inline(always)]
204 fn clear_bit(&mut self, i: #ty) {
205 self.0.clear_bit(i);
206 }
207
208 #[inline(always)]
209 fn get_bit(&self, i: #ty) -> bool {
210 self.0.get_bit(i)
211 }
212
213 #[inline(always)]
214 fn toggle_bit(&mut self, i: #ty) {
215 self.0.toggle_bit(i);
216 }
217
218 #[inline(always)]
219 fn set_bit_range(&mut self, range: ::core::ops::RangeInclusive<#ty>, b: #ty) {
220 self.0.set_bit_range(range, b);
221 }
222
223 #[inline(always)]
224 fn get_bit_range(&self, range: ::core::ops::RangeInclusive<#ty>) -> #ty {
225 self.0.get_bit_range(range)
226 }
227 }
228
229 impl ::core::default::Default for #name {
230 fn default() -> Self { Self(0) }
231 }
232
233 impl #name {
234
235 #[inline(always)]
236 pub const fn new(n: #ty) -> Self {
237 Self(n)
238 }
239
240 #[inline(always)]
241 pub const fn get_inner(&self) -> #ty { self.0 }
242
243 #[inline(always)]
244 pub const fn set_inner(&mut self, val: #ty) {
245 self.0 = val;
246 }
247
248 #(#fields)*
249 }
250 }
251}
252
253#[proc_macro]
254pub fn bitfield(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
255 let ts: proc_macro2::TokenStream = ts.into();
256 let mut ts = ts.into_iter().peekable();
257
258 let mut bitfields = vec![];
259
260 while ts.peek().is_some() {
261 let bf = parse_bitfield(&mut ts);
262 bitfields.push(bf);
263 }
264
265 quote! {
266 #(#bitfields)*
267 }.into()
268}