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