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
paren(ts: TokenStream) -> TokenTree15 fn paren(ts: TokenStream) -> TokenTree {
16 TT::Group(Group::new(Delimiter::Parenthesis, ts))
17 }
18
ident(s: &'static str) -> TokenTree19 fn ident(s: &'static str) -> TokenTree {
20 TT::Ident(Ident::new(s, Span::call_site()))
21 }
22
punct(ch: char) -> TokenTree23 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
parse_primary( &self, tok: TokenTree, it: &mut dyn Iterator<Item = TokenTree>, out: &mut TokenStream, ) -> Result<Option<TokenTree>, Error>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
parse_binop< F: Fn( &Self, TokenTree, &mut dyn Iterator<Item = TokenTree>, &mut TokenStream, ) -> Result<Option<TokenTree>, Error>, >( &self, tok: TokenTree, it: &mut dyn Iterator<Item = TokenTree>, out: &mut TokenStream, ch: char, f: F, method: &'static str, ) -> Result<Option<TokenTree>, Error>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)*
parse_sub( &self, tok: TokenTree, it: &mut dyn Iterator<Item = TokenTree>, out: &mut TokenStream, ) -> Result<Option<TokenTree>, Error>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)*
parse_and( &self, tok: TokenTree, it: &mut dyn Iterator<Item = TokenTree>, out: &mut TokenStream, ) -> Result<Option<TokenTree>, Error>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)*
parse_xor( &self, tok: TokenTree, it: &mut dyn Iterator<Item = TokenTree>, out: &mut TokenStream, ) -> Result<Option<TokenTree>, Error>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)*
parse_or( &self, tok: TokenTree, it: &mut dyn Iterator<Item = TokenTree>, out: &mut TokenStream, ) -> Result<Option<TokenTree>, Error>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
parse( it: &mut dyn Iterator<Item = TokenTree>, ) -> Result<proc_macro2::TokenStream, Error>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