1/// Define a type that supports parsing and printing a multi-character symbol 2/// as if it were a punctuation token. 3/// 4/// # Usage 5/// 6/// ``` 7/// syn::custom_punctuation!(LeftRightArrow, <=>); 8/// ``` 9/// 10/// The generated syntax tree node supports the following operations just like 11/// any built-in punctuation token. 12/// 13/// - [Peeking] — `input.peek(LeftRightArrow)` 14/// 15/// - [Parsing] — `input.parse::<LeftRightArrow>()?` 16/// 17/// - [Printing] — `quote!( ... #lrarrow ... )` 18/// 19/// - Construction from a [`Span`] — `let lrarrow = LeftRightArrow(sp)` 20/// 21/// - Construction from multiple [`Span`] — `let lrarrow = LeftRightArrow([sp, sp, sp])` 22/// 23/// - Field access to its spans — `let spans = lrarrow.spans` 24/// 25/// [Peeking]: crate::parse::ParseBuffer::peek 26/// [Parsing]: crate::parse::ParseBuffer::parse 27/// [Printing]: quote::ToTokens 28/// [`Span`]: proc_macro2::Span 29/// 30/// # Example 31/// 32/// ``` 33/// use proc_macro2::{TokenStream, TokenTree}; 34/// use syn::parse::{Parse, ParseStream, Peek, Result}; 35/// use syn::punctuated::Punctuated; 36/// use syn::Expr; 37/// 38/// syn::custom_punctuation!(PathSeparator, </>); 39/// 40/// // expr </> expr </> expr ... 41/// struct PathSegments { 42/// segments: Punctuated<Expr, PathSeparator>, 43/// } 44/// 45/// impl Parse for PathSegments { 46/// fn parse(input: ParseStream) -> Result<Self> { 47/// let mut segments = Punctuated::new(); 48/// 49/// let first = parse_until(input, PathSeparator)?; 50/// segments.push_value(syn::parse2(first)?); 51/// 52/// while input.peek(PathSeparator) { 53/// segments.push_punct(input.parse()?); 54/// 55/// let next = parse_until(input, PathSeparator)?; 56/// segments.push_value(syn::parse2(next)?); 57/// } 58/// 59/// Ok(PathSegments { segments }) 60/// } 61/// } 62/// 63/// fn parse_until<E: Peek>(input: ParseStream, end: E) -> Result<TokenStream> { 64/// let mut tokens = TokenStream::new(); 65/// while !input.is_empty() && !input.peek(end) { 66/// let next: TokenTree = input.parse()?; 67/// tokens.extend(Some(next)); 68/// } 69/// Ok(tokens) 70/// } 71/// 72/// fn main() { 73/// let input = r#" a::b </> c::d::e "#; 74/// let _: PathSegments = syn::parse_str(input).unwrap(); 75/// } 76/// ``` 77#[macro_export] 78macro_rules! custom_punctuation { 79 ($ident:ident, $($tt:tt)+) => { 80 pub struct $ident { 81 pub spans: $crate::custom_punctuation_repr!($($tt)+), 82 } 83 84 #[doc(hidden)] 85 #[allow(dead_code, non_snake_case)] 86 pub fn $ident<__S: $crate::__private::IntoSpans<$crate::custom_punctuation_repr!($($tt)+)>>( 87 spans: __S, 88 ) -> $ident { 89 let _validate_len = 0 $(+ $crate::custom_punctuation_len!(strict, $tt))*; 90 $ident { 91 spans: $crate::__private::IntoSpans::into_spans(spans) 92 } 93 } 94 95 const _: () = { 96 impl $crate::__private::Default for $ident { 97 fn default() -> Self { 98 $ident($crate::__private::Span::call_site()) 99 } 100 } 101 102 $crate::impl_parse_for_custom_punctuation!($ident, $($tt)+); 103 $crate::impl_to_tokens_for_custom_punctuation!($ident, $($tt)+); 104 $crate::impl_clone_for_custom_punctuation!($ident, $($tt)+); 105 $crate::impl_extra_traits_for_custom_punctuation!($ident, $($tt)+); 106 }; 107 }; 108} 109 110// Not public API. 111#[cfg(feature = "parsing")] 112#[doc(hidden)] 113#[macro_export] 114macro_rules! impl_parse_for_custom_punctuation { 115 ($ident:ident, $($tt:tt)+) => { 116 impl $crate::__private::CustomToken for $ident { 117 fn peek(cursor: $crate::buffer::Cursor) -> $crate::__private::bool { 118 $crate::__private::peek_punct(cursor, $crate::stringify_punct!($($tt)+)) 119 } 120 121 fn display() -> &'static $crate::__private::str { 122 $crate::__private::concat!("`", $crate::stringify_punct!($($tt)+), "`") 123 } 124 } 125 126 impl $crate::parse::Parse for $ident { 127 fn parse(input: $crate::parse::ParseStream) -> $crate::parse::Result<$ident> { 128 let spans: $crate::custom_punctuation_repr!($($tt)+) = 129 $crate::__private::parse_punct(input, $crate::stringify_punct!($($tt)+))?; 130 Ok($ident(spans)) 131 } 132 } 133 }; 134} 135 136// Not public API. 137#[cfg(not(feature = "parsing"))] 138#[doc(hidden)] 139#[macro_export] 140macro_rules! impl_parse_for_custom_punctuation { 141 ($ident:ident, $($tt:tt)+) => {}; 142} 143 144// Not public API. 145#[cfg(feature = "printing")] 146#[doc(hidden)] 147#[macro_export] 148macro_rules! impl_to_tokens_for_custom_punctuation { 149 ($ident:ident, $($tt:tt)+) => { 150 impl $crate::__private::ToTokens for $ident { 151 fn to_tokens(&self, tokens: &mut $crate::__private::TokenStream2) { 152 $crate::__private::print_punct($crate::stringify_punct!($($tt)+), &self.spans, tokens) 153 } 154 } 155 }; 156} 157 158// Not public API. 159#[cfg(not(feature = "printing"))] 160#[doc(hidden)] 161#[macro_export] 162macro_rules! impl_to_tokens_for_custom_punctuation { 163 ($ident:ident, $($tt:tt)+) => {}; 164} 165 166// Not public API. 167#[cfg(feature = "clone-impls")] 168#[doc(hidden)] 169#[macro_export] 170macro_rules! impl_clone_for_custom_punctuation { 171 ($ident:ident, $($tt:tt)+) => { 172 impl $crate::__private::Copy for $ident {} 173 174 #[allow(clippy::expl_impl_clone_on_copy)] 175 impl $crate::__private::Clone for $ident { 176 fn clone(&self) -> Self { 177 *self 178 } 179 } 180 }; 181} 182 183// Not public API. 184#[cfg(not(feature = "clone-impls"))] 185#[doc(hidden)] 186#[macro_export] 187macro_rules! impl_clone_for_custom_punctuation { 188 ($ident:ident, $($tt:tt)+) => {}; 189} 190 191// Not public API. 192#[cfg(feature = "extra-traits")] 193#[doc(hidden)] 194#[macro_export] 195macro_rules! impl_extra_traits_for_custom_punctuation { 196 ($ident:ident, $($tt:tt)+) => { 197 impl $crate::__private::Debug for $ident { 198 fn fmt(&self, f: &mut $crate::__private::Formatter) -> $crate::__private::FmtResult { 199 $crate::__private::Formatter::write_str(f, $crate::__private::stringify!($ident)) 200 } 201 } 202 203 impl $crate::__private::Eq for $ident {} 204 205 impl $crate::__private::PartialEq for $ident { 206 fn eq(&self, _other: &Self) -> $crate::__private::bool { 207 true 208 } 209 } 210 211 impl $crate::__private::Hash for $ident { 212 fn hash<__H: $crate::__private::Hasher>(&self, _state: &mut __H) {} 213 } 214 }; 215} 216 217// Not public API. 218#[cfg(not(feature = "extra-traits"))] 219#[doc(hidden)] 220#[macro_export] 221macro_rules! impl_extra_traits_for_custom_punctuation { 222 ($ident:ident, $($tt:tt)+) => {}; 223} 224 225// Not public API. 226#[doc(hidden)] 227#[macro_export] 228macro_rules! custom_punctuation_repr { 229 ($($tt:tt)+) => { 230 [$crate::__private::Span; 0 $(+ $crate::custom_punctuation_len!(lenient, $tt))+] 231 }; 232} 233 234// Not public API. 235#[doc(hidden)] 236#[macro_export] 237#[rustfmt::skip] 238macro_rules! custom_punctuation_len { 239 ($mode:ident, +) => { 1 }; 240 ($mode:ident, +=) => { 2 }; 241 ($mode:ident, &) => { 1 }; 242 ($mode:ident, &&) => { 2 }; 243 ($mode:ident, &=) => { 2 }; 244 ($mode:ident, @) => { 1 }; 245 ($mode:ident, !) => { 1 }; 246 ($mode:ident, ^) => { 1 }; 247 ($mode:ident, ^=) => { 2 }; 248 ($mode:ident, :) => { 1 }; 249 ($mode:ident, ::) => { 2 }; 250 ($mode:ident, ,) => { 1 }; 251 ($mode:ident, /) => { 1 }; 252 ($mode:ident, /=) => { 2 }; 253 ($mode:ident, .) => { 1 }; 254 ($mode:ident, ..) => { 2 }; 255 ($mode:ident, ...) => { 3 }; 256 ($mode:ident, ..=) => { 3 }; 257 ($mode:ident, =) => { 1 }; 258 ($mode:ident, ==) => { 2 }; 259 ($mode:ident, >=) => { 2 }; 260 ($mode:ident, >) => { 1 }; 261 ($mode:ident, <=) => { 2 }; 262 ($mode:ident, <) => { 1 }; 263 ($mode:ident, *=) => { 2 }; 264 ($mode:ident, !=) => { 2 }; 265 ($mode:ident, |) => { 1 }; 266 ($mode:ident, |=) => { 2 }; 267 ($mode:ident, ||) => { 2 }; 268 ($mode:ident, #) => { 1 }; 269 ($mode:ident, ?) => { 1 }; 270 ($mode:ident, ->) => { 2 }; 271 ($mode:ident, <-) => { 2 }; 272 ($mode:ident, %) => { 1 }; 273 ($mode:ident, %=) => { 2 }; 274 ($mode:ident, =>) => { 2 }; 275 ($mode:ident, ;) => { 1 }; 276 ($mode:ident, <<) => { 2 }; 277 ($mode:ident, <<=) => { 3 }; 278 ($mode:ident, >>) => { 2 }; 279 ($mode:ident, >>=) => { 3 }; 280 ($mode:ident, *) => { 1 }; 281 ($mode:ident, -) => { 1 }; 282 ($mode:ident, -=) => { 2 }; 283 ($mode:ident, ~) => { 1 }; 284 (lenient, $tt:tt) => { 0 }; 285 (strict, $tt:tt) => {{ $crate::custom_punctuation_unexpected!($tt); 0 }}; 286} 287 288// Not public API. 289#[doc(hidden)] 290#[macro_export] 291macro_rules! custom_punctuation_unexpected { 292 () => {}; 293} 294 295// Not public API. 296#[doc(hidden)] 297#[macro_export] 298macro_rules! stringify_punct { 299 ($($tt:tt)+) => { 300 $crate::__private::concat!($($crate::__private::stringify!($tt)),+) 301 }; 302} 303