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}