306 lines · 9.0 KB · Rust
RawDownload
// SPDX-License-Identifier: Apache-2.0 OR MIT /// Define a type that supports parsing and printing a multi-character symbol/// as if it were a punctuation token.////// # Usage////// ```/// syn::custom_punctuation!(LeftRightArrow, <=>);/// ```////// The generated syntax tree node supports the following operations just like/// any built-in punctuation token.////// - [Peeking] — `input.peek(LeftRightArrow)`////// - [Parsing] — `input.parse::<LeftRightArrow>()?`////// - [Printing] — `quote!( ... #lrarrow ... )`////// - Construction from a [`Span`] — `let lrarrow = LeftRightArrow(sp)`////// - Construction from multiple [`Span`] — `let lrarrow = LeftRightArrow([sp, sp, sp])`////// - Field access to its spans — `let spans = lrarrow.spans`////// [Peeking]: crate::parse::ParseBuffer::peek/// [Parsing]: crate::parse::ParseBuffer::parse/// [Printing]: quote::ToTokens/// [`Span`]: proc_macro2::Span////// # Example////// ```/// use proc_macro2::{TokenStream, TokenTree};/// use syn::parse::{Parse, ParseStream, Peek, Result};/// use syn::punctuated::Punctuated;/// use syn::Expr;////// syn::custom_punctuation!(PathSeparator, </>);////// // expr </> expr </> expr .../// struct PathSegments {///     segments: Punctuated<Expr, PathSeparator>,/// }////// impl Parse for PathSegments {///     fn parse(input: ParseStream) -> Result<Self> {///         let mut segments = Punctuated::new();//////         let first = parse_until(input, PathSeparator)?;///         segments.push_value(syn::parse2(first)?);//////         while input.peek(PathSeparator) {///             segments.push_punct(input.parse()?);//////             let next = parse_until(input, PathSeparator)?;///             segments.push_value(syn::parse2(next)?);///         }//////         Ok(PathSegments { segments })///     }/// }////// fn parse_until<E: Peek>(input: ParseStream, end: E) -> Result<TokenStream> {///     let mut tokens = TokenStream::new();///     while !input.is_empty() && !input.peek(end) {///         let next: TokenTree = input.parse()?;///         tokens.extend(Some(next));///     }///     Ok(tokens)/// }////// fn main() {///     let input = r#" a::b </> c::d::e "#;///     let _: PathSegments = syn::parse_str(input).unwrap();/// }/// ```#[macro_export]macro_rules! custom_punctuation {    ($ident:ident, $($tt:tt)+) => {        pub struct $ident {            #[allow(dead_code)]            pub spans: $crate::custom_punctuation_repr!($($tt)+),        }         #[doc(hidden)]        #[allow(dead_code, non_snake_case)]        pub fn $ident<__S: $crate::__private::IntoSpans<$crate::custom_punctuation_repr!($($tt)+)>>(            spans: __S,        ) -> $ident {            let _validate_len = 0 $(+ $crate::custom_punctuation_len!(strict, $tt))*;            $ident {                spans: $crate::__private::IntoSpans::into_spans(spans)            }        }         const _: () = {            impl $crate::__private::Default for $ident {                fn default() -> Self {                    $ident($crate::__private::Span::call_site())                }            }             $crate::impl_parse_for_custom_punctuation!($ident, $($tt)+);            $crate::impl_to_tokens_for_custom_punctuation!($ident, $($tt)+);            $crate::impl_clone_for_custom_punctuation!($ident, $($tt)+);            $crate::impl_extra_traits_for_custom_punctuation!($ident, $($tt)+);        };    };} // Not public API.#[cfg(feature = "parsing")]#[doc(hidden)]#[macro_export]macro_rules! impl_parse_for_custom_punctuation {    ($ident:ident, $($tt:tt)+) => {        impl $crate::__private::CustomToken for $ident {            fn peek(cursor: $crate::buffer::Cursor) -> $crate::__private::bool {                $crate::__private::peek_punct(cursor, $crate::stringify_punct!($($tt)+))            }             fn display() -> &'static $crate::__private::str {                $crate::__private::concat!("`", $crate::stringify_punct!($($tt)+), "`")            }        }         impl $crate::parse::Parse for $ident {            fn parse(input: $crate::parse::ParseStream) -> $crate::parse::Result<$ident> {                let spans: $crate::custom_punctuation_repr!($($tt)+) =                    $crate::__private::parse_punct(input, $crate::stringify_punct!($($tt)+))?;                Ok($ident(spans))            }        }    };} // Not public API.#[cfg(not(feature = "parsing"))]#[doc(hidden)]#[macro_export]macro_rules! impl_parse_for_custom_punctuation {    ($ident:ident, $($tt:tt)+) => {};} // Not public API.#[cfg(feature = "printing")]#[doc(hidden)]#[macro_export]macro_rules! impl_to_tokens_for_custom_punctuation {    ($ident:ident, $($tt:tt)+) => {        impl $crate::__private::ToTokens for $ident {            fn to_tokens(&self, tokens: &mut $crate::__private::TokenStream2) {                $crate::__private::print_punct($crate::stringify_punct!($($tt)+), &self.spans, tokens)            }        }    };} // Not public API.#[cfg(not(feature = "printing"))]#[doc(hidden)]#[macro_export]macro_rules! impl_to_tokens_for_custom_punctuation {    ($ident:ident, $($tt:tt)+) => {};} // Not public API.#[cfg(feature = "clone-impls")]#[doc(hidden)]#[macro_export]macro_rules! impl_clone_for_custom_punctuation {    ($ident:ident, $($tt:tt)+) => {        impl $crate::__private::Copy for $ident {}         #[allow(clippy::expl_impl_clone_on_copy)]        impl $crate::__private::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_punctuation {    ($ident:ident, $($tt:tt)+) => {};} // Not public API.#[cfg(feature = "extra-traits")]#[doc(hidden)]#[macro_export]macro_rules! impl_extra_traits_for_custom_punctuation {    ($ident:ident, $($tt:tt)+) => {        impl $crate::__private::Debug for $ident {            fn fmt(&self, f: &mut $crate::__private::Formatter) -> $crate::__private::FmtResult {                $crate::__private::Formatter::write_str(f, $crate::__private::stringify!($ident))            }        }         impl $crate::__private::Eq for $ident {}         impl $crate::__private::PartialEq for $ident {            fn eq(&self, _other: &Self) -> $crate::__private::bool {                true            }        }         impl $crate::__private::Hash for $ident {            fn hash<__H: $crate::__private::Hasher>(&self, _state: &mut __H) {}        }    };} // Not public API.#[cfg(not(feature = "extra-traits"))]#[doc(hidden)]#[macro_export]macro_rules! impl_extra_traits_for_custom_punctuation {    ($ident:ident, $($tt:tt)+) => {};} // Not public API.#[doc(hidden)]#[macro_export]macro_rules! custom_punctuation_repr {    ($($tt:tt)+) => {        [$crate::__private::Span; 0 $(+ $crate::custom_punctuation_len!(lenient, $tt))+]    };} // Not public API.#[doc(hidden)]#[macro_export]#[rustfmt::skip]macro_rules! custom_punctuation_len {    ($mode:ident, &)     => { 1 };    ($mode:ident, &&)    => { 2 };    ($mode:ident, &=)    => { 2 };    ($mode:ident, @)     => { 1 };    ($mode:ident, ^)     => { 1 };    ($mode:ident, ^=)    => { 2 };    ($mode:ident, :)     => { 1 };    ($mode:ident, ,)     => { 1 };    ($mode:ident, $)     => { 1 };    ($mode:ident, .)     => { 1 };    ($mode:ident, ..)    => { 2 };    ($mode:ident, ...)   => { 3 };    ($mode:ident, ..=)   => { 3 };    ($mode:ident, =)     => { 1 };    ($mode:ident, ==)    => { 2 };    ($mode:ident, =>)    => { 2 };    ($mode:ident, >=)    => { 2 };    ($mode:ident, >)     => { 1 };    ($mode:ident, <-)    => { 2 };    ($mode:ident, <=)    => { 2 };    ($mode:ident, <)     => { 1 };    ($mode:ident, -)     => { 1 };    ($mode:ident, -=)    => { 2 };    ($mode:ident, !=)    => { 2 };    ($mode:ident, !)     => { 1 };    ($mode:ident, |)     => { 1 };    ($mode:ident, |=)    => { 2 };    ($mode:ident, ||)    => { 2 };    ($mode:ident, ::)    => { 2 };    ($mode:ident, %)     => { 1 };    ($mode:ident, %=)    => { 2 };    ($mode:ident, +)     => { 1 };    ($mode:ident, +=)    => { 2 };    ($mode:ident, #)     => { 1 };    ($mode:ident, ?)     => { 1 };    ($mode:ident, ->)    => { 2 };    ($mode:ident, ;)     => { 1 };    ($mode:ident, <<)    => { 2 };    ($mode:ident, <<=)   => { 3 };    ($mode:ident, >>)    => { 2 };    ($mode:ident, >>=)   => { 3 };    ($mode:ident, /)     => { 1 };    ($mode:ident, /=)    => { 2 };    ($mode:ident, *)     => { 1 };    ($mode:ident, *=)    => { 2 };    ($mode:ident, ~)     => { 1 };    (lenient, $tt:tt)    => { 0 };    (strict, $tt:tt)     => {{ $crate::custom_punctuation_unexpected!($tt); 0 }};} // Not public API.#[doc(hidden)]#[macro_export]macro_rules! custom_punctuation_unexpected {    () => {};} // Not public API.#[doc(hidden)]#[macro_export]macro_rules! stringify_punct {    ($($tt:tt)+) => {        $crate::__private::concat!($($crate::__private::stringify!($tt)),+)    };}