blob: 5b80a14d2ae6f75b9851374d230f483601e5b7dc [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
David Tolnay2b687b02018-08-24 13:36:36 -040029macro_rules! impl_token {
David Tolnay18c754c2018-08-21 23:26:58 -040030 ($token:tt $name:ident #[$doc:meta]) => {
David Tolnay18c754c2018-08-21 23:26:58 -040031 impl Token for $name {
32 fn peek(lookahead: &Lookahead1) -> bool {
David Tolnay2b687b02018-08-24 13:36:36 -040033 lookahead::is_token(lookahead, $token)
David Tolnay18c754c2018-08-21 23:26:58 -040034 }
35
36 fn display() -> String {
37 concat!("`", $token, "`").to_owned()
38 }
39 }
40
41 impl private::Sealed for $name {}
42 };
43}
44
45macro_rules! define_keywords {
David Tolnay2b687b02018-08-24 13:36:36 -040046 ($($token:tt pub struct $name:ident #[$doc:meta])*) => {
David Tolnay18c754c2018-08-21 23:26:58 -040047 $(
David Tolnay2b687b02018-08-24 13:36:36 -040048 #[$doc]
49 #[derive(Debug)]
50 pub struct $name {
51 pub span: Span,
52 }
53
54 #[doc(hidden)]
55 #[allow(non_snake_case)]
56 pub fn $name<T: IntoSpans<[Span; 1]>>(span: T) -> $name {
57 $name {
58 span: span.into_spans()[0],
59 }
60 }
61
62 impl_token!($token $name #[$doc]);
David Tolnay18c754c2018-08-21 23:26:58 -040063
64 impl Parse for $name {
65 fn parse(input: ParseStream) -> Result<Self> {
66 parse_keyword(input, $token).map($name)
67 }
68 }
69 )*
70 };
71}
72
73macro_rules! define_punctuation {
David Tolnay2b687b02018-08-24 13:36:36 -040074 ($($token:tt pub struct $name:ident/$len:tt #[$doc:meta])*) => {
David Tolnay18c754c2018-08-21 23:26:58 -040075 $(
David Tolnay2b687b02018-08-24 13:36:36 -040076 #[$doc]
77 #[derive(Debug)]
78 pub struct $name {
79 pub spans: [Span; $len],
80 }
81
82 impl Deref for $name {
83 type Target = [Span; $len];
84
85 fn deref(&self) -> &Self::Target {
86 &self.spans
87 }
88 }
89
90 impl DerefMut for $name {
91 fn deref_mut(&mut self) -> &mut Self::Target {
92 &mut self.spans
93 }
94 }
95
96 #[doc(hidden)]
97 #[allow(non_snake_case)]
98 pub fn $name<T: IntoSpans<[Span; $len]>>(spans: T) -> $name {
99 $name {
100 spans: spans.into_spans(),
101 }
102 }
103
104 impl_token!($token $name #[$doc]);
David Tolnay18c754c2018-08-21 23:26:58 -0400105
106 impl Parse for $name {
107 fn parse(input: ParseStream) -> Result<Self> {
David Tolnay2b687b02018-08-24 13:36:36 -0400108 parse_punctuation(input, $token).map($name::<[Span; $len]>)
David Tolnay18c754c2018-08-21 23:26:58 -0400109 }
110 }
111 )*
112 };
113}
114
115define_keywords! {
David Tolnay2b687b02018-08-24 13:36:36 -0400116 "struct" pub struct Struct /// `struct`
117 "enum" pub struct Enum /// `enum`
David Tolnay18c754c2018-08-21 23:26:58 -0400118}
119
120define_punctuation! {
David Tolnay2b687b02018-08-24 13:36:36 -0400121 ":" pub struct Colon/1 /// `:`
122 "," pub struct Comma/1 /// `,`
123 ".." pub struct Dot2/2 /// `..`
David Tolnay18c754c2018-08-21 23:26:58 -0400124}
125
126/// `{...}`
127#[derive(Debug)]
128pub struct Brace(pub Span);
129
130fn parse_keyword(input: ParseStream, token: &str) -> Result<Span> {
131 input.step_cursor(|cursor| {
132 if let Some((ident, rest)) = cursor.ident() {
133 if ident == token {
134 return Ok((ident.span(), rest));
135 }
136 }
137 Err(cursor.error(format!("expected `{}`", token)))
138 })
139}
140
David Tolnay2b687b02018-08-24 13:36:36 -0400141fn parse_punctuation<S: FromSpans>(input: ParseStream, token: &str) -> Result<S> {
David Tolnay18c754c2018-08-21 23:26:58 -0400142 input.step_cursor(|cursor| {
David Tolnay2b687b02018-08-24 13:36:36 -0400143 let mut cursor = *cursor;
144 let mut spans = [cursor.span(); 3];
145 assert!(token.len() <= spans.len());
146
147 for (i, ch) in token.chars().enumerate() {
148 match cursor.punct() {
149 Some((punct, rest)) => {
150 spans[i] = punct.span();
151 if punct.as_char() != ch {
152 break;
153 } else if i == token.len() - 1 {
154 return Ok((S::from_spans(&spans), rest));
155 } else if punct.spacing() != Spacing::Joint {
156 break;
157 }
158 cursor = rest;
159 }
160 None => break,
David Tolnay18c754c2018-08-21 23:26:58 -0400161 }
162 }
David Tolnay2b687b02018-08-24 13:36:36 -0400163
164 Err(Error::new(spans[0], format!("expected `{}`", token)))
David Tolnay18c754c2018-08-21 23:26:58 -0400165 })
166}