1#[cfg(feature = "extra-traits")]
92use std::cmp;
93#[cfg(feature = "extra-traits")]
94use std::fmt::{self, Debug};
95#[cfg(feature = "extra-traits")]
96use std::hash::{Hash, Hasher};
97use std::ops::{Deref, DerefMut};
98
99#[cfg(feature = "parsing")]
100use proc_macro2::Delimiter;
101#[cfg(any(feature = "parsing", feature = "printing"))]
102use proc_macro2::Ident;
103use proc_macro2::Span;
104#[cfg(feature = "printing")]
105use proc_macro2::TokenStream;
106#[cfg(feature = "printing")]
107use quote::{ToTokens, TokenStreamExt};
108
109use self::private::WithSpan;
110#[cfg(feature = "parsing")]
111use crate::buffer::Cursor;
112#[cfg(feature = "parsing")]
113use crate::error::Result;
114#[cfg(any(feature = "full", feature = "derive"))]
115#[cfg(feature = "parsing")]
116use crate::lifetime::Lifetime;
117#[cfg(any(feature = "full", feature = "derive"))]
118#[cfg(feature = "parsing")]
119use crate::lit::{Lit, LitBool, LitByte, LitByteStr, LitChar, LitFloat, LitInt, LitStr};
120#[cfg(feature = "parsing")]
121use crate::lookahead;
122#[cfg(feature = "parsing")]
123use crate::parse::{Parse, ParseStream};
124use crate::span::IntoSpans;
125
126#[cfg(feature = "parsing")]
130pub trait Token: private::Sealed {
131 #[doc(hidden)]
133 fn peek(cursor: Cursor) -> bool;
134
135 #[doc(hidden)]
137 fn display() -> &'static str;
138}
139
140mod private {
141 use proc_macro2::Span;
142
143 #[cfg(feature = "parsing")]
144 pub trait Sealed {}
145
146 #[repr(C)]
149 pub struct WithSpan {
150 pub span: Span,
151 }
152}
153
154#[cfg(feature = "parsing")]
155impl private::Sealed for Ident {}
156
157#[cfg(any(feature = "full", feature = "derive"))]
158#[cfg(feature = "parsing")]
159fn peek_impl(cursor: Cursor, peek: fn(ParseStream) -> bool) -> bool {
160 use crate::parse::Unexpected;
161 use std::cell::Cell;
162 use std::rc::Rc;
163
164 let scope = Span::call_site();
165 let unexpected = Rc::new(Cell::new(Unexpected::None));
166 let buffer = crate::parse::new_parse_buffer(scope, cursor, unexpected);
167 peek(&buffer)
168}
169
170#[cfg(any(feature = "full", feature = "derive"))]
171macro_rules! impl_token {
172 ($name:ident $display:expr) => {
173 #[cfg(feature = "parsing")]
174 impl Token for $name {
175 fn peek(cursor: Cursor) -> bool {
176 fn peek(input: ParseStream) -> bool {
177 <$name as Parse>::parse(input).is_ok()
178 }
179 peek_impl(cursor, peek)
180 }
181
182 fn display() -> &'static str {
183 $display
184 }
185 }
186
187 #[cfg(feature = "parsing")]
188 impl private::Sealed for $name {}
189 };
190}
191
192#[cfg(any(feature = "full", feature = "derive"))]
193impl_token!(Lifetime "lifetime");
194#[cfg(any(feature = "full", feature = "derive"))]
195impl_token!(Lit "literal");
196#[cfg(any(feature = "full", feature = "derive"))]
197impl_token!(LitStr "string literal");
198#[cfg(any(feature = "full", feature = "derive"))]
199impl_token!(LitByteStr "byte string literal");
200#[cfg(any(feature = "full", feature = "derive"))]
201impl_token!(LitByte "byte literal");
202#[cfg(any(feature = "full", feature = "derive"))]
203impl_token!(LitChar "character literal");
204#[cfg(any(feature = "full", feature = "derive"))]
205impl_token!(LitInt "integer literal");
206#[cfg(any(feature = "full", feature = "derive"))]
207impl_token!(LitFloat "floating point literal");
208#[cfg(any(feature = "full", feature = "derive"))]
209impl_token!(LitBool "boolean literal");
210
211#[doc(hidden)]
213#[cfg(feature = "parsing")]
214pub trait CustomToken {
215 fn peek(cursor: Cursor) -> bool;
216 fn display() -> &'static str;
217}
218
219#[cfg(feature = "parsing")]
220impl<T: CustomToken> private::Sealed for T {}
221
222#[cfg(feature = "parsing")]
223impl<T: CustomToken> Token for T {
224 fn peek(cursor: Cursor) -> bool {
225 <Self as CustomToken>::peek(cursor)
226 }
227
228 fn display() -> &'static str {
229 <Self as CustomToken>::display()
230 }
231}
232
233macro_rules! define_keywords {
234 ($($token:tt pub struct $name:ident #[$doc:meta])*) => {
235 $(
236 #[cfg_attr(feature = "clone-impls", derive(Copy, Clone))]
237 #[$doc]
238 pub struct $name {
244 pub span: Span,
245 }
246
247 #[doc(hidden)]
248 #[allow(non_snake_case)]
249 pub fn $name<S: IntoSpans<[Span; 1]>>(span: S) -> $name {
250 $name {
251 span: span.into_spans()[0],
252 }
253 }
254
255 impl std::default::Default for $name {
256 fn default() -> Self {
257 $name {
258 span: Span::call_site(),
259 }
260 }
261 }
262
263 #[cfg(feature = "extra-traits")]
264 impl Debug for $name {
265 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
266 f.write_str(stringify!($name))
267 }
268 }
269
270 #[cfg(feature = "extra-traits")]
271 impl cmp::Eq for $name {}
272
273 #[cfg(feature = "extra-traits")]
274 impl PartialEq for $name {
275 fn eq(&self, _other: &$name) -> bool {
276 true
277 }
278 }
279
280 #[cfg(feature = "extra-traits")]
281 impl Hash for $name {
282 fn hash<H: Hasher>(&self, _state: &mut H) {}
283 }
284
285 #[cfg(feature = "printing")]
286 impl ToTokens for $name {
287 fn to_tokens(&self, tokens: &mut TokenStream) {
288 printing::keyword($token, self.span, tokens);
289 }
290 }
291
292 #[cfg(feature = "parsing")]
293 impl Parse for $name {
294 fn parse(input: ParseStream) -> Result<Self> {
295 Ok($name {
296 span: parsing::keyword(input, $token)?,
297 })
298 }
299 }
300
301 #[cfg(feature = "parsing")]
302 impl Token for $name {
303 fn peek(cursor: Cursor) -> bool {
304 parsing::peek_keyword(cursor, $token)
305 }
306
307 fn display() -> &'static str {
308 concat!("`", $token, "`")
309 }
310 }
311
312 #[cfg(feature = "parsing")]
313 impl private::Sealed for $name {}
314 )*
315 };
316}
317
318macro_rules! impl_deref_if_len_is_1 {
319 ($name:ident/1) => {
320 impl Deref for $name {
321 type Target = WithSpan;
322
323 fn deref(&self) -> &Self::Target {
324 unsafe { &*(self as *const Self as *const WithSpan) }
325 }
326 }
327
328 impl DerefMut for $name {
329 fn deref_mut(&mut self) -> &mut Self::Target {
330 unsafe { &mut *(self as *mut Self as *mut WithSpan) }
331 }
332 }
333 };
334
335 ($name:ident/$len:tt) => {};
336}
337
338macro_rules! define_punctuation_structs {
339 ($($token:tt pub struct $name:ident/$len:tt #[$doc:meta])*) => {
340 $(
341 #[cfg_attr(feature = "clone-impls", derive(Copy, Clone))]
342 #[repr(C)]
343 #[$doc]
344 pub struct $name {
350 pub spans: [Span; $len],
351 }
352
353 #[doc(hidden)]
354 #[allow(non_snake_case)]
355 pub fn $name<S: IntoSpans<[Span; $len]>>(spans: S) -> $name {
356 $name {
357 spans: spans.into_spans(),
358 }
359 }
360
361 impl std::default::Default for $name {
362 fn default() -> Self {
363 $name {
364 spans: [Span::call_site(); $len],
365 }
366 }
367 }
368
369 #[cfg(feature = "extra-traits")]
370 impl Debug for $name {
371 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
372 f.write_str(stringify!($name))
373 }
374 }
375
376 #[cfg(feature = "extra-traits")]
377 impl cmp::Eq for $name {}
378
379 #[cfg(feature = "extra-traits")]
380 impl PartialEq for $name {
381 fn eq(&self, _other: &$name) -> bool {
382 true
383 }
384 }
385
386 #[cfg(feature = "extra-traits")]
387 impl Hash for $name {
388 fn hash<H: Hasher>(&self, _state: &mut H) {}
389 }
390
391 impl_deref_if_len_is_1!($name/$len);
392 )*
393 };
394}
395
396macro_rules! define_punctuation {
397 ($($token:tt pub struct $name:ident/$len:tt #[$doc:meta])*) => {
398 $(
399 define_punctuation_structs! {
400 $token pub struct $name/$len #[$doc]
401 }
402
403 #[cfg(feature = "printing")]
404 impl ToTokens for $name {
405 fn to_tokens(&self, tokens: &mut TokenStream) {
406 printing::punct($token, &self.spans, tokens);
407 }
408 }
409
410 #[cfg(feature = "parsing")]
411 impl Parse for $name {
412 fn parse(input: ParseStream) -> Result<Self> {
413 Ok($name {
414 spans: parsing::punct(input, $token)?,
415 })
416 }
417 }
418
419 #[cfg(feature = "parsing")]
420 impl Token for $name {
421 fn peek(cursor: Cursor) -> bool {
422 parsing::peek_punct(cursor, $token)
423 }
424
425 fn display() -> &'static str {
426 concat!("`", $token, "`")
427 }
428 }
429
430 #[cfg(feature = "parsing")]
431 impl private::Sealed for $name {}
432 )*
433 };
434}
435
436macro_rules! define_delimiters {
437 ($($token:tt pub struct $name:ident #[$doc:meta])*) => {
438 $(
439 #[cfg_attr(feature = "clone-impls", derive(Copy, Clone))]
440 #[$doc]
441 pub struct $name {
442 pub span: Span,
443 }
444
445 #[doc(hidden)]
446 #[allow(non_snake_case)]
447 pub fn $name<S: IntoSpans<[Span; 1]>>(span: S) -> $name {
448 $name {
449 span: span.into_spans()[0],
450 }
451 }
452
453 impl std::default::Default for $name {
454 fn default() -> Self {
455 $name {
456 span: Span::call_site(),
457 }
458 }
459 }
460
461 #[cfg(feature = "extra-traits")]
462 impl Debug for $name {
463 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
464 f.write_str(stringify!($name))
465 }
466 }
467
468 #[cfg(feature = "extra-traits")]
469 impl cmp::Eq for $name {}
470
471 #[cfg(feature = "extra-traits")]
472 impl PartialEq for $name {
473 fn eq(&self, _other: &$name) -> bool {
474 true
475 }
476 }
477
478 #[cfg(feature = "extra-traits")]
479 impl Hash for $name {
480 fn hash<H: Hasher>(&self, _state: &mut H) {}
481 }
482
483 impl $name {
484 #[cfg(feature = "printing")]
485 pub fn surround<F>(&self, tokens: &mut TokenStream, f: F)
486 where
487 F: FnOnce(&mut TokenStream),
488 {
489 printing::delim($token, self.span, tokens, f);
490 }
491 }
492
493 #[cfg(feature = "parsing")]
494 impl private::Sealed for $name {}
495 )*
496 };
497}
498
499define_punctuation_structs! {
500 "_" pub struct Underscore/1 }
502
503#[cfg(feature = "printing")]
504impl ToTokens for Underscore {
505 fn to_tokens(&self, tokens: &mut TokenStream) {
506 tokens.append(Ident::new("_", self.span));
507 }
508}
509
510#[cfg(feature = "parsing")]
511impl Parse for Underscore {
512 fn parse(input: ParseStream) -> Result<Self> {
513 input.step(|cursor| {
514 if let Some((ident, rest)) = cursor.ident() {
515 if ident == "_" {
516 return Ok((Underscore(ident.span()), rest));
517 }
518 }
519 if let Some((punct, rest)) = cursor.punct() {
520 if punct.as_char() == '_' {
521 return Ok((Underscore(punct.span()), rest));
522 }
523 }
524 Err(cursor.error("expected `_`"))
525 })
526 }
527}
528
529#[cfg(feature = "parsing")]
530impl Token for Underscore {
531 fn peek(cursor: Cursor) -> bool {
532 if let Some((ident, _rest)) = cursor.ident() {
533 return ident == "_";
534 }
535 if let Some((punct, _rest)) = cursor.punct() {
536 return punct.as_char() == '_';
537 }
538 false
539 }
540
541 fn display() -> &'static str {
542 "`_`"
543 }
544}
545
546#[cfg(feature = "parsing")]
547impl private::Sealed for Underscore {}
548
549#[cfg(feature = "parsing")]
550impl Token for Paren {
551 fn peek(cursor: Cursor) -> bool {
552 lookahead::is_delimiter(cursor, Delimiter::Parenthesis)
553 }
554
555 fn display() -> &'static str {
556 "parentheses"
557 }
558}
559
560#[cfg(feature = "parsing")]
561impl Token for Brace {
562 fn peek(cursor: Cursor) -> bool {
563 lookahead::is_delimiter(cursor, Delimiter::Brace)
564 }
565
566 fn display() -> &'static str {
567 "curly braces"
568 }
569}
570
571#[cfg(feature = "parsing")]
572impl Token for Bracket {
573 fn peek(cursor: Cursor) -> bool {
574 lookahead::is_delimiter(cursor, Delimiter::Bracket)
575 }
576
577 fn display() -> &'static str {
578 "square brackets"
579 }
580}
581
582#[cfg(feature = "parsing")]
583impl Token for Group {
584 fn peek(cursor: Cursor) -> bool {
585 lookahead::is_delimiter(cursor, Delimiter::None)
586 }
587
588 fn display() -> &'static str {
589 "invisible group"
590 }
591}
592
593define_keywords! {
594 "abstract" pub struct Abstract "as" pub struct As "async" pub struct Async "auto" pub struct Auto "await" pub struct Await "become" pub struct Become "box" pub struct Box "break" pub struct Break "const" pub struct Const "continue" pub struct Continue "crate" pub struct Crate "default" pub struct Default "do" pub struct Do "dyn" pub struct Dyn "else" pub struct Else "enum" pub struct Enum "extern" pub struct Extern "final" pub struct Final "fn" pub struct Fn "for" pub struct For "if" pub struct If "impl" pub struct Impl "in" pub struct In "let" pub struct Let "loop" pub struct Loop "macro" pub struct Macro "match" pub struct Match "mod" pub struct Mod "move" pub struct Move "mut" pub struct Mut "override" pub struct Override "priv" pub struct Priv "pub" pub struct Pub "ref" pub struct Ref "return" pub struct Return "Self" pub struct SelfType "self" pub struct SelfValue "static" pub struct Static "struct" pub struct Struct "super" pub struct Super "trait" pub struct Trait "try" pub struct Try "type" pub struct Type "typeof" pub struct Typeof "union" pub struct Union "unsafe" pub struct Unsafe "unsized" pub struct Unsized "use" pub struct Use "virtual" pub struct Virtual "where" pub struct Where "while" pub struct While "yield" pub struct Yield }
647
648define_punctuation! {
649 "+" pub struct Add/1 "+=" pub struct AddEq/2 "&" pub struct And/1 "&&" pub struct AndAnd/2 "&=" pub struct AndEq/2 "@" pub struct At/1 "!" pub struct Bang/1 "^" pub struct Caret/1 "^=" pub struct CaretEq/2 ":" pub struct Colon/1 "::" pub struct Colon2/2 "," pub struct Comma/1 "/" pub struct Div/1 "/=" pub struct DivEq/2 "$" pub struct Dollar/1 "." pub struct Dot/1 ".." pub struct Dot2/2 "..." pub struct Dot3/3 "..=" pub struct DotDotEq/3 "=" pub struct Eq/1 "==" pub struct EqEq/2 ">=" pub struct Ge/2 ">" pub struct Gt/1 "<=" pub struct Le/2 "<" pub struct Lt/1 "*=" pub struct MulEq/2 "!=" pub struct Ne/2 "|" pub struct Or/1 "|=" pub struct OrEq/2 "||" pub struct OrOr/2 "#" pub struct Pound/1 "?" pub struct Question/1 "->" pub struct RArrow/2 "<-" pub struct LArrow/2 "%" pub struct Rem/1 "%=" pub struct RemEq/2 "=>" pub struct FatArrow/2 ";" pub struct Semi/1 "<<" pub struct Shl/2 "<<=" pub struct ShlEq/3 ">>" pub struct Shr/2 ">>=" pub struct ShrEq/3 "*" pub struct Star/1 "-" pub struct Sub/1 "-=" pub struct SubEq/2 "~" pub struct Tilde/1 }
696
697define_delimiters! {
698 "{" pub struct Brace "[" pub struct Bracket "(" pub struct Paren " " pub struct Group }
703
704macro_rules! export_token_macro {
705 ($($await_rule:tt)*) => {
706 #[macro_export]
715 macro_rules! Token {
716 (abstract) => { $crate::token::Abstract };
717 (as) => { $crate::token::As };
718 (async) => { $crate::token::Async };
719 (auto) => { $crate::token::Auto };
720 $($await_rule => { $crate::token::Await };)*
721 (become) => { $crate::token::Become };
722 (box) => { $crate::token::Box };
723 (break) => { $crate::token::Break };
724 (const) => { $crate::token::Const };
725 (continue) => { $crate::token::Continue };
726 (crate) => { $crate::token::Crate };
727 (default) => { $crate::token::Default };
728 (do) => { $crate::token::Do };
729 (dyn) => { $crate::token::Dyn };
730 (else) => { $crate::token::Else };
731 (enum) => { $crate::token::Enum };
732 (extern) => { $crate::token::Extern };
733 (final) => { $crate::token::Final };
734 (fn) => { $crate::token::Fn };
735 (for) => { $crate::token::For };
736 (if) => { $crate::token::If };
737 (impl) => { $crate::token::Impl };
738 (in) => { $crate::token::In };
739 (let) => { $crate::token::Let };
740 (loop) => { $crate::token::Loop };
741 (macro) => { $crate::token::Macro };
742 (match) => { $crate::token::Match };
743 (mod) => { $crate::token::Mod };
744 (move) => { $crate::token::Move };
745 (mut) => { $crate::token::Mut };
746 (override) => { $crate::token::Override };
747 (priv) => { $crate::token::Priv };
748 (pub) => { $crate::token::Pub };
749 (ref) => { $crate::token::Ref };
750 (return) => { $crate::token::Return };
751 (Self) => { $crate::token::SelfType };
752 (self) => { $crate::token::SelfValue };
753 (static) => { $crate::token::Static };
754 (struct) => { $crate::token::Struct };
755 (super) => { $crate::token::Super };
756 (trait) => { $crate::token::Trait };
757 (try) => { $crate::token::Try };
758 (type) => { $crate::token::Type };
759 (typeof) => { $crate::token::Typeof };
760 (union) => { $crate::token::Union };
761 (unsafe) => { $crate::token::Unsafe };
762 (unsized) => { $crate::token::Unsized };
763 (use) => { $crate::token::Use };
764 (virtual) => { $crate::token::Virtual };
765 (where) => { $crate::token::Where };
766 (while) => { $crate::token::While };
767 (yield) => { $crate::token::Yield };
768 (+) => { $crate::token::Add };
769 (+=) => { $crate::token::AddEq };
770 (&) => { $crate::token::And };
771 (&&) => { $crate::token::AndAnd };
772 (&=) => { $crate::token::AndEq };
773 (@) => { $crate::token::At };
774 (!) => { $crate::token::Bang };
775 (^) => { $crate::token::Caret };
776 (^=) => { $crate::token::CaretEq };
777 (:) => { $crate::token::Colon };
778 (::) => { $crate::token::Colon2 };
779 (,) => { $crate::token::Comma };
780 (/) => { $crate::token::Div };
781 (/=) => { $crate::token::DivEq };
782 ($) => { $crate::token::Dollar };
783 (.) => { $crate::token::Dot };
784 (..) => { $crate::token::Dot2 };
785 (...) => { $crate::token::Dot3 };
786 (..=) => { $crate::token::DotDotEq };
787 (=) => { $crate::token::Eq };
788 (==) => { $crate::token::EqEq };
789 (>=) => { $crate::token::Ge };
790 (>) => { $crate::token::Gt };
791 (<=) => { $crate::token::Le };
792 (<) => { $crate::token::Lt };
793 (*=) => { $crate::token::MulEq };
794 (!=) => { $crate::token::Ne };
795 (|) => { $crate::token::Or };
796 (|=) => { $crate::token::OrEq };
797 (||) => { $crate::token::OrOr };
798 (#) => { $crate::token::Pound };
799 (?) => { $crate::token::Question };
800 (->) => { $crate::token::RArrow };
801 (<-) => { $crate::token::LArrow };
802 (%) => { $crate::token::Rem };
803 (%=) => { $crate::token::RemEq };
804 (=>) => { $crate::token::FatArrow };
805 (;) => { $crate::token::Semi };
806 (<<) => { $crate::token::Shl };
807 (<<=) => { $crate::token::ShlEq };
808 (>>) => { $crate::token::Shr };
809 (>>=) => { $crate::token::ShrEq };
810 (*) => { $crate::token::Star };
811 (-) => { $crate::token::Sub };
812 (-=) => { $crate::token::SubEq };
813 (~) => { $crate::token::Tilde };
814 (_) => { $crate::token::Underscore };
815 }
816 };
817}
818
819#[cfg(not(syn_omit_await_from_token_macro))]
823include!("await.rs"); #[cfg(syn_omit_await_from_token_macro)]
825export_token_macro![];
826
827#[doc(hidden)]
829#[cfg(feature = "parsing")]
830pub mod parsing {
831 use proc_macro2::{Spacing, Span};
832
833 use crate::buffer::Cursor;
834 use crate::error::{Error, Result};
835 use crate::parse::ParseStream;
836 use crate::span::FromSpans;
837
838 pub fn keyword(input: ParseStream, token: &str) -> Result<Span> {
839 input.step(|cursor| {
840 if let Some((ident, rest)) = cursor.ident() {
841 if ident == token {
842 return Ok((ident.span(), rest));
843 }
844 }
845 Err(cursor.error(format!("expected `{}`", token)))
846 })
847 }
848
849 pub fn peek_keyword(cursor: Cursor, token: &str) -> bool {
850 if let Some((ident, _rest)) = cursor.ident() {
851 ident == token
852 } else {
853 false
854 }
855 }
856
857 pub fn punct<S: FromSpans>(input: ParseStream, token: &str) -> Result<S> {
858 let mut spans = [input.span(); 3];
859 punct_helper(input, token, &mut spans)?;
860 Ok(S::from_spans(&spans))
861 }
862
863 fn punct_helper(input: ParseStream, token: &str, spans: &mut [Span; 3]) -> Result<()> {
864 input.step(|cursor| {
865 let mut cursor = *cursor;
866 assert!(token.len() <= spans.len());
867
868 for (i, ch) in token.chars().enumerate() {
869 match cursor.punct() {
870 Some((punct, rest)) => {
871 spans[i] = punct.span();
872 if punct.as_char() != ch {
873 break;
874 } else if i == token.len() - 1 {
875 return Ok(((), rest));
876 } else if punct.spacing() != Spacing::Joint {
877 break;
878 }
879 cursor = rest;
880 }
881 None => break,
882 }
883 }
884
885 Err(Error::new(spans[0], format!("expected `{}`", token)))
886 })
887 }
888
889 pub fn peek_punct(mut cursor: Cursor, token: &str) -> bool {
890 for (i, ch) in token.chars().enumerate() {
891 match cursor.punct() {
892 Some((punct, rest)) => {
893 if punct.as_char() != ch {
894 break;
895 } else if i == token.len() - 1 {
896 return true;
897 } else if punct.spacing() != Spacing::Joint {
898 break;
899 }
900 cursor = rest;
901 }
902 None => break,
903 }
904 }
905 false
906 }
907}
908
909#[doc(hidden)]
911#[cfg(feature = "printing")]
912pub mod printing {
913 use proc_macro2::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream};
914 use quote::TokenStreamExt;
915
916 pub fn punct(s: &str, spans: &[Span], tokens: &mut TokenStream) {
917 assert_eq!(s.len(), spans.len());
918
919 let mut chars = s.chars();
920 let mut spans = spans.iter();
921 let ch = chars.next_back().unwrap();
922 let span = spans.next_back().unwrap();
923 for (ch, span) in chars.zip(spans) {
924 let mut op = Punct::new(ch, Spacing::Joint);
925 op.set_span(*span);
926 tokens.append(op);
927 }
928
929 let mut op = Punct::new(ch, Spacing::Alone);
930 op.set_span(*span);
931 tokens.append(op);
932 }
933
934 pub fn keyword(s: &str, span: Span, tokens: &mut TokenStream) {
935 tokens.append(Ident::new(s, span));
936 }
937
938 pub fn delim<F>(s: &str, span: Span, tokens: &mut TokenStream, f: F)
939 where
940 F: FnOnce(&mut TokenStream),
941 {
942 let delim = match s {
943 "(" => Delimiter::Parenthesis,
944 "[" => Delimiter::Bracket,
945 "{" => Delimiter::Brace,
946 " " => Delimiter::None,
947 _ => panic!("unknown delimiter: {}", s),
948 };
949 let mut inner = TokenStream::new();
950 f(&mut inner);
951 let mut g = Group::new(delim, inner);
952 g.set_span(span);
953 tokens.append(g);
954 }
955}