xref: /qemu/rust/qemu-api-macros/src/bits.rs (revision 4b66abead9c9c7b637fb87e6bf782a45eee2a621)
1 // SPDX-License-Identifier: MIT or Apache-2.0 or GPL-2.0-or-later
2 
3 // shadowing is useful together with "if let"
4 #![allow(clippy::shadow_unrelated)]
5 
6 use proc_macro2::{
7     Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree, TokenTree as TT,
8 };
9 
10 use crate::utils::MacroError;
11 
12 pub struct BitsConstInternal {
13     typ: TokenTree,
14 }
15 
16 fn paren(ts: TokenStream) -> TokenTree {
17     TT::Group(Group::new(Delimiter::Parenthesis, ts))
18 }
19 
20 fn ident(s: &'static str) -> TokenTree {
21     TT::Ident(Ident::new(s, Span::call_site()))
22 }
23 
24 fn punct(ch: char) -> TokenTree {
25     TT::Punct(Punct::new(ch, Spacing::Alone))
26 }
27 
28 /// Implements a recursive-descent parser that translates Boolean expressions on
29 /// bitmasks to invocations of `const` functions defined by the `bits!` macro.
30 impl BitsConstInternal {
31     // primary ::= '(' or ')'
32     //           | ident
33     //           | '!' ident
34     fn parse_primary(
35         &self,
36         tok: TokenTree,
37         it: &mut dyn Iterator<Item = TokenTree>,
38         out: &mut TokenStream,
39     ) -> Result<Option<TokenTree>, MacroError> {
40         let next = match tok {
41             TT::Group(ref g) => {
42                 if g.delimiter() != Delimiter::Parenthesis && g.delimiter() != Delimiter::None {
43                     return Err(MacroError::Message("expected parenthesis".into(), g.span()));
44                 }
45                 let mut stream = g.stream().into_iter();
46                 let Some(first_tok) = stream.next() else {
47                     return Err(MacroError::Message(
48                         "expected operand, found ')'".into(),
49                         g.span(),
50                     ));
51                 };
52                 let mut output = TokenStream::new();
53                 // start from the lowest precedence
54                 let next = self.parse_or(first_tok, &mut stream, &mut output)?;
55                 if let Some(tok) = next {
56                     return Err(MacroError::Message(
57                         format!("unexpected token {tok}"),
58                         tok.span(),
59                     ));
60                 }
61                 out.extend(Some(paren(output)));
62                 it.next()
63             }
64             TT::Ident(_) => {
65                 let mut output = TokenStream::new();
66                 output.extend([
67                     self.typ.clone(),
68                     TT::Punct(Punct::new(':', Spacing::Joint)),
69                     TT::Punct(Punct::new(':', Spacing::Joint)),
70                     tok,
71                 ]);
72                 out.extend(Some(paren(output)));
73                 it.next()
74             }
75             TT::Punct(ref p) => {
76                 if p.as_char() != '!' {
77                     return Err(MacroError::Message("expected operand".into(), p.span()));
78                 }
79                 let Some(rhs_tok) = it.next() else {
80                     return Err(MacroError::Message(
81                         "expected operand at end of input".into(),
82                         p.span(),
83                     ));
84                 };
85                 let next = self.parse_primary(rhs_tok, it, out)?;
86                 out.extend([punct('.'), ident("invert"), paren(TokenStream::new())]);
87                 next
88             }
89             _ => {
90                 return Err(MacroError::Message("unexpected literal".into(), tok.span()));
91             }
92         };
93         Ok(next)
94     }
95 
96     fn parse_binop<
97         F: Fn(
98             &Self,
99             TokenTree,
100             &mut dyn Iterator<Item = TokenTree>,
101             &mut TokenStream,
102         ) -> Result<Option<TokenTree>, MacroError>,
103     >(
104         &self,
105         tok: TokenTree,
106         it: &mut dyn Iterator<Item = TokenTree>,
107         out: &mut TokenStream,
108         ch: char,
109         f: F,
110         method: &'static str,
111     ) -> Result<Option<TokenTree>, MacroError> {
112         let mut next = f(self, tok, it, out)?;
113         while next.is_some() {
114             let op = next.as_ref().unwrap();
115             let TT::Punct(ref p) = op else { break };
116             if p.as_char() != ch {
117                 break;
118             }
119 
120             let Some(rhs_tok) = it.next() else {
121                 return Err(MacroError::Message(
122                     "expected operand at end of input".into(),
123                     p.span(),
124                 ));
125             };
126             let mut rhs = TokenStream::new();
127             next = f(self, rhs_tok, it, &mut rhs)?;
128             out.extend([punct('.'), ident(method), paren(rhs)]);
129         }
130         Ok(next)
131     }
132 
133     // sub ::= primary ('-' primary)*
134     pub fn parse_sub(
135         &self,
136         tok: TokenTree,
137         it: &mut dyn Iterator<Item = TokenTree>,
138         out: &mut TokenStream,
139     ) -> Result<Option<TokenTree>, MacroError> {
140         self.parse_binop(tok, it, out, '-', Self::parse_primary, "difference")
141     }
142 
143     // and ::= sub ('&' sub)*
144     fn parse_and(
145         &self,
146         tok: TokenTree,
147         it: &mut dyn Iterator<Item = TokenTree>,
148         out: &mut TokenStream,
149     ) -> Result<Option<TokenTree>, MacroError> {
150         self.parse_binop(tok, it, out, '&', Self::parse_sub, "intersection")
151     }
152 
153     // xor ::= and ('&' and)*
154     fn parse_xor(
155         &self,
156         tok: TokenTree,
157         it: &mut dyn Iterator<Item = TokenTree>,
158         out: &mut TokenStream,
159     ) -> Result<Option<TokenTree>, MacroError> {
160         self.parse_binop(tok, it, out, '^', Self::parse_and, "symmetric_difference")
161     }
162 
163     // or ::= xor ('|' xor)*
164     pub fn parse_or(
165         &self,
166         tok: TokenTree,
167         it: &mut dyn Iterator<Item = TokenTree>,
168         out: &mut TokenStream,
169     ) -> Result<Option<TokenTree>, MacroError> {
170         self.parse_binop(tok, it, out, '|', Self::parse_xor, "union")
171     }
172 
173     pub fn parse(
174         it: &mut dyn Iterator<Item = TokenTree>,
175     ) -> Result<proc_macro2::TokenStream, MacroError> {
176         let mut pos = Span::call_site();
177         let mut typ = proc_macro2::TokenStream::new();
178 
179         // Gobble everything up to an `@` sign, which is followed by a
180         // parenthesized expression; that is, all token trees except the
181         // last two form the type.
182         let next = loop {
183             let tok = it.next();
184             if let Some(ref t) = tok {
185                 pos = t.span();
186             }
187             match tok {
188                 None => break None,
189                 Some(TT::Punct(ref p)) if p.as_char() == '@' => {
190                     let tok = it.next();
191                     if let Some(ref t) = tok {
192                         pos = t.span();
193                     }
194                     break tok;
195                 }
196                 Some(x) => typ.extend(Some(x)),
197             }
198         };
199 
200         let Some(tok) = next else {
201             return Err(MacroError::Message(
202                 "expected expression, do not call this macro directly".into(),
203                 pos,
204             ));
205         };
206         let TT::Group(ref _group) = tok else {
207             return Err(MacroError::Message(
208                 "expected parenthesis, do not call this macro directly".into(),
209                 tok.span(),
210             ));
211         };
212         let mut out = TokenStream::new();
213         let state = Self {
214             typ: TT::Group(Group::new(Delimiter::None, typ)),
215         };
216 
217         let next = state.parse_primary(tok, it, &mut out)?;
218 
219         // A parenthesized expression is a single production of the grammar,
220         // so the input must have reached the last token.
221         if let Some(tok) = next {
222             return Err(MacroError::Message(
223                 format!("unexpected token {tok}"),
224                 tok.span(),
225             ));
226         }
227         Ok(out)
228     }
229 }
230