bitfi_macros/
lib.rs

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}