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