// SPDX-License-Identifier: MIT or Apache-2.0 or GPL-2.0-or-later // shadowing is useful together with "if let" #![allow(clippy::shadow_unrelated)] use proc_macro2::{ Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree, TokenTree as TT, }; use crate::utils::MacroError; pub struct BitsConstInternal { typ: TokenTree, } fn paren(ts: TokenStream) -> TokenTree { TT::Group(Group::new(Delimiter::Parenthesis, ts)) } fn ident(s: &'static str) -> TokenTree { TT::Ident(Ident::new(s, Span::call_site())) } fn punct(ch: char) -> TokenTree { TT::Punct(Punct::new(ch, Spacing::Alone)) } /// Implements a recursive-descent parser that translates Boolean expressions on /// bitmasks to invocations of `const` functions defined by the `bits!` macro. impl BitsConstInternal { // primary ::= '(' or ')' // | ident // | '!' ident fn parse_primary( &self, tok: TokenTree, it: &mut dyn Iterator, out: &mut TokenStream, ) -> Result, MacroError> { let next = match tok { TT::Group(ref g) => { if g.delimiter() != Delimiter::Parenthesis && g.delimiter() != Delimiter::None { return Err(MacroError::Message("expected parenthesis".into(), g.span())); } let mut stream = g.stream().into_iter(); let Some(first_tok) = stream.next() else { return Err(MacroError::Message( "expected operand, found ')'".into(), g.span(), )); }; let mut output = TokenStream::new(); // start from the lowest precedence let next = self.parse_or(first_tok, &mut stream, &mut output)?; if let Some(tok) = next { return Err(MacroError::Message( format!("unexpected token {tok}"), tok.span(), )); } out.extend(Some(paren(output))); it.next() } TT::Ident(_) => { let mut output = TokenStream::new(); output.extend([ self.typ.clone(), TT::Punct(Punct::new(':', Spacing::Joint)), TT::Punct(Punct::new(':', Spacing::Joint)), tok, ]); out.extend(Some(paren(output))); it.next() } TT::Punct(ref p) => { if p.as_char() != '!' { return Err(MacroError::Message("expected operand".into(), p.span())); } let Some(rhs_tok) = it.next() else { return Err(MacroError::Message( "expected operand at end of input".into(), p.span(), )); }; let next = self.parse_primary(rhs_tok, it, out)?; out.extend([punct('.'), ident("invert"), paren(TokenStream::new())]); next } _ => { return Err(MacroError::Message("unexpected literal".into(), tok.span())); } }; Ok(next) } fn parse_binop< F: Fn( &Self, TokenTree, &mut dyn Iterator, &mut TokenStream, ) -> Result, MacroError>, >( &self, tok: TokenTree, it: &mut dyn Iterator, out: &mut TokenStream, ch: char, f: F, method: &'static str, ) -> Result, MacroError> { let mut next = f(self, tok, it, out)?; while next.is_some() { let op = next.as_ref().unwrap(); let TT::Punct(ref p) = op else { break }; if p.as_char() != ch { break; } let Some(rhs_tok) = it.next() else { return Err(MacroError::Message( "expected operand at end of input".into(), p.span(), )); }; let mut rhs = TokenStream::new(); next = f(self, rhs_tok, it, &mut rhs)?; out.extend([punct('.'), ident(method), paren(rhs)]); } Ok(next) } // sub ::= primary ('-' primary)* pub fn parse_sub( &self, tok: TokenTree, it: &mut dyn Iterator, out: &mut TokenStream, ) -> Result, MacroError> { self.parse_binop(tok, it, out, '-', Self::parse_primary, "difference") } // and ::= sub ('&' sub)* fn parse_and( &self, tok: TokenTree, it: &mut dyn Iterator, out: &mut TokenStream, ) -> Result, MacroError> { self.parse_binop(tok, it, out, '&', Self::parse_sub, "intersection") } // xor ::= and ('&' and)* fn parse_xor( &self, tok: TokenTree, it: &mut dyn Iterator, out: &mut TokenStream, ) -> Result, MacroError> { self.parse_binop(tok, it, out, '^', Self::parse_and, "symmetric_difference") } // or ::= xor ('|' xor)* pub fn parse_or( &self, tok: TokenTree, it: &mut dyn Iterator, out: &mut TokenStream, ) -> Result, MacroError> { self.parse_binop(tok, it, out, '|', Self::parse_xor, "union") } pub fn parse( it: &mut dyn Iterator, ) -> Result { let mut pos = Span::call_site(); let mut typ = proc_macro2::TokenStream::new(); // Gobble everything up to an `@` sign, which is followed by a // parenthesized expression; that is, all token trees except the // last two form the type. let next = loop { let tok = it.next(); if let Some(ref t) = tok { pos = t.span(); } match tok { None => break None, Some(TT::Punct(ref p)) if p.as_char() == '@' => { let tok = it.next(); if let Some(ref t) = tok { pos = t.span(); } break tok; } Some(x) => typ.extend(Some(x)), } }; let Some(tok) = next else { return Err(MacroError::Message( "expected expression, do not call this macro directly".into(), pos, )); }; let TT::Group(ref _group) = tok else { return Err(MacroError::Message( "expected parenthesis, do not call this macro directly".into(), tok.span(), )); }; let mut out = TokenStream::new(); let state = Self { typ: TT::Group(Group::new(Delimiter::None, typ)), }; let next = state.parse_primary(tok, it, &mut out)?; // A parenthesized expression is a single production of the grammar, // so the input must have reached the last token. if let Some(tok) = next { return Err(MacroError::Message( format!("unexpected token {tok}"), tok.span(), )); } Ok(out) } }