-
-
Notifications
You must be signed in to change notification settings - Fork 303
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #481 from dtolnay/custom
Custom keywords
- Loading branch information
Showing
4 changed files
with
294 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,29 @@ | ||
pub use std::clone::Clone; | ||
pub use std::cmp::{Eq, PartialEq}; | ||
pub use std::convert::From; | ||
pub use std::default::Default; | ||
pub use std::fmt::{self, Debug, Formatter}; | ||
pub use std::hash::{Hash, Hasher}; | ||
pub use std::marker::Copy; | ||
pub use std::option::Option::{None, Some}; | ||
pub use std::result::Result::{Err, Ok}; | ||
|
||
#[cfg(feature = "parsing")] | ||
pub use std::convert::From; | ||
pub use proc_macro2::{Span, TokenStream as TokenStream2}; | ||
|
||
pub use span::IntoSpans; | ||
|
||
#[cfg(feature = "proc-macro")] | ||
pub use proc_macro::TokenStream; | ||
|
||
#[cfg(feature = "printing")] | ||
pub use quote::{ToTokens, TokenStreamExt}; | ||
|
||
#[allow(non_camel_case_types)] | ||
pub type bool = help::Bool; | ||
#[allow(non_camel_case_types)] | ||
pub type str = help::Str; | ||
|
||
mod help { | ||
pub type Bool = bool; | ||
pub type Str = str; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,246 @@ | ||
/// Define a type that supports parsing and printing a given identifier as if it | ||
/// were a keyword. | ||
/// | ||
/// # Usage | ||
/// | ||
/// As a convention, it is recommended that this macro be invoked within a | ||
/// module called `kw` and that the resulting parser be invoked with a `kw::` | ||
/// prefix. | ||
/// | ||
/// ``` | ||
/// # extern crate syn; | ||
/// # | ||
/// mod kw { | ||
/// # use syn; | ||
/// syn::custom_keyword!(whatever); | ||
/// } | ||
/// ``` | ||
/// | ||
/// The generated syntax tree node supports the following operations just like | ||
/// any built-in keyword token. | ||
/// | ||
/// - [Peeking] — `input.peek(kw::whatever)` | ||
/// | ||
/// - [Parsing] — `input.parse::<kw::whatever>()?` | ||
/// | ||
/// - [Printing] — `quote!( ... #whatever_token ... )` | ||
/// | ||
/// - Construction from a [`Span`] — `let whatever_token = kw::whatever(sp)` | ||
/// | ||
/// - Field access to its span — `let sp = whatever_token.span` | ||
/// | ||
/// [Peeking]: parse/struct.ParseBuffer.html#method.peek | ||
/// [Parsing]: parse/struct.ParseBuffer.html#method.parse | ||
/// [Printing]: https://docs.rs/quote/0.6/quote/trait.ToTokens.html | ||
/// [`Span`]: struct.Span.html | ||
/// | ||
/// # Example | ||
/// | ||
/// This example parses input that looks like `bool = true` or `str = "value"`. | ||
/// The key must be either the identifier `bool` or the identifier `str`. If | ||
/// `bool`, the value may be either `true` or `false`. If `str`, the value may | ||
/// be any string literal. | ||
/// | ||
/// The symbols `bool` and `str` are not reserved keywords in Rust so these are | ||
/// not considered keywords in the `syn::token` module. Like any other | ||
/// identifier that is not a keyword, these can be declared as custom keywords | ||
/// by crates that need to use them as such. | ||
/// | ||
/// ``` | ||
/// # extern crate syn; | ||
/// # | ||
/// use syn::{LitBool, LitStr, Token}; | ||
/// use syn::parse::{Parse, ParseStream, Result}; | ||
/// | ||
/// mod kw { | ||
/// # use syn; | ||
/// syn::custom_keyword!(bool); | ||
/// syn::custom_keyword!(str); | ||
/// } | ||
/// | ||
/// enum Argument { | ||
/// Bool { | ||
/// bool_token: kw::bool, | ||
/// eq_token: Token![=], | ||
/// value: LitBool, | ||
/// }, | ||
/// Str { | ||
/// str_token: kw::str, | ||
/// eq_token: Token![=], | ||
/// value: LitStr, | ||
/// }, | ||
/// } | ||
/// | ||
/// impl Parse for Argument { | ||
/// fn parse(input: ParseStream) -> Result<Self> { | ||
/// let lookahead = input.lookahead1(); | ||
/// if lookahead.peek(kw::bool) { | ||
/// Ok(Argument::Bool { | ||
/// bool_token: input.parse::<kw::bool>()?, | ||
/// eq_token: input.parse()?, | ||
/// value: input.parse()?, | ||
/// }) | ||
/// } else if lookahead.peek(kw::str) { | ||
/// Ok(Argument::Str { | ||
/// str_token: input.parse::<kw::str>()?, | ||
/// eq_token: input.parse()?, | ||
/// value: input.parse()?, | ||
/// }) | ||
/// } else { | ||
/// Err(lookahead.error()) | ||
/// } | ||
/// } | ||
/// } | ||
/// # | ||
/// # fn main() {} | ||
/// ``` | ||
#[macro_export(local_inner_macros)] | ||
macro_rules! custom_keyword { | ||
($ident:ident) => { | ||
pub struct $ident { | ||
pub span: $crate::export::Span, | ||
} | ||
|
||
#[doc(hidden)] | ||
#[allow(non_snake_case)] | ||
pub fn $ident<__S: $crate::export::IntoSpans<[$crate::export::Span; 1]>>(span: __S) -> $ident { | ||
$ident { | ||
span: $crate::export::IntoSpans::into_spans(span)[0], | ||
} | ||
} | ||
|
||
impl $crate::export::Default for $ident { | ||
fn default() -> Self { | ||
$ident { | ||
span: $crate::export::Span::call_site(), | ||
} | ||
} | ||
} | ||
|
||
impl_parse_for_custom_keyword!($ident); | ||
impl_to_tokens_for_custom_keyword!($ident); | ||
impl_clone_for_custom_keyword!($ident); | ||
impl_extra_traits_for_custom_keyword!($ident); | ||
} | ||
} | ||
|
||
// Not public API. | ||
#[cfg(feature = "parsing")] | ||
#[doc(hidden)] | ||
#[macro_export] | ||
macro_rules! impl_parse_for_custom_keyword { | ||
($ident:ident) => { | ||
// For peek. | ||
impl $crate::token::CustomKeyword for $ident { | ||
fn ident() -> &'static $crate::export::str { | ||
stringify!($ident) | ||
} | ||
|
||
fn display() -> &'static $crate::export::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 $crate::export::Some((ident, rest)) = cursor.ident() { | ||
if ident == stringify!($ident) { | ||
return $crate::export::Ok(($ident { span: ident.span() }, rest)); | ||
} | ||
} | ||
$crate::export::Err(cursor.error(concat!("expected `", stringify!($ident), "`"))) | ||
}) | ||
} | ||
} | ||
}; | ||
} | ||
|
||
// Not public API. | ||
#[cfg(not(feature = "parsing"))] | ||
#[doc(hidden)] | ||
#[macro_export] | ||
macro_rules! impl_parse_for_custom_keyword { | ||
($ident:ident) => {}; | ||
} | ||
|
||
// Not public API. | ||
#[cfg(feature = "printing")] | ||
#[doc(hidden)] | ||
#[macro_export] | ||
macro_rules! impl_to_tokens_for_custom_keyword { | ||
($ident:ident) => { | ||
impl $crate::export::ToTokens for $ident { | ||
fn to_tokens(&self, tokens: &mut $crate::export::TokenStream2) { | ||
let ident = $crate::Ident::new(stringify!($ident), self.span); | ||
$crate::export::TokenStreamExt::append(tokens, ident); | ||
} | ||
} | ||
}; | ||
} | ||
|
||
// Not public API. | ||
#[cfg(not(feature = "printing"))] | ||
#[doc(hidden)] | ||
#[macro_export] | ||
macro_rules! impl_to_tokens_for_custom_keyword { | ||
($ident:ident) => {}; | ||
} | ||
|
||
// Not public API. | ||
#[cfg(feature = "clone-impls")] | ||
#[doc(hidden)] | ||
#[macro_export] | ||
macro_rules! impl_clone_for_custom_keyword { | ||
($ident:ident) => { | ||
impl $crate::export::Copy for $ident {} | ||
|
||
impl $crate::export::Clone for $ident { | ||
fn clone(&self) -> Self { | ||
*self | ||
} | ||
} | ||
}; | ||
} | ||
|
||
// Not public API. | ||
#[cfg(not(feature = "clone-impls"))] | ||
#[doc(hidden)] | ||
#[macro_export] | ||
macro_rules! impl_clone_for_custom_keyword { | ||
($ident:ident) => {}; | ||
} | ||
|
||
// Not public API. | ||
#[cfg(feature = "extra-traits")] | ||
#[doc(hidden)] | ||
#[macro_export] | ||
macro_rules! impl_extra_traits_for_custom_keyword { | ||
($ident:ident) => { | ||
impl $crate::export::Debug for $ident { | ||
fn fmt(&self, f: &mut $crate::export::Formatter) -> $crate::export::fmt::Result { | ||
$crate::export::Formatter::write_str(f, stringify!($ident)) | ||
} | ||
} | ||
|
||
impl $crate::export::Eq for $ident {} | ||
|
||
impl $crate::export::PartialEq for $ident { | ||
fn eq(&self, _other: &Self) -> $crate::export::bool { | ||
true | ||
} | ||
} | ||
|
||
impl $crate::export::Hash for $ident { | ||
fn hash<__H: $crate::export::Hasher>(&self, _state: &mut __H) {} | ||
} | ||
}; | ||
} | ||
|
||
// Not public API. | ||
#[cfg(not(feature = "extra-traits"))] | ||
#[doc(hidden)] | ||
#[macro_export] | ||
macro_rules! impl_extra_traits_for_custom_keyword { | ||
($ident:ident) => {}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters