Skip to main content

bitfi_macros/
lib.rs

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