syn_error_experiment/
token.rs1use proc_macro2::Span;
4
5use parse::{Lookahead1, Parse, ParseStream, Result};
6
7pub trait Token: private::Sealed {
11 #[doc(hidden)]
13 fn peek(lookahead: &Lookahead1) -> bool;
14
15 #[doc(hidden)]
17 fn display() -> String;
18}
19
20mod private {
21 pub trait Sealed {}
22}
23
24#[macro_export]
27#[cfg_attr(rustfmt, rustfmt_skip)]
28macro_rules! Token {
29 (struct) => { $crate::token::Struct };
30 (enum) => { $crate::token::Enum };
31 (:) => { $crate::token::Colon };
32 (,) => { $crate::token::Comma };
33}
34
35macro_rules! define_token {
36 ($token:tt $name:ident #[$doc:meta]) => {
37 #[$doc]
38 #[derive(Debug)]
39 pub struct $name(pub Span);
40
41 impl Token for $name {
42 fn peek(lookahead: &Lookahead1) -> bool {
43 ::lookahead::is_token(lookahead, $token)
44 }
45
46 fn display() -> String {
47 concat!("`", $token, "`").to_owned()
48 }
49 }
50
51 impl private::Sealed for $name {}
52 };
53}
54
55macro_rules! define_keywords {
56 ($($token:tt $name:ident #[$doc:meta])*) => {
57 $(
58 define_token!($token $name #[$doc]);
59
60 impl Parse for $name {
61 fn parse(input: ParseStream) -> Result<Self> {
62 parse_keyword(input, $token).map($name)
63 }
64 }
65 )*
66 };
67}
68
69macro_rules! define_punctuation {
70 ($($token:tt $name:ident #[$doc:meta])*) => {
71 $(
72 define_token!($token $name #[$doc]);
73
74 impl Parse for $name {
75 fn parse(input: ParseStream) -> Result<Self> {
76 parse_punctuation(input, $token).map($name)
77 }
78 }
79 )*
80 };
81}
82
83define_keywords! {
84 "struct" Struct "enum" Enum }
87
88define_punctuation! {
89 ":" Colon "," Comma }
92
93#[derive(Debug)]
95pub struct Brace(pub Span);
96
97fn parse_keyword(input: ParseStream, token: &str) -> Result<Span> {
98 input.step_cursor(|cursor| {
99 if let Some((ident, rest)) = cursor.ident() {
100 if ident == token {
101 return Ok((ident.span(), rest));
102 }
103 }
104 Err(cursor.error(format!("expected `{}`", token)))
105 })
106}
107
108fn parse_punctuation(input: ParseStream, token: &str) -> Result<Span> {
109 input.step_cursor(|cursor| {
110 if let Some((punct, rest)) = cursor.punct() {
112 if punct.as_char() == token.chars().next().unwrap() {
113 return Ok((punct.span(), rest));
114 }
115 }
116 Err(cursor.error(format!("expected `{}`", token)))
117 })
118}