Skip to content

Commit

Permalink
Prototype for support of custom keywords
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
kureuil committed Sep 4, 2018
1 parent 719ad5a commit c0beaf3
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 2 deletions.
81 changes: 81 additions & 0 deletions src/keyword.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
use buffer::Cursor;
use token::Token;

pub trait Keyword {
fn ident() -> &'static str;

fn display() -> &'static str;
}

impl<K: Keyword> 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 {}
}
}
}
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,9 @@ pub mod export;
#[cfg(feature = "parsing")]
mod lookahead;

#[cfg(feature = "parsing")]
mod keyword;

#[cfg(feature = "parsing")]
pub mod parse;

Expand Down
3 changes: 2 additions & 1 deletion src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
5 changes: 4 additions & 1 deletion src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -143,6 +143,9 @@ mod private {
#[cfg(feature = "parsing")]
impl private::Sealed for Ident {}

#[cfg(feature = "parsing")]
impl<K: Keyword> private::Sealed for K {}

#[cfg(feature = "parsing")]
fn peek_impl(cursor: Cursor, peek: fn(ParseStream) -> bool) -> bool {
let scope = Span::call_site();
Expand Down

0 comments on commit c0beaf3

Please sign in to comment.