1use std::iter::Peekable;
2
3use proc_macro2::{Spacing, Span, TokenStream, TokenTree};
4
5pub struct Parser {
6 tokens: Peekable<Box<dyn Iterator<Item = TokenTree>>>,
7 last_span: Option<Span>,
8}
9
10impl From<TokenStream> for Parser {
11 fn from(value: TokenStream) -> Self {
12 let tokens: Box<dyn Iterator<Item = TokenTree>> = Box::new(value.into_iter());
13 Parser {
14 tokens: tokens.peekable(),
15 last_span: None,
16 }
17 }
18}
19
20impl Parser {
21 pub fn parse<T: Parse>(mut self) -> syn::Result<T> {
23 match T::parse(&mut self) {
24 Ok(Some(t)) => {
25 if let Some(s) = self.peek_span() {
26 return Err(syn::Error::new(
27 s,
28 format!("extra input after the end of what was expected"),
29 ));
30 }
31 Ok(t)
32 }
33
34 Err(e) => Err(e),
35
36 Ok(None) => {
37 let span = Span::call_site();
38 return Err(syn::Error::new(
39 span,
40 format!("expected a {}", T::description()),
41 ));
42 }
43 }
44 }
45
46 pub fn peek_token(&mut self) -> Option<&TokenTree> {
48 self.tokens.peek()
49 }
50
51 pub fn peek_span(&mut self) -> Option<Span> {
53 Some(self.peek_token()?.span())
54 }
55
56 pub fn last_span(&self) -> Option<Span> {
58 self.last_span.clone()
59 }
60
61 pub fn eat_token(&mut self) -> Option<TokenTree> {
63 let t = self.tokens.next()?;
64 self.last_span = Some(t.span());
65 Some(t)
66 }
67
68 pub fn eat_token_if(&mut self, test: impl Fn(&TokenTree) -> bool) -> Option<TokenTree> {
71 if test(self.peek_token()?) {
72 self.eat_token()
73 } else {
74 None
75 }
76 }
77
78 pub fn eat_map<R>(&mut self, op: impl FnOnce(&TokenTree) -> Option<R>) -> Option<R> {
79 let t = self.peek_token()?;
80 let r = op(t)?;
81 self.eat_token();
82 Some(r)
83 }
84
85 pub fn eat_keyword(&mut self, kw: &str) -> Option<()> {
86 assert!(KEYWORDS.contains(&kw));
87 self.eat_map(|t| match t {
88 TokenTree::Ident(i) => {
89 let s = i.to_string();
90 if s == kw {
91 Some(())
92 } else {
93 None
94 }
95 }
96 _ => None,
97 })
98 }
99
100 pub fn eat_ident(&mut self) -> Option<String> {
101 self.eat_map(|t| match t {
102 TokenTree::Ident(i) => {
103 let s = i.to_string();
104 if KEYWORDS.iter().any(|k| k == &s) {
105 None
106 } else {
107 Some(i.to_string())
108 }
109 }
110 _ => None,
111 })
112 }
113
114 pub fn eat_punct(&mut self, ch: char) -> Option<Span> {
115 self.eat_map(|t| match t {
116 TokenTree::Punct(punct) if punct.as_char() == ch => Some(punct.span()),
117 _ => None,
118 })
119 }
120}
121
122pub struct TextAccum<'p> {
128 text: String,
129 span: Span,
130 parser: &'p mut Parser,
131}
132
133impl<'p> TextAccum<'p> {
134 pub fn new(parser: &'p mut Parser, t0: TokenTree) -> Self {
135 let mut s = Self {
136 text: String::new(),
137 span: t0.span(),
138 parser,
139 };
140 s.accum_token(&t0);
141 s
142 }
143
144 pub fn accum(&mut self) -> Option<TokenTree> {
146 self.accum_if(|_| true)
147 }
148
149 pub fn accum_if(&mut self, test: impl Fn(&TokenTree) -> bool) -> Option<TokenTree> {
151 let t1 = self.parser.eat_token_if(test)?;
152 self.accum_token(&t1);
153 Some(t1)
154 }
155
156 fn accum_token(&mut self, token: &TokenTree) {
157 self.text.push_str(&token.to_string());
158
159 match token {
161 TokenTree::Group(_) => (),
162 TokenTree::Ident(_) => self.text.push(' '),
163 TokenTree::Punct(p) => match p.spacing() {
164 Spacing::Alone => self.text.push(' '),
165 Spacing::Joint => (),
166 },
167 TokenTree::Literal(_) => (),
168 }
169
170 self.span = self.span.join(token.span()).unwrap_or(self.span);
171 }
172
173 pub fn into_accumulated_result(self) -> (String, Span) {
175 (self.text, self.span)
176 }
177}
178
179pub trait Parse: Sized {
181 fn parse(p: &mut Parser) -> syn::Result<Option<Self>>;
189
190 fn parse_many(p: &mut Parser) -> syn::Result<Vec<Self>> {
192 let mut result = vec![];
193
194 while let Some(e) = Self::parse(p)? {
195 result.push(e);
196 }
197
198 Ok(result)
199 }
200
201 fn description() -> String;
204}
205
206pub const KEYWORDS: &[&str] = &["package", "class", "extends", "implements"];