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