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