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 15 fn paren(ts: TokenStream) -> TokenTree { 16 TT::Group(Group::new(Delimiter::Parenthesis, ts)) 17 } 18 19 fn ident(s: &'static str) -> TokenTree { 20 TT::Ident(Ident::new(s, Span::call_site())) 21 } 22 23 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 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 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)* 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)* 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)* 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)* 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 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