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