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] 78 macro_rules! custom_punctuation { 79 ($ident:ident, $($tt:tt)+) => { 80 pub struct $ident { 81 #[allow(dead_code)] 82 pub spans: $crate::custom_punctuation_repr!($($tt)+), 83 } 84 85 #[doc(hidden)] 86 #[allow(dead_code, non_snake_case)] 87 pub fn $ident<__S: $crate::__private::IntoSpans<$crate::custom_punctuation_repr!($($tt)+)>>( 88 spans: __S, 89 ) -> $ident { 90 let _validate_len = 0 $(+ $crate::custom_punctuation_len!(strict, $tt))*; 91 $ident { 92 spans: $crate::__private::IntoSpans::into_spans(spans) 93 } 94 } 95 96 const _: () = { 97 impl $crate::__private::Default for $ident { 98 fn default() -> Self { 99 $ident($crate::__private::Span::call_site()) 100 } 101 } 102 103 $crate::impl_parse_for_custom_punctuation!($ident, $($tt)+); 104 $crate::impl_to_tokens_for_custom_punctuation!($ident, $($tt)+); 105 $crate::impl_clone_for_custom_punctuation!($ident, $($tt)+); 106 $crate::impl_extra_traits_for_custom_punctuation!($ident, $($tt)+); 107 }; 108 }; 109 } 110 111 // Not public API. 112 #[cfg(feature = "parsing")] 113 #[doc(hidden)] 114 #[macro_export] 115 macro_rules! impl_parse_for_custom_punctuation { 116 ($ident:ident, $($tt:tt)+) => { 117 impl $crate::__private::CustomToken for $ident { 118 fn peek(cursor: $crate::buffer::Cursor) -> $crate::__private::bool { 119 $crate::__private::peek_punct(cursor, $crate::stringify_punct!($($tt)+)) 120 } 121 122 fn display() -> &'static $crate::__private::str { 123 $crate::__private::concat!("`", $crate::stringify_punct!($($tt)+), "`") 124 } 125 } 126 127 impl $crate::parse::Parse for $ident { 128 fn parse(input: $crate::parse::ParseStream) -> $crate::parse::Result<$ident> { 129 let spans: $crate::custom_punctuation_repr!($($tt)+) = 130 $crate::__private::parse_punct(input, $crate::stringify_punct!($($tt)+))?; 131 Ok($ident(spans)) 132 } 133 } 134 }; 135 } 136 137 // Not public API. 138 #[cfg(not(feature = "parsing"))] 139 #[doc(hidden)] 140 #[macro_export] 141 macro_rules! impl_parse_for_custom_punctuation { 142 ($ident:ident, $($tt:tt)+) => {}; 143 } 144 145 // Not public API. 146 #[cfg(feature = "printing")] 147 #[doc(hidden)] 148 #[macro_export] 149 macro_rules! impl_to_tokens_for_custom_punctuation { 150 ($ident:ident, $($tt:tt)+) => { 151 impl $crate::__private::ToTokens for $ident { 152 fn to_tokens(&self, tokens: &mut $crate::__private::TokenStream2) { 153 $crate::__private::print_punct($crate::stringify_punct!($($tt)+), &self.spans, tokens) 154 } 155 } 156 }; 157 } 158 159 // Not public API. 160 #[cfg(not(feature = "printing"))] 161 #[doc(hidden)] 162 #[macro_export] 163 macro_rules! impl_to_tokens_for_custom_punctuation { 164 ($ident:ident, $($tt:tt)+) => {}; 165 } 166 167 // Not public API. 168 #[cfg(feature = "clone-impls")] 169 #[doc(hidden)] 170 #[macro_export] 171 macro_rules! impl_clone_for_custom_punctuation { 172 ($ident:ident, $($tt:tt)+) => { 173 impl $crate::__private::Copy for $ident {} 174 175 #[allow(clippy::expl_impl_clone_on_copy)] 176 impl $crate::__private::Clone for $ident { 177 fn clone(&self) -> Self { 178 *self 179 } 180 } 181 }; 182 } 183 184 // Not public API. 185 #[cfg(not(feature = "clone-impls"))] 186 #[doc(hidden)] 187 #[macro_export] 188 macro_rules! impl_clone_for_custom_punctuation { 189 ($ident:ident, $($tt:tt)+) => {}; 190 } 191 192 // Not public API. 193 #[cfg(feature = "extra-traits")] 194 #[doc(hidden)] 195 #[macro_export] 196 macro_rules! impl_extra_traits_for_custom_punctuation { 197 ($ident:ident, $($tt:tt)+) => { 198 impl $crate::__private::Debug for $ident { 199 fn fmt(&self, f: &mut $crate::__private::Formatter) -> $crate::__private::FmtResult { 200 $crate::__private::Formatter::write_str(f, $crate::__private::stringify!($ident)) 201 } 202 } 203 204 impl $crate::__private::Eq for $ident {} 205 206 impl $crate::__private::PartialEq for $ident { 207 fn eq(&self, _other: &Self) -> $crate::__private::bool { 208 true 209 } 210 } 211 212 impl $crate::__private::Hash for $ident { 213 fn hash<__H: $crate::__private::Hasher>(&self, _state: &mut __H) {} 214 } 215 }; 216 } 217 218 // Not public API. 219 #[cfg(not(feature = "extra-traits"))] 220 #[doc(hidden)] 221 #[macro_export] 222 macro_rules! impl_extra_traits_for_custom_punctuation { 223 ($ident:ident, $($tt:tt)+) => {}; 224 } 225 226 // Not public API. 227 #[doc(hidden)] 228 #[macro_export] 229 macro_rules! custom_punctuation_repr { 230 ($($tt:tt)+) => { 231 [$crate::__private::Span; 0 $(+ $crate::custom_punctuation_len!(lenient, $tt))+] 232 }; 233 } 234 235 // Not public API. 236 #[doc(hidden)] 237 #[macro_export] 238 #[rustfmt::skip] 239 macro_rules! custom_punctuation_len { 240 ($mode:ident, &) => { 1 }; 241 ($mode:ident, &&) => { 2 }; 242 ($mode:ident, &=) => { 2 }; 243 ($mode:ident, @) => { 1 }; 244 ($mode:ident, ^) => { 1 }; 245 ($mode:ident, ^=) => { 2 }; 246 ($mode:ident, :) => { 1 }; 247 ($mode:ident, ,) => { 1 }; 248 ($mode:ident, $) => { 1 }; 249 ($mode:ident, .) => { 1 }; 250 ($mode:ident, ..) => { 2 }; 251 ($mode:ident, ...) => { 3 }; 252 ($mode:ident, ..=) => { 3 }; 253 ($mode:ident, =) => { 1 }; 254 ($mode:ident, ==) => { 2 }; 255 ($mode:ident, =>) => { 2 }; 256 ($mode:ident, >=) => { 2 }; 257 ($mode:ident, >) => { 1 }; 258 ($mode:ident, <-) => { 2 }; 259 ($mode:ident, <=) => { 2 }; 260 ($mode:ident, <) => { 1 }; 261 ($mode:ident, -) => { 1 }; 262 ($mode:ident, -=) => { 2 }; 263 ($mode:ident, !=) => { 2 }; 264 ($mode:ident, !) => { 1 }; 265 ($mode:ident, |) => { 1 }; 266 ($mode:ident, |=) => { 2 }; 267 ($mode:ident, ||) => { 2 }; 268 ($mode:ident, ::) => { 2 }; 269 ($mode:ident, %) => { 1 }; 270 ($mode:ident, %=) => { 2 }; 271 ($mode:ident, +) => { 1 }; 272 ($mode:ident, +=) => { 2 }; 273 ($mode:ident, #) => { 1 }; 274 ($mode:ident, ?) => { 1 }; 275 ($mode:ident, ->) => { 2 }; 276 ($mode:ident, ;) => { 1 }; 277 ($mode:ident, <<) => { 2 }; 278 ($mode:ident, <<=) => { 3 }; 279 ($mode:ident, >>) => { 2 }; 280 ($mode:ident, >>=) => { 3 }; 281 ($mode:ident, /) => { 1 }; 282 ($mode:ident, /=) => { 2 }; 283 ($mode:ident, *) => { 1 }; 284 ($mode:ident, *=) => { 2 }; 285 ($mode:ident, ~) => { 1 }; 286 (lenient, $tt:tt) => { 0 }; 287 (strict, $tt:tt) => {{ $crate::custom_punctuation_unexpected!($tt); 0 }}; 288 } 289 290 // Not public API. 291 #[doc(hidden)] 292 #[macro_export] 293 macro_rules! custom_punctuation_unexpected { 294 () => {}; 295 } 296 297 // Not public API. 298 #[doc(hidden)] 299 #[macro_export] 300 macro_rules! stringify_punct { 301 ($($tt:tt)+) => { 302 $crate::__private::concat!($($crate::__private::stringify!($tt)),+) 303 }; 304 } 305