proc_macro_tool/
parse_iter.rs1use crate::{stream, TokenTreeExt as _};
2use proc_macro::{Group, Spacing::*, Span, TokenStream, TokenTree};
3use std::{array, collections::VecDeque, iter::{self, FusedIterator}};
4
5pub trait ParseIterExt: IntoIterator<Item = TokenTree> + Sized {
7 fn parse_iter(self) -> ParseIter<Self::IntoIter> {
9 ParseIter {
10 iter: self.into_iter(),
11 buf: VecDeque::new(),
12 end_span: Span::call_site(),
13 }
14 }
15}
16impl<I: IntoIterator<Item = TokenTree>> ParseIterExt for I { }
17
18pub trait ParseIterGroupExt: Into<Group> + Sized {
20 fn stream_parse_iter(self) -> ParseIter<<TokenStream as IntoIterator>::IntoIter> {
22 let group: Group = self.into();
23 ParseIter {
24 iter: group.stream().into_iter(),
25 buf: VecDeque::new(),
26 end_span: group.span_close(),
27 }
28 }
29}
30impl ParseIterGroupExt for Group { }
31
32#[derive(Debug, Clone)]
34pub struct ParseIter<I: Iterator<Item = TokenTree>> {
35 iter: I,
36 buf: VecDeque<TokenTree>,
37 end_span: Span,
38}
39
40impl<I: Iterator<Item = TokenTree>> ParseIter<I> {
41 pub fn peek(&mut self) -> Option<&TokenTree> {
42 self.peek_i(0)
43 }
44
45 pub fn next_if<F>(&mut self, f: F) -> Option<TokenTree>
46 where F: FnOnce(&TokenTree) -> bool,
47 {
48 let peek = self.peek()?;
49
50 if f(peek) {
51 self.next()
52 } else {
53 None
54 }
55 }
56
57 pub fn next_is<F>(&mut self, f: F) -> bool
58 where F: FnOnce(TokenTree) -> bool,
59 {
60 self.next().is_some_and(f)
61 }
62
63 pub fn next_i_if<F>(&mut self, i: usize, f: F) -> Option<TokenTree>
64 where F: FnOnce(&TokenTree) -> bool,
65 {
66 let peek = self.peek_i(i)?;
67
68 if f(peek) {
69 self.nth(i)
70 } else {
71 None
72 }
73 }
74
75 #[track_caller]
78 pub fn next_tts<const N: usize>(&mut self) -> [TokenTree; N] {
79 array::from_fn(|_| self.next()
80 .expect("unexpected end of input"))
81 }
82
83 pub fn peek_i(&mut self, i: usize) -> Option<&TokenTree> {
84 for _ in self.buf.len()..=i {
85 self.buf.push_back(self.iter.next()?);
86 }
87 Some(&self.buf[i])
88 }
89
90 pub fn span(&mut self) -> Span {
93 let end_span = self.end_span;
94 self.peek().map_or(end_span, TokenTree::span)
95 }
96
97 pub fn span_i(&mut self, i: usize) -> Span {
100 let end_span = self.end_span;
101 self.peek_i(i).map_or(end_span, TokenTree::span)
102 }
103
104 pub fn peek_is<F>(&mut self, f: F) -> bool
105 where F: FnOnce(&TokenTree) -> bool,
106 {
107 self.peek().is_some_and(f)
108 }
109
110 pub fn peek_i_is<F>(&mut self, i: usize, f: F) -> bool
111 where F: FnOnce(&TokenTree) -> bool,
112 {
113 self.peek_i(i).is_some_and(f)
114 }
115
116 pub fn peek_puncts(
118 &mut self,
119 puncts: impl AsRef<[u8]>,
120 ) -> Option<impl Iterator<Item = &TokenTree>> {
121 self.peek_i_puncts(0, puncts)
122 }
123
124 pub fn peek_i_puncts(
126 &mut self,
127 i: usize,
128 puncts: impl AsRef<[u8]>,
129 ) -> Option<impl Iterator<Item = &TokenTree>> {
130 let mut prev = None;
131
132 let puncts = puncts.as_ref();
133
134 for (j, ch) in puncts.iter().copied().map(char::from).enumerate() {
135 if let Some(prev) = prev {
136 if prev == Alone { return None }
137 }
138
139 let tt = self.peek_i(i + j)?;
140 let TokenTree::Punct(p) = tt else { return None };
141 if p.as_char() != ch { return None }
142
143 prev = Some(p.spacing());
144 }
145 Some(self.buf.iter().skip(i).take(puncts.len()))
146 }
147
148 pub fn is_puncts(&mut self, puncts: impl AsRef<[u8]>) -> bool {
149 self.peek_puncts(puncts).is_some()
150 }
151
152 pub fn is_puncts_at(&mut self, i: usize, puncts: impl AsRef<[u8]>) -> bool {
153 self.peek_i_puncts(i, puncts).is_some()
154 }
155
156 pub fn next_puncts(
158 &mut self,
159 puncts: impl AsRef<[u8]>,
160 ) -> Option<impl Iterator<Item = TokenTree> + '_> {
161 let puncts = puncts.as_ref();
162 let _ = self.peek_puncts(puncts)?;
163
164 Some(self.buf.drain(..puncts.len()))
165 }
166
167 #[allow(clippy::missing_panics_doc)]
173 pub fn split_puncts(&mut self, puncts: impl AsRef<[u8]>) -> Option<TokenStream> {
174 let puncts = puncts.as_ref();
175 let mut i = 0;
176
177 loop {
178 if self.peek_i_puncts(i, puncts).is_some() {
179 let left = self.take(i).collect();
180 let _ = self.next_puncts(puncts).unwrap();
181 break Some(left);
182 }
183 self.peek_i(i)?;
184 i += 1;
185 }
186 }
187
188 #[allow(clippy::missing_panics_doc)]
194 pub fn split_puncts_exclude(&mut self, puncts: impl AsRef<[u8]>) -> Option<TokenStream> {
195 let puncts = puncts.as_ref();
196 let mut i = 0;
197
198 loop {
199 if self.peek_i_puncts(i, puncts).is_some() {
200 let left = self.take(i).collect();
201 break Some(left);
202 }
203 self.peek_i(i)?;
204 i += 1;
205 }
206 }
207
208 #[allow(clippy::missing_panics_doc)]
214 pub fn split_puncts_include(&mut self, puncts: impl AsRef<[u8]>) -> Option<TokenStream> {
215 let puncts = puncts.as_ref();
216 let mut i = 0;
217
218 loop {
219 if self.peek_i_puncts(i, puncts).is_some() {
220 let left = self.take(i+puncts.len()).collect();
221 break Some(left);
222 }
223 self.peek_i(i)?;
224 i += 1;
225 }
226 }
227
228 pub fn split_puncts_all<'a>(
232 &'a mut self,
233 puncts: impl AsRef<[u8]> + 'a,
234 ) -> impl Iterator<Item = TokenStream> + 'a {
235 iter::from_fn(move || {
236 self.split_puncts(puncts.as_ref())
237 .or_else(|| self.peek().is_some().then(|| self.collect()))
238 })
239 }
240
241 pub fn next_attributes(&mut self) -> Vec<TokenStream> {
245 let mut attributes = vec![];
246
247 while self.peek_puncts("#").is_some()
248 &&self.peek_i_is(1, |tt| tt.is_delimiter_bracket())
249 {
250 attributes.push(self.next_tts::<2>().into_iter().collect());
251 }
252
253 attributes
254 }
255
256 pub fn push_if_to<To, F>(&mut self, to: &mut To, f: F) -> bool
257 where To: Extend<TokenTree>,
258 F: FnOnce(&TokenTree) -> bool,
259 {
260 self.peek()
261 .is_some_and(f)
262 .then(|| to.extend(self.next()))
263 .is_some()
264 }
265
266 pub fn next_outer_attributes(&mut self) -> Vec<TokenStream> {
270 let mut attributes = vec![];
271
272 while self.peek_puncts("#").is_some()
273 &&self.peek_i_is(1, |tt| tt.is_punch('!'))
274 &&self.peek_i_is(2, |tt| tt.is_delimiter_bracket())
275 {
276 attributes.push(stream(self.next_tts::<3>()));
277 }
278
279 attributes
280 }
281
282 #[allow(clippy::missing_panics_doc)]
283 pub fn next_vis(&mut self) -> Option<TokenStream> {
284 if self.peek_is(|tt| tt.is_keyword("pub")) {
285 if self.peek_i_is(1, |tt| tt.is_delimiter_paren()) {
286 return Some(stream(self.next_tts::<2>()));
287 }
288 return Some(self.next().unwrap().into());
289 }
290 None
291 }
292
293 pub fn end_span(&self) -> Span {
294 self.end_span
295 }
296
297 pub fn set_end_span(&mut self, end_span: Span) {
298 self.end_span = end_span;
299 }
300}
301impl<I: Iterator<Item = TokenTree>> Iterator for ParseIter<I> {
302 type Item = TokenTree;
303
304 fn next(&mut self) -> Option<Self::Item> {
305 self.buf.pop_front()
306 .or_else(|| self.iter.next())
307 }
308
309 fn count(self) -> usize
310 where Self: Sized,
311 {
312 self.buf.len() + self.iter.count()
313 }
314
315 fn fold<B, F>(self, init: B, f: F) -> B
316 where Self: Sized,
317 F: FnMut(B, Self::Item) -> B,
318 {
319 self.buf.into_iter().chain(self.iter).fold(init, f)
320 }
321
322 fn size_hint(&self) -> (usize, Option<usize>) {
323 let (lo, hi) = self.iter.size_hint();
324 let lo = lo.saturating_add(self.buf.len());
325 let hi = hi.and_then(|hi| hi.checked_add(self.buf.len()));
326 (lo, hi)
327 }
328}
329impl<I: DoubleEndedIterator<Item = TokenTree>> DoubleEndedIterator for ParseIter<I> {
330 fn next_back(&mut self) -> Option<Self::Item> {
331 self.iter.next_back()
332 .or_else(|| self.buf.pop_back())
333 }
334
335 fn rfold<B, F>(self, init: B, f: F) -> B
336 where Self: Sized,
337 F: FnMut(B, Self::Item) -> B,
338 {
339 self.buf.into_iter().chain(self.iter).rfold(init, f)
340 }
341}
342impl<I: ExactSizeIterator<Item = TokenTree>> ExactSizeIterator for ParseIter<I> { }
343impl<I: FusedIterator<Item = TokenTree>> FusedIterator for ParseIter<I> { }