blob: d26f504d2c13f18499304abdcff56583afd93263 [file] [log] [blame]
David Tolnay18c754c2018-08-21 23:26:58 -04001//! Tokens representing Rust punctuation, keywords, and delimiters.
2
David Tolnay2b687b02018-08-24 13:36:36 -04003use std::ops::{Deref, DerefMut};
David Tolnay18c754c2018-08-21 23:26:58 -04004
David Tolnay2b687b02018-08-24 13:36:36 -04005use proc_macro2::{Spacing, Span};
6
David Tolnay544a90f2018-08-24 20:34:03 -04007use super::error::Error;
8use super::lookahead;
9use super::parse::{Lookahead1, Parse, ParseStream, Result};
10use super::span::{FromSpans, IntoSpans};
David Tolnay18c754c2018-08-21 23:26:58 -040011
12/// Marker trait for types that represent single tokens.
13///
14/// This trait is sealed and cannot be implemented for types outside of Syn.
15pub trait Token: private::Sealed {
16 // Not public API.
17 #[doc(hidden)]
18 fn peek(lookahead: &Lookahead1) -> bool;
19
20 // Not public API.
21 #[doc(hidden)]
22 fn display() -> String;
23}
24
25mod private {
26 pub trait Sealed {}
27}
28
29/// A type-macro that expands to the name of the Rust type representation of a
30/// given token.
31#[macro_export]
32#[cfg_attr(rustfmt, rustfmt_skip)]
33macro_rules! Token {
David Tolnay544a90f2018-08-24 20:34:03 -040034 (struct) => { $crate::next::token::Struct };
35 (enum) => { $crate::next::token::Enum };
36 (:) => { $crate::next::token::Colon };
37 (,) => { $crate::next::token::Comma };
38 (..) => { $crate::next::token::Dot2 };
David Tolnay18c754c2018-08-21 23:26:58 -040039}
40
David Tolnay2b687b02018-08-24 13:36:36 -040041macro_rules! impl_token {
David Tolnay18c754c2018-08-21 23:26:58 -040042 ($token:tt $name:ident #[$doc:meta]) => {
David Tolnay18c754c2018-08-21 23:26:58 -040043 impl Token for $name {
44 fn peek(lookahead: &Lookahead1) -> bool {
David Tolnay2b687b02018-08-24 13:36:36 -040045 lookahead::is_token(lookahead, $token)
David Tolnay18c754c2018-08-21 23:26:58 -040046 }
47
48 fn display() -> String {
49 concat!("`", $token, "`").to_owned()
50 }
51 }
52
53 impl private::Sealed for $name {}
54 };
55}
56
57macro_rules! define_keywords {
David Tolnay2b687b02018-08-24 13:36:36 -040058 ($($token:tt pub struct $name:ident #[$doc:meta])*) => {
David Tolnay18c754c2018-08-21 23:26:58 -040059 $(
David Tolnay2b687b02018-08-24 13:36:36 -040060 #[$doc]
61 #[derive(Debug)]
62 pub struct $name {
63 pub span: Span,
64 }
65
66 #[doc(hidden)]
67 #[allow(non_snake_case)]
68 pub fn $name<T: IntoSpans<[Span; 1]>>(span: T) -> $name {
69 $name {
70 span: span.into_spans()[0],
71 }
72 }
73
74 impl_token!($token $name #[$doc]);
David Tolnay18c754c2018-08-21 23:26:58 -040075
76 impl Parse for $name {
77 fn parse(input: ParseStream) -> Result<Self> {
78 parse_keyword(input, $token).map($name)
79 }
80 }
81 )*
82 };
83}
84
85macro_rules! define_punctuation {
David Tolnay2b687b02018-08-24 13:36:36 -040086 ($($token:tt pub struct $name:ident/$len:tt #[$doc:meta])*) => {
David Tolnay18c754c2018-08-21 23:26:58 -040087 $(
David Tolnay2b687b02018-08-24 13:36:36 -040088 #[$doc]
89 #[derive(Debug)]
90 pub struct $name {
91 pub spans: [Span; $len],
92 }
93
94 impl Deref for $name {
95 type Target = [Span; $len];
96
97 fn deref(&self) -> &Self::Target {
98 &self.spans
99 }
100 }
101
102 impl DerefMut for $name {
103 fn deref_mut(&mut self) -> &mut Self::Target {
104 &mut self.spans
105 }
106 }
107
108 #[doc(hidden)]
109 #[allow(non_snake_case)]
110 pub fn $name<T: IntoSpans<[Span; $len]>>(spans: T) -> $name {
111 $name {
112 spans: spans.into_spans(),
113 }
114 }
115
116 impl_token!($token $name #[$doc]);
David Tolnay18c754c2018-08-21 23:26:58 -0400117
118 impl Parse for $name {
119 fn parse(input: ParseStream) -> Result<Self> {
David Tolnay2b687b02018-08-24 13:36:36 -0400120 parse_punctuation(input, $token).map($name::<[Span; $len]>)
David Tolnay18c754c2018-08-21 23:26:58 -0400121 }
122 }
123 )*
124 };
125}
126
127define_keywords! {
David Tolnay2b687b02018-08-24 13:36:36 -0400128 "struct" pub struct Struct /// `struct`
129 "enum" pub struct Enum /// `enum`
David Tolnay18c754c2018-08-21 23:26:58 -0400130}
131
132define_punctuation! {
David Tolnay2b687b02018-08-24 13:36:36 -0400133 ":" pub struct Colon/1 /// `:`
134 "," pub struct Comma/1 /// `,`
135 ".." pub struct Dot2/2 /// `..`
David Tolnay18c754c2018-08-21 23:26:58 -0400136}
137
138/// `{...}`
139#[derive(Debug)]
140pub struct Brace(pub Span);
141
142fn parse_keyword(input: ParseStream, token: &str) -> Result<Span> {
143 input.step_cursor(|cursor| {
144 if let Some((ident, rest)) = cursor.ident() {
145 if ident == token {
146 return Ok((ident.span(), rest));
147 }
148 }
149 Err(cursor.error(format!("expected `{}`", token)))
150 })
151}
152
David Tolnay2b687b02018-08-24 13:36:36 -0400153fn parse_punctuation<S: FromSpans>(input: ParseStream, token: &str) -> Result<S> {
David Tolnay18c754c2018-08-21 23:26:58 -0400154 input.step_cursor(|cursor| {
David Tolnay2b687b02018-08-24 13:36:36 -0400155 let mut cursor = *cursor;
156 let mut spans = [cursor.span(); 3];
157 assert!(token.len() <= spans.len());
158
159 for (i, ch) in token.chars().enumerate() {
160 match cursor.punct() {
161 Some((punct, rest)) => {
162 spans[i] = punct.span();
163 if punct.as_char() != ch {
164 break;
165 } else if i == token.len() - 1 {
166 return Ok((S::from_spans(&spans), rest));
167 } else if punct.spacing() != Spacing::Joint {
168 break;
169 }
170 cursor = rest;
171 }
172 None => break,
David Tolnay18c754c2018-08-21 23:26:58 -0400173 }
174 }
David Tolnay2b687b02018-08-24 13:36:36 -0400175
176 Err(Error::new(spans[0], format!("expected `{}`", token)))
David Tolnay18c754c2018-08-21 23:26:58 -0400177 })
178}