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