blob: 31002d5031a3aff182bf24b21861409fbc1cd0f4 [file] [log] [blame]
David Tolnay18c754c2018-08-21 23:26:58 -04001//! Tokens representing Rust punctuation, keywords, and delimiters.
2
3use proc_macro2::Span;
4
5use parse::{Lookahead1, Parse, ParseStream, Result};
6
7/// Marker trait for types that represent single tokens.
8///
9/// This trait is sealed and cannot be implemented for types outside of Syn.
10pub trait Token: private::Sealed {
11 // Not public API.
12 #[doc(hidden)]
13 fn peek(lookahead: &Lookahead1) -> bool;
14
15 // Not public API.
16 #[doc(hidden)]
17 fn display() -> String;
18}
19
20mod private {
21 pub trait Sealed {}
22}
23
24/// A type-macro that expands to the name of the Rust type representation of a
25/// given token.
26#[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 /// `struct`
85 "enum" Enum /// `enum`
86}
87
88define_punctuation! {
89 ":" Colon /// `:`
90 "," Comma /// `,`
91}
92
93/// `{...}`
94#[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 // TODO: support multi-character punctuation
111 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}