loess_rust/lexical_structure/
tokens.rs1use loess::{Error, ErrorPriority, Errors, Input, IntoTokens, PeekFrom, PopFrom, SimpleSpanned};
2use proc_macro2::{Ident, Punct, Spacing, Span, TokenStream, TokenTree};
3
4#[derive(Clone)]
6pub struct RArrow(pub Punct, pub Punct);
7
8impl Default for RArrow {
9 fn default() -> Self {
10 Self(
11 Punct::new('-', Spacing::Joint).with_span(Span::mixed_site()),
12 Punct::new('>', Spacing::Alone).with_span(Span::mixed_site()),
13 )
14 }
15}
16
17impl PeekFrom for RArrow {
19 fn peek_from(input: &Input) -> bool {
20 input.peek(|tts, _| {
21 matches!(tts, [TokenTree::Punct(minus), TokenTree::Punct(gt)]
22 if minus.as_char() == '-' && minus.spacing() == Spacing::Joint && gt.as_char() == '>')
23 })
24 }
25}
26
27impl PopFrom for RArrow {
28 fn pop_from(input: &mut Input, errors: &mut Errors) -> Result<Self, ()> {
29 input
30 .pop_or_replace(|tts, _| match tts {
31 [TokenTree::Punct(minus), TokenTree::Punct(gt)]
32 if minus.as_char() == '-'
33 && minus.spacing() == Spacing::Joint
34 && gt.as_char() == '>' =>
35 {
36 Ok(Self(minus, gt))
37 }
38 other => Err(other),
39 })
40 .map_err(|spans| {
41 errors.push(Error::new(ErrorPriority::GRAMMAR, "Expected `->`.", spans))
42 })
43 }
44}
45
46impl IntoTokens for RArrow {
47 fn into_tokens(self, _root: &TokenStream, tokens: &mut impl Extend<TokenTree>) {
48 let Self(minus, gt) = self;
49 tokens.extend([minus.into(), gt.into()])
50 }
51}
52
53#[derive(Clone)]
55pub struct DotDot(pub Punct, pub Punct);
56
57impl Default for DotDot {
58 fn default() -> Self {
59 Self(
60 Punct::new('.', Spacing::Joint).with_span(Span::mixed_site()),
61 Punct::new('.', Spacing::Alone).with_span(Span::mixed_site()),
62 )
63 }
64}
65
66impl PeekFrom for DotDot {
68 fn peek_from(input: &Input) -> bool {
69 input.peek(|tts, mut rest| {
70 matches!(tts, [TokenTree::Punct(dot0), TokenTree::Punct(dot1)]
71 if dot0.as_char() == '.'
72 && dot0.spacing() == Spacing::Joint
73 && dot1.as_char() == '.'
74 && (dot1.spacing() == Spacing::Alone || !matches!(rest.next(), Some(TokenTree::Punct(next_punct)) if matches!(next_punct.as_char(), '.' | '='))))
75 })
76 }
77}
78
79impl PopFrom for DotDot {
80 fn pop_from(input: &mut Input, errors: &mut Errors) -> Result<Self, ()> {
81 input
82 .pop_or_replace(|tts, rest| match tts {
83 [TokenTree::Punct(dot0), TokenTree::Punct(dot1)]
84 if dot0.as_char() == '.'
85 && dot0.spacing() == Spacing::Joint
86 && dot1.as_char() == '.'
87 && (dot1.spacing() == Spacing::Alone || !matches!(rest.front(), Some(TokenTree::Punct(next_punct)) if matches!(next_punct.as_char(), '.' | '=')))
88 => { Ok(Self(dot0, dot1)) }
89 other => Err(other),
90 })
91 .map_err(|spans| {
92 errors.push(Error::new(ErrorPriority::GRAMMAR, "Expected `..`.", spans))
93 })
94 }
95}
96
97impl IntoTokens for DotDot {
98 fn into_tokens(self, _root: &TokenStream, tokens: &mut impl Extend<TokenTree>) {
99 let Self(dot0, dot1) = self;
100 tokens.extend([dot0.into(), dot1.into()])
101 }
102}
103
104#[derive(Clone)]
106pub struct Semi(pub Punct);
107
108impl Default for Semi {
109 fn default() -> Self {
110 Self(Punct::new(';', Spacing::Alone).with_span(Span::mixed_site()))
111 }
112}
113
114impl PeekFrom for Semi {
116 fn peek_from(input: &Input) -> bool {
117 matches!(
118 input.front(),
119 Some(TokenTree::Punct(semi)) if semi.as_char() == ';',
120 )
121 }
122}
123
124impl PopFrom for Semi {
125 fn pop_from(input: &mut Input, errors: &mut Errors) -> Result<Self, ()> {
126 input
127 .pop_or_replace(|tts, _| match tts {
128 [TokenTree::Punct(semi)] if semi.as_char() == ';' => Ok(Self(semi)),
129 other => Err(other),
130 })
131 .map_err(|spans| {
132 errors.push(Error::new(ErrorPriority::GRAMMAR, "Expected `;`.", spans))
133 })
134 }
135}
136
137impl IntoTokens for Semi {
138 fn into_tokens(self, root: &TokenStream, tokens: &mut impl Extend<TokenTree>) {
139 self.0.into_tokens(root, tokens)
140 }
141}
142
143#[derive(Clone)]
145pub struct Comma(pub Punct);
146
147impl Default for Comma {
148 fn default() -> Self {
149 Self(Punct::new(',', Spacing::Alone).with_span(Span::mixed_site()))
150 }
151}
152
153impl PeekFrom for Comma {
155 fn peek_from(input: &Input) -> bool {
156 matches!(
157 input.front(),
158 Some(TokenTree::Punct(comma)) if comma.as_char() == ',',
159 )
160 }
161}
162
163impl PopFrom for Comma {
164 fn pop_from(input: &mut Input, errors: &mut Errors) -> Result<Self, ()> {
165 input
166 .pop_or_replace(|tts, _| match tts {
167 [TokenTree::Punct(comma)] if comma.as_char() == ',' => Ok(Self(comma)),
168 other => Err(other),
169 })
170 .map_err(|spans| {
171 errors.push(Error::new(ErrorPriority::GRAMMAR, "Expected `,`.", spans))
172 })
173 }
174}
175
176impl IntoTokens for Comma {
177 fn into_tokens(self, root: &TokenStream, tokens: &mut impl Extend<TokenTree>) {
178 self.0.into_tokens(root, tokens)
179 }
180}
181
182#[derive(Clone)]
184pub struct Or(pub Punct);
185
186impl Default for Or {
187 fn default() -> Self {
188 Self(Punct::new('|', Spacing::Alone).with_span(Span::mixed_site()))
189 }
190}
191
192impl PeekFrom for Or {
194 fn peek_from(input: &Input) -> bool {
195 matches!(
196 input.front(),
197 Some(TokenTree::Punct(or)) if or.as_char() == '|' && or.spacing() == Spacing::Alone,
198 )
199 }
200}
201
202impl PopFrom for Or {
203 fn pop_from(input: &mut Input, errors: &mut Errors) -> Result<Self, ()> {
204 input
205 .pop_or_replace(|tts, _| match tts {
206 [TokenTree::Punct(or)] if or.as_char() == '|' && or.spacing() == Spacing::Alone => {
207 Ok(Self(or))
208 }
209 other => Err(other),
210 })
211 .map_err(|spans| {
212 errors.push(Error::new(ErrorPriority::GRAMMAR, "Expected `|`.", spans))
213 })
214 }
215}
216
217impl IntoTokens for Or {
218 fn into_tokens(self, root: &TokenStream, tokens: &mut impl Extend<TokenTree>) {
219 self.0.into_tokens(root, tokens)
220 }
221}
222
223#[derive(Clone)]
225pub struct Dot(pub Punct);
226
227impl Default for Dot {
228 fn default() -> Self {
229 Self(Punct::new('.', Spacing::Alone).with_span(Span::mixed_site()))
230 }
231}
232
233impl PeekFrom for Dot {
235 fn peek_from(input: &Input) -> bool {
236 matches!(
237 input.front(),
238 Some(TokenTree::Punct(dot)) if dot.as_char() == '.' && dot.spacing() == Spacing::Alone,
239 )
240 }
241}
242
243impl PopFrom for Dot {
244 fn pop_from(input: &mut Input, errors: &mut Errors) -> Result<Self, ()> {
245 input
246 .pop_or_replace(|tts, _| match tts {
247 [TokenTree::Punct(dot)]
248 if dot.as_char() == '.' && dot.spacing() == Spacing::Alone =>
249 {
250 Ok(Self(dot))
251 }
252 other => Err(other),
253 })
254 .map_err(|spans| {
255 errors.push(Error::new(ErrorPriority::GRAMMAR, "Expected `.`.", spans))
256 })
257 }
258}
259
260impl IntoTokens for Dot {
261 fn into_tokens(self, root: &TokenStream, tokens: &mut impl Extend<TokenTree>) {
262 self.0.into_tokens(root, tokens)
263 }
264}
265
266#[derive(Clone)]
268pub struct Colon(pub Punct);
269
270impl Default for Colon {
271 fn default() -> Self {
272 Self(Punct::new(':', Spacing::Alone).with_span(Span::mixed_site()))
273 }
274}
275
276impl PeekFrom for Colon {
278 fn peek_from(input: &Input) -> bool {
279 matches!(
280 input.front(),
281 Some(TokenTree::Punct(colon)) if colon.as_char() == ':' && colon.spacing() == Spacing::Alone,
282 )
283 }
284}
285
286impl PopFrom for Colon {
287 fn pop_from(input: &mut Input, errors: &mut Errors) -> Result<Self, ()> {
288 input
289 .pop_or_replace(|tts, _| match tts {
290 [TokenTree::Punct(colon)]
291 if colon.as_char() == ':' && colon.spacing() == Spacing::Alone =>
292 {
293 Ok(Self(colon))
294 }
295 other => Err(other),
296 })
297 .map_err(|spans| {
298 errors.push(Error::new(ErrorPriority::GRAMMAR, "Expected `:`.", spans))
299 })
300 }
301}
302
303impl IntoTokens for Colon {
304 fn into_tokens(self, root: &TokenStream, tokens: &mut impl Extend<TokenTree>) {
305 self.0.into_tokens(root, tokens)
306 }
307}
308
309macro_rules! ident_token {
310 ($name:ident = $str:literal) => {
311 #[doc = concat!("`", $str, "`")]
312 #[derive(Clone)]
313 pub struct $name(pub Ident);
314
315 #[doc = concat!("`", $str, "`")]
316 impl PeekFrom for $name {
317 fn peek_from(input: &Input) -> bool {
318 matches!(input.front(), Some(TokenTree::Ident(ident)) if ident == $str)
319 }
320 }
321
322 impl PopFrom for $name {
323 fn pop_from(input: &mut Input, errors: &mut Errors) -> Result<Self, ()>
324 where
325 Self: Sized,
326 {
327 input
328 .pop_or_replace(|t, _| match t {
329 [TokenTree::Ident(ident)] if ident == $str => Ok(Self(ident)),
330 other => Err(other),
331 })
332 .map_err(|spans| {
333 errors.push(Error::new(ErrorPriority::TOKEN, concat!("Expected `", $str, "`."), spans))
334 })
335 }
336 }
337
338 impl IntoTokens for $name {
339 fn into_tokens(self, root: &TokenStream, tokens: &mut impl Extend<TokenTree>) {
340 self.0.into_tokens(root, tokens)
341 }
342 }
343 };
344}
345
346ident_token!(As = "as");
347ident_token!(Async = "async");
348ident_token!(Await = "await");
349ident_token!(Box = "box");
350ident_token!(Const = "const");
351ident_token!(For = "for");
352ident_token!(In = "in");
353ident_token!(Let = "let");
354ident_token!(Pub = "pub");
355ident_token!(SelfLowercase = "self");
356ident_token!(Struct = "struct");