1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
//! Tokens representing Rust punctuation, keywords, and delimiters.

use proc_macro2::Span;

use parse::{Lookahead1, Parse, ParseStream, Result};

/// Marker trait for types that represent single tokens.
///
/// This trait is sealed and cannot be implemented for types outside of Syn.
pub trait Token: private::Sealed {
    // Not public API.
    #[doc(hidden)]
    fn peek(lookahead: &Lookahead1) -> bool;

    // Not public API.
    #[doc(hidden)]
    fn display() -> String;
}

mod private {
    pub trait Sealed {}
}

/// A type-macro that expands to the name of the Rust type representation of a
/// given token.
#[macro_export]
#[cfg_attr(rustfmt, rustfmt_skip)]
macro_rules! Token {
    (struct) => { $crate::token::Struct };
    (enum)   => { $crate::token::Enum };
    (:)      => { $crate::token::Colon };
    (,)      => { $crate::token::Comma };
}

macro_rules! define_token {
    ($token:tt $name:ident #[$doc:meta]) => {
        #[$doc]
        #[derive(Debug)]
        pub struct $name(pub Span);

        impl Token for $name {
            fn peek(lookahead: &Lookahead1) -> bool {
                ::lookahead::is_token(lookahead, $token)
            }

            fn display() -> String {
                concat!("`", $token, "`").to_owned()
            }
        }

        impl private::Sealed for $name {}
    };
}

macro_rules! define_keywords {
    ($($token:tt $name:ident #[$doc:meta])*) => {
        $(
            define_token!($token $name #[$doc]);

            impl Parse for $name {
                fn parse(input: ParseStream) -> Result<Self> {
                    parse_keyword(input, $token).map($name)
                }
            }
        )*
    };
}

macro_rules! define_punctuation {
    ($($token:tt $name:ident #[$doc:meta])*) => {
        $(
            define_token!($token $name #[$doc]);

            impl Parse for $name {
                fn parse(input: ParseStream) -> Result<Self> {
                    parse_punctuation(input, $token).map($name)
                }
            }
        )*
    };
}

define_keywords! {
    "struct" Struct /// `struct`
    "enum"   Enum   /// `enum`
}

define_punctuation! {
    ":" Colon /// `:`
    "," Comma /// `,`
}

/// `{...}`
#[derive(Debug)]
pub struct Brace(pub Span);

fn parse_keyword(input: ParseStream, token: &str) -> Result<Span> {
    input.step_cursor(|cursor| {
        if let Some((ident, rest)) = cursor.ident() {
            if ident == token {
                return Ok((ident.span(), rest));
            }
        }
        Err(cursor.error(format!("expected `{}`", token)))
    })
}

fn parse_punctuation(input: ParseStream, token: &str) -> Result<Span> {
    input.step_cursor(|cursor| {
        // TODO: support multi-character punctuation
        if let Some((punct, rest)) = cursor.punct() {
            if punct.as_char() == token.chars().next().unwrap() {
                return Ok((punct.span(), rest));
            }
        }
        Err(cursor.error(format!("expected `{}`", token)))
    })
}