David Tolnay | 18c754c | 2018-08-21 23:26:58 -0400 | [diff] [blame^] | 1 | //! Tokens representing Rust punctuation, keywords, and delimiters. |
| 2 | |
| 3 | use proc_macro2::Span; |
| 4 | |
| 5 | use 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. |
| 10 | pub 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 | |
| 20 | mod 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)] |
| 28 | macro_rules! Token { |
| 29 | (struct) => { $crate::token::Struct }; |
| 30 | (enum) => { $crate::token::Enum }; |
| 31 | (:) => { $crate::token::Colon }; |
| 32 | (,) => { $crate::token::Comma }; |
| 33 | } |
| 34 | |
| 35 | macro_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 | |
| 55 | macro_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 | |
| 69 | macro_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 | |
| 83 | define_keywords! { |
| 84 | "struct" Struct /// `struct` |
| 85 | "enum" Enum /// `enum` |
| 86 | } |
| 87 | |
| 88 | define_punctuation! { |
| 89 | ":" Colon /// `:` |
| 90 | "," Comma /// `,` |
| 91 | } |
| 92 | |
| 93 | /// `{...}` |
| 94 | #[derive(Debug)] |
| 95 | pub struct Brace(pub Span); |
| 96 | |
| 97 | fn 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 | |
| 108 | fn 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 | } |