1extern crate proc_macro;
4
5use core::str::FromStr;
6use proc_macro::TokenStream;
7use proc_macro2::Span;
8use quote::quote;
9use syn::{
10 parse::{Parse, ParseStream, Result},
11 parse_macro_input, Attribute, Error, Ident, LitInt, Token, TypePath,
12};
13
14enum PhantomEntry {
17 Enum {
18 attributes: Vec<Attribute>,
19 name: String,
20 start: u64,
21 end: u64,
22 enum_type: Ident,
23 variant_list: Vec<String>,
24 },
25 Integer {
26 attributes: Vec<Attribute>,
27 name: String,
28 start: u64,
29 end: u64,
30 },
31 Bool {
32 attributes: Vec<Attribute>,
33 name: String,
34 bit: u64,
35 },
36 Const {
37 attributes: Vec<Attribute>,
38 name: String,
39 const_ident: Ident,
40 },
41}
42
43struct PhantomFields {
44 self_member_type: TypePath,
45 entries: Vec<PhantomEntry>,
46}
47
48impl Parse for PhantomFields {
49 fn parse(input: ParseStream) -> Result<Self> {
50 let _ = input.parse::<Token![self]>()?;
51 let _ = input.parse::<Token![.]>()?;
52 let lit = input.parse::<LitInt>()?;
53 if lit.value() != 0 {
54 return Err(Error::new(lit.span(), "Currently only self.0 is supported"));
55 }
56 let _ = input.parse::<Token![:]>()?;
57 let self_member_type: TypePath = input.parse::<TypePath>()?;
58 let _ = input.parse::<Token![,]>()?;
59 let mut entries: Vec<PhantomEntry> = vec![];
61 'entry_loop: loop {
62 if input.is_empty() {
63 break;
64 }
65 let attributes = input.call(Attribute::parse_outer)?;
66 let name = input.parse::<Ident>()?.to_string();
67 let _ = input.parse::<Token![:]>()?;
68 let lookahead_int_or_ident = input.lookahead1();
69 if lookahead_int_or_ident.peek(LitInt) {
70 let start = input.parse::<LitInt>()?.value();
71 let lookahead_bool_or_span = input.lookahead1();
72 if lookahead_bool_or_span.peek(Token![,]) {
73 entries.push(PhantomEntry::Bool {
75 attributes,
76 name,
77 bit: start,
78 });
79 let _ = input.parse::<Token![,]>()?;
80 continue 'entry_loop;
81 } else if lookahead_bool_or_span.peek(Token![-]) {
82 let _ = input.parse::<Token![-]>()?;
84 let end = input.parse::<LitInt>()?.value();
85 let lookahead = input.lookahead1();
86 if lookahead.peek(Token![=]) {
87 let _ = input.parse::<Token![=]>()?;
89 let enum_type = input.parse::<Ident>()?;
90 let mut variant_list = vec![];
91 let _ = input.parse::<Token![<]>()?;
92 'variant_gather_loop: loop {
93 variant_list.push(input.parse::<Ident>()?.to_string());
94 let lookahead = input.lookahead1();
95 if lookahead.peek(Token![>]) {
96 let _ = input.parse::<Token![>]>()?;
98 break 'variant_gather_loop;
99 } else if lookahead.peek(Token![,]) {
100 let _ = input.parse::<Token![,]>()?;
102 continue 'variant_gather_loop;
103 } else {
104 return Err(lookahead.error());
105 }
106 }
107 entries.push(PhantomEntry::Enum {
108 attributes,
109 name,
110 start,
111 end,
112 enum_type,
113 variant_list,
114 });
115 let _ = input.parse::<Token![,]>()?;
116 continue 'entry_loop;
117 } else if lookahead.peek(Token![,]) {
118 entries.push(PhantomEntry::Integer {
120 attributes,
121 name,
122 start,
123 end,
124 });
125 let _ = input.parse::<Token![,]>()?;
126 continue 'entry_loop;
127 } else {
128 return Err(lookahead.error());
129 }
130 } else {
131 return Err(lookahead_bool_or_span.error());
132 }
133 } else if lookahead_int_or_ident.peek(Ident) {
134 let const_ident = input.parse::<Ident>()?;
135 entries.push(PhantomEntry::Const {
137 attributes,
138 name,
139 const_ident,
140 });
141 let _ = input.parse::<Token![,]>()?;
142 continue 'entry_loop;
143 } else {
144 return Err(lookahead_int_or_ident.error());
145 }
146 }
147 Ok(PhantomFields { self_member_type, entries })
148 }
149}
150
151#[proc_macro]
226pub fn phantom_fields(input: TokenStream) -> TokenStream {
227 let PhantomFields { self_member_type, entries } = parse_macro_input!(input as PhantomFields);
228
229 let mut out_text = String::new();
230
231 for entry in entries.into_iter() {
232 match entry {
233 PhantomEntry::Enum {
234 attributes,
235 name,
236 start,
237 end,
238 enum_type,
239 variant_list,
240 } => {
241 for attribute in attributes.into_iter() {
242 out_text.push_str(&format!("{}\n", TokenStream::from(quote! { #attribute })));
243 }
244 let mask_name = Ident::new(&format!("{}_MASK", name.to_uppercase()), Span::call_site());
245 let read_name = Ident::new(&name.clone(), Span::call_site());
246 let with_name = Ident::new(&format!("with_{}", name), Span::call_site());
247 let width = (end - start) + 1;
248 out_text.push_str(&format!(
249 "{}\n",
250 TokenStream::from(quote! {
251 #[allow(clippy::identity_op)]
252 pub const #mask_name: #self_member_type = ((1<<(#width))-1) << #start;
253
254 #[allow(missing_docs)]
255 pub fn #read_name(self) -> #enum_type
256 })
257 ));
258 out_text.push('{');
259 out_text.push_str(&format!(
260 "{}\n",
261 TokenStream::from(quote! {
262 match (self.0 & Self::#mask_name) >> #start
263 })
264 ));
265 out_text.push('{');
266 let enum_type_string = enum_type.to_string();
267 for (i, variant) in variant_list.iter().enumerate() {
268 out_text.push_str(&format!("{} => {}::{},\n", i, enum_type_string, variant));
269 }
270 out_text.push_str("_ => unreachable!(),");
271 out_text.push_str("} }\n");
272 out_text.push_str(&format!(
273 "{}\n",
274 TokenStream::from(quote! {
275 #[allow(missing_docs)]
276 pub const fn #with_name(self, #read_name: #enum_type) -> Self {
277 Self((self.0 & !Self::#mask_name) | (((#read_name as #self_member_type) << #start) & Self::#mask_name))
278 }
279 })
280 ));
281 }
282 PhantomEntry::Integer {
283 attributes,
284 name,
285 start,
286 end,
287 } => {
288 for attribute in attributes.into_iter() {
289 out_text.push_str(&format!("{}\n", TokenStream::from(quote! { #attribute })));
290 }
291 let mask_name = Ident::new(&format!("{}_MASK", name.to_uppercase()), Span::call_site());
292 let read_name = Ident::new(&name.clone(), Span::call_site());
293 let with_name = Ident::new(&format!("with_{}", name), Span::call_site());
294 let width = (end - start) + 1;
295 out_text.push_str(&format!(
296 "{}\n",
297 TokenStream::from(quote! {
298 #[allow(clippy::identity_op)]
299 pub const #mask_name: #self_member_type = ((1<<(#width))-1) << #start;
300
301 #[allow(missing_docs)]
302 pub const fn #read_name(self) -> #self_member_type {
303 (self.0 & Self::#mask_name) >> #start
304 }
305
306 #[allow(missing_docs)]
307 pub const fn #with_name(self, #read_name: #self_member_type) -> Self {
308 Self((self.0 & !Self::#mask_name) | ((#read_name << #start) & Self::#mask_name))
309 }
310 })
311 ));
312 }
313 PhantomEntry::Bool { attributes, name, bit } => {
314 for attribute in attributes.into_iter() {
315 out_text.push_str(&format!("{}\n", TokenStream::from(quote! { #attribute })));
316 }
317 let const_name = Ident::new(&format!("{}_BIT", name.to_uppercase()), Span::call_site());
318 let read_name = Ident::new(&name.clone(), Span::call_site());
319 let with_name = Ident::new(&format!("with_{}", name), Span::call_site());
320 out_text.push_str(&format!(
321 "{}\n",
322 TokenStream::from(quote! {
323 #[allow(clippy::identity_op)]
324 pub const #const_name: #self_member_type = 1 << #bit;
325
326 #[allow(missing_docs)]
327 pub const fn #read_name(self) -> bool {
328 (self.0 & Self::#const_name) != 0
329 }
330
331 #[allow(missing_docs)]
333 pub const fn #with_name(self, bit: bool) -> Self {
334 Self(self.0 ^ (((#self_member_type::wrapping_sub(0, bit as #self_member_type) ^ self.0) & Self::#const_name)))
335 }
336 })
337 ));
338 }
339 PhantomEntry::Const {
340 attributes,
341 name,
342 const_ident,
343 } => {
344 for attribute in attributes.into_iter() {
345 out_text.push_str(&format!("{}\n", TokenStream::from(quote! { #attribute })));
346 }
347 let read_name = Ident::new(&name.clone(), Span::call_site());
348 let with_name = Ident::new(&format!("with_{}", name), Span::call_site());
349 out_text.push_str(&format!(
350 "{}\n",
351 TokenStream::from(quote! {
352 #[allow(missing_docs)]
353 pub const fn #read_name(self) -> bool {
354 (self.0 & #const_ident) != 0
355 }
356
357 #[allow(missing_docs)]
359 pub const fn #with_name(self, bit: bool) -> Self {
360 Self(self.0 ^ (((#self_member_type::wrapping_sub(0, bit as #self_member_type) ^ self.0) & #const_ident as #self_member_type)))
361 }
362 })
363 ));
364 }
365 };
366 }
367
368 TokenStream::from_str(&out_text).map_err(|e| panic!("{:?}", e)).unwrap()
369}