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_i_if<F>(&mut self, i: usize, f: F) -> Option<TokenTree>
58 where F: FnOnce(&TokenTree) -> bool,
59 {
60 let peek = self.peek_i(i)?;
61
62 if f(peek) {
63 self.nth(i)
64 } else {
65 None
66 }
67 }
68
69 #[track_caller]
72 pub fn next_tts<const N: usize>(&mut self) -> [TokenTree; N] {
73 array::from_fn(|_| self.next()
74 .expect("unexpected end of input"))
75 }
76
77 pub fn peek_i(&mut self, i: usize) -> Option<&TokenTree> {
78 for _ in self.buf.len()..=i {
79 self.buf.push_back(self.iter.next()?);
80 }
81 Some(&self.buf[i])
82 }
83
84 pub fn span(&mut self) -> Span {
87 let end_span = self.end_span;
88 self.peek().map_or(end_span, TokenTree::span)
89 }
90
91 pub fn span_i(&mut self, i: usize) -> Span {
94 let end_span = self.end_span;
95 self.peek_i(i).map_or(end_span, TokenTree::span)
96 }
97
98 pub fn peek_is<F>(&mut self, f: F) -> bool
99 where F: FnOnce(&TokenTree) -> bool,
100 {
101 self.peek().is_some_and(f)
102 }
103
104 pub fn peek_i_is<F>(&mut self, i: usize, f: F) -> bool
105 where F: FnOnce(&TokenTree) -> bool,
106 {
107 self.peek_i(i).is_some_and(f)
108 }
109
110 pub fn peek_puncts(
112 &mut self,
113 puncts: impl AsRef<[u8]>,
114 ) -> Option<impl Iterator<Item = &TokenTree>> {
115 self.peek_i_puncts(0, puncts)
116 }
117
118 pub fn peek_i_puncts(
120 &mut self,
121 i: usize,
122 puncts: impl AsRef<[u8]>,
123 ) -> Option<impl Iterator<Item = &TokenTree>> {
124 let mut prev = None;
125
126 let puncts = puncts.as_ref();
127
128 for (j, ch) in puncts.iter().copied().map(char::from).enumerate() {
129 if let Some(prev) = prev {
130 if prev == Alone { return None }
131 }
132
133 let tt = self.peek_i(i + j)?;
134 let TokenTree::Punct(p) = tt else { return None };
135 if p.as_char() != ch { return None }
136
137 prev = Some(p.spacing());
138 }
139 Some(self.buf.iter().skip(i).take(puncts.len()))
140 }
141
142 pub fn is_puncts(&mut self, puncts: impl AsRef<[u8]>) -> bool {
143 self.peek_puncts(puncts).is_some()
144 }
145
146 pub fn is_puncts_at(&mut self, i: usize, puncts: impl AsRef<[u8]>) -> bool {
147 self.peek_i_puncts(i, puncts).is_some()
148 }
149
150 pub fn next_puncts(
152 &mut self,
153 puncts: impl AsRef<[u8]>,
154 ) -> Option<impl Iterator<Item = TokenTree> + '_> {
155 let puncts = puncts.as_ref();
156 let _ = self.peek_puncts(puncts)?;
157
158 Some(self.buf.drain(..puncts.len()))
159 }
160
161 #[allow(clippy::missing_panics_doc)]
167 pub fn split_puncts(&mut self, puncts: impl AsRef<[u8]>) -> Option<TokenStream> {
168 let puncts = puncts.as_ref();
169 let mut i = 0;
170
171 loop {
172 if self.peek_i_puncts(i, puncts).is_some() {
173 let left = self.take(i).collect();
174 let _ = self.next_puncts(puncts).unwrap();
175 break Some(left);
176 }
177 self.peek_i(i)?;
178 i += 1;
179 }
180 }
181
182 #[allow(clippy::missing_panics_doc)]
188 pub fn split_puncts_exclude(&mut self, puncts: impl AsRef<[u8]>) -> Option<TokenStream> {
189 let puncts = puncts.as_ref();
190 let mut i = 0;
191
192 loop {
193 if self.peek_i_puncts(i, puncts).is_some() {
194 let left = self.take(i).collect();
195 break Some(left);
196 }
197 self.peek_i(i)?;
198 i += 1;
199 }
200 }
201
202 #[allow(clippy::missing_panics_doc)]
208 pub fn split_puncts_include(&mut self, puncts: impl AsRef<[u8]>) -> Option<TokenStream> {
209 let puncts = puncts.as_ref();
210 let mut i = 0;
211
212 loop {
213 if self.peek_i_puncts(i, puncts).is_some() {
214 let left = self.take(i+puncts.len()).collect();
215 break Some(left);
216 }
217 self.peek_i(i)?;
218 i += 1;
219 }
220 }
221
222 pub fn split_puncts_all<'a>(
226 &'a mut self,
227 puncts: impl AsRef<[u8]> + 'a,
228 ) -> impl Iterator<Item = TokenStream> + 'a {
229 iter::from_fn(move || {
230 self.split_puncts(puncts.as_ref())
231 .or_else(|| self.peek().is_some().then(|| self.collect()))
232 })
233 }
234
235 pub fn next_attributes(&mut self) -> Vec<TokenStream> {
239 let mut attributes = vec![];
240
241 while self.peek_puncts("#").is_some()
242 &&self.peek_i_is(1, |tt| tt.is_delimiter_bracket())
243 {
244 attributes.push(self.next_tts::<2>().into_iter().collect());
245 }
246
247 attributes
248 }
249
250 pub fn push_if_to<To, F>(&mut self, to: &mut To, f: F) -> bool
251 where To: Extend<TokenTree>,
252 F: FnOnce(&TokenTree) -> bool,
253 {
254 self.peek()
255 .is_some_and(f)
256 .then(|| to.extend(self.next()))
257 .is_some()
258 }
259
260 pub fn next_outer_attributes(&mut self) -> Vec<TokenStream> {
264 let mut attributes = vec![];
265
266 while self.peek_puncts("#").is_some()
267 &&self.peek_i_is(1, |tt| tt.is_punch('!'))
268 &&self.peek_i_is(2, |tt| tt.is_delimiter_bracket())
269 {
270 attributes.push(stream(self.next_tts::<3>()));
271 }
272
273 attributes
274 }
275
276 #[allow(clippy::missing_panics_doc)]
277 pub fn next_vis(&mut self) -> Option<TokenStream> {
278 if self.peek_is(|tt| tt.is_keyword("pub")) {
279 if self.peek_i_is(1, |tt| tt.is_delimiter_paren()) {
280 return Some(stream(self.next_tts::<2>()));
281 }
282 return Some(self.next().unwrap().into());
283 }
284 None
285 }
286
287 pub fn end_span(&self) -> Span {
288 self.end_span
289 }
290
291 pub fn set_end_span(&mut self, end_span: Span) {
292 self.end_span = end_span;
293 }
294}
295impl<I: Iterator<Item = TokenTree>> Iterator for ParseIter<I> {
296 type Item = TokenTree;
297
298 fn next(&mut self) -> Option<Self::Item> {
299 self.buf.pop_front()
300 .or_else(|| self.iter.next())
301 }
302
303 fn count(self) -> usize
304 where Self: Sized,
305 {
306 self.buf.len() + self.iter.count()
307 }
308
309 fn fold<B, F>(self, init: B, f: F) -> B
310 where Self: Sized,
311 F: FnMut(B, Self::Item) -> B,
312 {
313 self.buf.into_iter().chain(self.iter).fold(init, f)
314 }
315
316 fn size_hint(&self) -> (usize, Option<usize>) {
317 let (lo, hi) = self.iter.size_hint();
318 let lo = lo.saturating_add(self.buf.len());
319 let hi = hi.and_then(|hi| hi.checked_add(self.buf.len()));
320 (lo, hi)
321 }
322}
323impl<I: DoubleEndedIterator<Item = TokenTree>> DoubleEndedIterator for ParseIter<I> {
324 fn next_back(&mut self) -> Option<Self::Item> {
325 self.iter.next_back()
326 .or_else(|| self.buf.pop_back())
327 }
328
329 fn rfold<B, F>(self, init: B, f: F) -> B
330 where Self: Sized,
331 F: FnMut(B, Self::Item) -> B,
332 {
333 self.buf.into_iter().chain(self.iter).rfold(init, f)
334 }
335}
336impl<I: ExactSizeIterator<Item = TokenTree>> ExactSizeIterator for ParseIter<I> { }
337impl<I: FusedIterator<Item = TokenTree>> FusedIterator for ParseIter<I> { }