From c0beaf31dcbce4693e939bba5770cad41ece8839 Mon Sep 17 00:00:00 2001 From: Louis Kureuil Person Date: Wed, 5 Sep 2018 00:12:43 +0200 Subject: [PATCH] Prototype for support of custom keywords First prototype for support of custom keywords with syn's new parsing API. Documentation and tests aren't present for now as I'm mainly reaching for feedback. This patch introduces a new Keyword trait, a new macro custom_keyword! and exposes the existing TokenMarker enum. The Keyword trait automatically implements the Token trait, making it possible to peek on custom keywords (this is why I had to make TokenMarker public). The custom macro generates a structure storing an Ident and implementing the Keyword and Parse traits. A function with the same name as the structure is also generated in order to use it like any predefined keyword. --- src/keyword.rs | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 ++ src/parse.rs | 3 +- src/token.rs | 5 +++- 4 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 src/keyword.rs diff --git a/src/keyword.rs b/src/keyword.rs new file mode 100644 index 0000000000..9ecf07b495 --- /dev/null +++ b/src/keyword.rs @@ -0,0 +1,81 @@ +use buffer::Cursor; +use token::Token; + +pub trait Keyword { + fn ident() -> &'static str; + + fn display() -> &'static str; +} + +impl Token for K { + fn peek(cursor: Cursor) -> bool { + if let Some((ident, _rest)) = cursor.ident() { + ident == K::ident() + } else { + false + } + } + + fn display() -> &'static str { + K::display() + } +} + +#[macro_export] +macro_rules! custom_keyword { + ($ident:ident) => { + custom_keyword_internal!({ pub(in self) } $ident); + }; + (pub $ident:ident) => { + custom_keyword_internal!({ pub } $ident); + }; + (pub(crate) $ident:ident) => { + custom_keyword_internal!({ pub(crate) } $ident); + }; + (pub(super) $ident:ident) => { + custom_keyword_internal!({ pub(super) } $ident); + }; + (pub(self) $ident:ident) => { + custom_keyword_internal!({ pub(self) } $ident); + }; + (pub(in $path:path) $ident:ident) => { + custom_keyword_internal!({ pub(in $path) } $ident); + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! custom_keyword_internal { + ({ $($vis:tt)* } $ident:ident) => { + $($vis)* struct $ident { + inner: $crate::Ident + } + + impl $crate::parse::Keyword for $ident { + fn ident() -> &'static str { + stringify!($ident) + } + + fn display() -> &'static str { + concat!("`", stringify!($ident), "`") + } + } + + impl $crate::parse::Parse for $ident { + fn parse(input: $crate::parse::ParseStream) -> $crate::parse::Result<$ident> { + input.step(|cursor| { + if let Some((ident, rest)) = cursor.ident() { + if ident == stringify!($ident) { + return Ok(($ident { inner: ident }, rest)); + } + } + Err(cursor.error(concat!("expected `", stringify!($ident), "`"))) + }) + } + } + + $($vis)* fn $ident(marker: $crate::parse::TokenMarker) -> $ident { + match marker {} + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 33432561f6..ccf70b7b45 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -570,6 +570,9 @@ pub mod export; #[cfg(feature = "parsing")] mod lookahead; +#[cfg(feature = "parsing")] +mod keyword; + #[cfg(feature = "parsing")] pub mod parse; diff --git a/src/parse.rs b/src/parse.rs index 0445cefb28..4dd735deed 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -214,7 +214,8 @@ use punctuated::Punctuated; use token::Token; pub use error::{Error, Result}; -pub use lookahead::{Lookahead1, Peek}; +pub use lookahead::{Lookahead1, Peek, TokenMarker}; +pub use keyword::Keyword; /// Parsing interface implemented by all types that can be parsed in a default /// way from a token stream. diff --git a/src/token.rs b/src/token.rs index 4a44067dce..637d9015a0 100644 --- a/src/token.rs +++ b/src/token.rs @@ -118,7 +118,7 @@ use lit::{Lit, LitBool, LitByte, LitByteStr, LitChar, LitFloat, LitInt, LitStr}; #[cfg(feature = "parsing")] use lookahead; #[cfg(feature = "parsing")] -use parse::{Parse, ParseStream}; +use parse::{Keyword, Parse, ParseStream}; use span::IntoSpans; /// Marker trait for types that represent single tokens. @@ -143,6 +143,9 @@ mod private { #[cfg(feature = "parsing")] impl private::Sealed for Ident {} +#[cfg(feature = "parsing")] +impl private::Sealed for K {} + #[cfg(feature = "parsing")] fn peek_impl(cursor: Cursor, peek: fn(ParseStream) -> bool) -> bool { let scope = Span::call_site();