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 16*648fe157SPaolo Bonzini fn paren(ts: TokenStream) -> TokenTree { 17*648fe157SPaolo Bonzini TT::Group(Group::new(Delimiter::Parenthesis, ts)) 18*648fe157SPaolo Bonzini } 19*648fe157SPaolo Bonzini 20*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 24*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 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 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)* 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)* 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)* 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)* 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 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