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