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 |
| 28 | /// [`Span`]: struct.Span.html |
| 29 | /// |
| 30 | /// # Example |
| 31 | /// |
| 32 | /// ```edition2018 |
David Tolnay | 9bfd112 | 2019-04-14 12:43:31 -0700 | [diff] [blame] | 33 | /// use syn::parse::{Parse, ParseStream, Result}; |
David Tolnay | 9888040 | 2019-04-14 13:25:32 -0700 | [diff] [blame^] | 34 | /// use syn::punctuated::Punctuated; |
David Tolnay | 9bfd112 | 2019-04-14 12:43:31 -0700 | [diff] [blame] | 35 | /// use syn::token::Paren; |
David Tolnay | 9888040 | 2019-04-14 13:25:32 -0700 | [diff] [blame^] | 36 | /// use syn::{parenthesized, Expr, ExprParen}; |
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 | /// |
| 40 | /// // (expr) </> (expr) </> (expr) ... |
| 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 | /// |
| 49 | /// let la = input.lookahead1(); |
| 50 | /// if la.peek(Paren) { |
| 51 | /// let content; |
| 52 | /// let paren_token = parenthesized!(content in input); |
| 53 | /// let expr = Box::new(content.parse()?); |
| 54 | /// segments.push_value(Expr::Paren(ExprParen { attrs: vec![], paren_token, expr })); |
| 55 | /// } else { |
| 56 | /// return Err(la.error()); |
| 57 | /// } |
| 58 | /// |
David Tolnay | 9888040 | 2019-04-14 13:25:32 -0700 | [diff] [blame^] | 59 | /// while input.peek(PathSeparator) { |
David Tolnay | 9bfd112 | 2019-04-14 12:43:31 -0700 | [diff] [blame] | 60 | /// segments.push_punct(input.parse()?); |
| 61 | /// let content; |
| 62 | /// let paren_token = parenthesized!(content in input); |
| 63 | /// let expr = Box::new(content.parse()?); |
| 64 | /// segments.push_value(Expr::Paren(ExprParen { attrs: vec![], paren_token, expr })); |
| 65 | /// } |
| 66 | /// |
| 67 | /// Ok(PathSegments { segments }) |
| 68 | /// } |
| 69 | /// } |
| 70 | /// |
David Tolnay | 9888040 | 2019-04-14 13:25:32 -0700 | [diff] [blame^] | 71 | /// fn main() { |
| 72 | /// let input = r#" ("five") </> ("hundred") "#; |
| 73 | /// let _: PathSegments = syn::parse_str(input).unwrap(); |
| 74 | /// } |
David Tolnay | 9bfd112 | 2019-04-14 12:43:31 -0700 | [diff] [blame] | 75 | /// ``` |
David Tolnay | 9bfd112 | 2019-04-14 12:43:31 -0700 | [diff] [blame] | 76 | #[macro_export(local_inner_macros)] |
| 77 | macro_rules! custom_punctuation { |
| 78 | ($ident:ident, $($tt:tt)+) => { |
| 79 | pub struct $ident { |
David Tolnay | 76aabde | 2019-04-14 13:18:33 -0700 | [diff] [blame] | 80 | pub spans: custom_punctuation_repr!($($tt)+), |
David Tolnay | 9bfd112 | 2019-04-14 12:43:31 -0700 | [diff] [blame] | 81 | } |
| 82 | |
| 83 | #[doc(hidden)] |
| 84 | #[allow(non_snake_case)] |
David Tolnay | 76aabde | 2019-04-14 13:18:33 -0700 | [diff] [blame] | 85 | pub fn $ident<__S: $crate::export::IntoSpans<custom_punctuation_repr!($($tt)+)>>( |
David Tolnay | 9bfd112 | 2019-04-14 12:43:31 -0700 | [diff] [blame] | 86 | spans: __S, |
| 87 | ) -> $ident { |
David Tolnay | 76aabde | 2019-04-14 13:18:33 -0700 | [diff] [blame] | 88 | let _validate_len = 0 $(+ custom_punctuation_len!(strict, $tt))*; |
David Tolnay | 9bfd112 | 2019-04-14 12:43:31 -0700 | [diff] [blame] | 89 | $ident { |
| 90 | spans: $crate::export::IntoSpans::into_spans(spans) |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | impl $crate::export::Default for $ident { |
| 95 | fn default() -> Self { |
| 96 | $ident($crate::export::Span::call_site()) |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | impl_parse_for_custom_punctuation!($ident, $($tt)+); |
| 101 | impl_to_tokens_for_custom_punctuation!($ident, $($tt)+); |
| 102 | impl_clone_for_custom_punctuation!($ident, $($tt)+); |
| 103 | impl_extra_traits_for_custom_punctuation!($ident, $($tt)+); |
| 104 | }; |
| 105 | } |
| 106 | |
| 107 | // Not public API. |
| 108 | #[cfg(feature = "parsing")] |
| 109 | #[doc(hidden)] |
| 110 | #[macro_export(local_inner_macros)] |
| 111 | macro_rules! impl_parse_for_custom_punctuation { |
David Tolnay | 3c55767 | 2019-04-14 13:07:01 -0700 | [diff] [blame] | 112 | ($ident:ident, $($tt:tt)+) => { |
David Tolnay | 9bfd112 | 2019-04-14 12:43:31 -0700 | [diff] [blame] | 113 | impl $crate::token::CustomToken for $ident { |
| 114 | fn peek(cursor: $crate::buffer::Cursor) -> bool { |
| 115 | $crate::token::parsing::peek_punct(cursor, stringify_punct!($($tt)+)) |
| 116 | } |
| 117 | |
| 118 | fn display() -> &'static $crate::export::str { |
David Tolnay | 04cd0b7 | 2019-04-14 13:22:33 -0700 | [diff] [blame] | 119 | custom_punctuation_concat!("`", stringify_punct!($($tt)+), "`") |
David Tolnay | 9bfd112 | 2019-04-14 12:43:31 -0700 | [diff] [blame] | 120 | } |
| 121 | } |
| 122 | |
| 123 | impl $crate::parse::Parse for $ident { |
| 124 | fn parse(input: $crate::parse::ParseStream) -> $crate::parse::Result<$ident> { |
David Tolnay | 76aabde | 2019-04-14 13:18:33 -0700 | [diff] [blame] | 125 | let spans: custom_punctuation_repr!($($tt)+) = |
David Tolnay | 9bfd112 | 2019-04-14 12:43:31 -0700 | [diff] [blame] | 126 | $crate::token::parsing::punct(input, stringify_punct!($($tt)+))?; |
| 127 | Ok($ident(spans)) |
| 128 | } |
| 129 | } |
| 130 | }; |
| 131 | } |
| 132 | |
| 133 | // Not public API. |
| 134 | #[cfg(not(feature = "parsing"))] |
| 135 | #[doc(hidden)] |
| 136 | #[macro_export] |
| 137 | macro_rules! impl_parse_for_custom_punctuation { |
David Tolnay | 3c55767 | 2019-04-14 13:07:01 -0700 | [diff] [blame] | 138 | ($ident:ident, $($tt:tt)+) => {}; |
David Tolnay | 9bfd112 | 2019-04-14 12:43:31 -0700 | [diff] [blame] | 139 | } |
| 140 | |
| 141 | // Not public API. |
| 142 | #[cfg(feature = "printing")] |
| 143 | #[doc(hidden)] |
| 144 | #[macro_export(local_inner_macros)] |
| 145 | macro_rules! impl_to_tokens_for_custom_punctuation { |
David Tolnay | 3c55767 | 2019-04-14 13:07:01 -0700 | [diff] [blame] | 146 | ($ident:ident, $($tt:tt)+) => { |
David Tolnay | 9bfd112 | 2019-04-14 12:43:31 -0700 | [diff] [blame] | 147 | impl $crate::export::ToTokens for $ident { |
| 148 | fn to_tokens(&self, tokens: &mut $crate::export::TokenStream2) { |
| 149 | $crate::token::printing::punct(stringify_punct!($($tt)+), &self.spans, tokens) |
| 150 | } |
| 151 | } |
| 152 | }; |
| 153 | } |
| 154 | |
| 155 | // Not public API. |
| 156 | #[cfg(not(feature = "printing"))] |
| 157 | #[doc(hidden)] |
| 158 | #[macro_export] |
| 159 | macro_rules! impl_to_tokens_for_custom_punctuation { |
David Tolnay | 3c55767 | 2019-04-14 13:07:01 -0700 | [diff] [blame] | 160 | ($ident:ident, $($tt:tt)+) => {}; |
David Tolnay | 9bfd112 | 2019-04-14 12:43:31 -0700 | [diff] [blame] | 161 | } |
| 162 | |
| 163 | // Not public API. |
| 164 | #[cfg(feature = "clone-impls")] |
| 165 | #[doc(hidden)] |
| 166 | #[macro_export] |
| 167 | macro_rules! impl_clone_for_custom_punctuation { |
David Tolnay | 3c55767 | 2019-04-14 13:07:01 -0700 | [diff] [blame] | 168 | ($ident:ident, $($tt:tt)+) => { |
David Tolnay | 9bfd112 | 2019-04-14 12:43:31 -0700 | [diff] [blame] | 169 | impl $crate::export::Copy for $ident {} |
| 170 | |
| 171 | impl $crate::export::Clone for $ident { |
| 172 | fn clone(&self) -> Self { |
| 173 | *self |
| 174 | } |
| 175 | } |
| 176 | }; |
| 177 | } |
| 178 | |
| 179 | // Not public API. |
| 180 | #[cfg(not(feature = "clone-impls"))] |
| 181 | #[doc(hidden)] |
| 182 | #[macro_export] |
| 183 | macro_rules! impl_clone_for_custom_punctuation { |
David Tolnay | 3c55767 | 2019-04-14 13:07:01 -0700 | [diff] [blame] | 184 | ($ident:ident, $($tt:tt)+) => {}; |
David Tolnay | 9bfd112 | 2019-04-14 12:43:31 -0700 | [diff] [blame] | 185 | } |
| 186 | |
| 187 | // Not public API. |
| 188 | #[cfg(feature = "extra-traits")] |
| 189 | #[doc(hidden)] |
| 190 | #[macro_export(local_inner_macros)] |
| 191 | macro_rules! impl_extra_traits_for_custom_punctuation { |
David Tolnay | 3c55767 | 2019-04-14 13:07:01 -0700 | [diff] [blame] | 192 | ($ident:ident, $($tt:tt)+) => { |
David Tolnay | 9bfd112 | 2019-04-14 12:43:31 -0700 | [diff] [blame] | 193 | impl $crate::export::Debug for $ident { |
| 194 | fn fmt(&self, f: &mut $crate::export::Formatter) -> $crate::export::fmt::Result { |
| 195 | $crate::export::Formatter::write_str(f, stringify_punct!($($tt)+)) |
| 196 | } |
| 197 | } |
| 198 | |
| 199 | impl $crate::export::Eq for $ident {} |
| 200 | |
| 201 | impl $crate::export::PartialEq for $ident { |
| 202 | fn eq(&self, _other: &Self) -> $crate::export::bool { |
| 203 | true |
| 204 | } |
| 205 | } |
| 206 | |
| 207 | impl $crate::export::Hash for $ident { |
| 208 | fn hash<__H: $crate::export::Hasher>(&self, _state: &mut __H) {} |
| 209 | } |
| 210 | }; |
| 211 | } |
| 212 | |
| 213 | // Not public API. |
| 214 | #[cfg(not(feature = "extra-traits"))] |
| 215 | #[doc(hidden)] |
| 216 | #[macro_export] |
| 217 | macro_rules! impl_extra_traits_for_custom_punctuation { |
David Tolnay | 3c55767 | 2019-04-14 13:07:01 -0700 | [diff] [blame] | 218 | ($ident:ident, $($tt:tt)+) => {}; |
David Tolnay | 9bfd112 | 2019-04-14 12:43:31 -0700 | [diff] [blame] | 219 | } |
David Tolnay | f07f817 | 2019-04-14 13:07:52 -0700 | [diff] [blame] | 220 | |
| 221 | // Not public API. |
| 222 | #[doc(hidden)] |
| 223 | #[macro_export(local_inner_macros)] |
David Tolnay | 76aabde | 2019-04-14 13:18:33 -0700 | [diff] [blame] | 224 | macro_rules! custom_punctuation_repr { |
| 225 | ($($tt:tt)+) => { |
| 226 | [$crate::export::Span; 0 $(+ custom_punctuation_len!(lenient, $tt))+] |
| 227 | }; |
| 228 | } |
| 229 | |
| 230 | // Not public API. |
| 231 | #[doc(hidden)] |
| 232 | #[macro_export(local_inner_macros)] |
| 233 | macro_rules! custom_punctuation_len { |
David Tolnay | f07f817 | 2019-04-14 13:07:52 -0700 | [diff] [blame] | 234 | ($mode:ident, +) => { 1 }; |
| 235 | ($mode:ident, +=) => { 2 }; |
| 236 | ($mode:ident, &) => { 1 }; |
| 237 | ($mode:ident, &&) => { 2 }; |
| 238 | ($mode:ident, &=) => { 2 }; |
| 239 | ($mode:ident, @) => { 1 }; |
| 240 | ($mode:ident, !) => { 1 }; |
| 241 | ($mode:ident, ^) => { 1 }; |
| 242 | ($mode:ident, ^=) => { 2 }; |
| 243 | ($mode:ident, :) => { 1 }; |
| 244 | ($mode:ident, ::) => { 2 }; |
| 245 | ($mode:ident, ,) => { 1 }; |
| 246 | ($mode:ident, /) => { 1 }; |
| 247 | ($mode:ident, /=) => { 2 }; |
| 248 | ($mode:ident, .) => { 1 }; |
| 249 | ($mode:ident, ..) => { 2 }; |
| 250 | ($mode:ident, ...) => { 3 }; |
| 251 | ($mode:ident, ..=) => { 3 }; |
| 252 | ($mode:ident, =) => { 1 }; |
| 253 | ($mode:ident, ==) => { 2 }; |
| 254 | ($mode:ident, >=) => { 2 }; |
| 255 | ($mode:ident, >) => { 1 }; |
| 256 | ($mode:ident, <=) => { 2 }; |
| 257 | ($mode:ident, <) => { 1 }; |
| 258 | ($mode:ident, *=) => { 2 }; |
| 259 | ($mode:ident, !=) => { 2 }; |
| 260 | ($mode:ident, |) => { 1 }; |
| 261 | ($mode:ident, |=) => { 2 }; |
| 262 | ($mode:ident, ||) => { 2 }; |
| 263 | ($mode:ident, #) => { 1 }; |
| 264 | ($mode:ident, ?) => { 1 }; |
| 265 | ($mode:ident, ->) => { 2 }; |
| 266 | ($mode:ident, <-) => { 2 }; |
| 267 | ($mode:ident, %) => { 1 }; |
| 268 | ($mode:ident, %=) => { 2 }; |
| 269 | ($mode:ident, =>) => { 2 }; |
| 270 | ($mode:ident, ;) => { 1 }; |
| 271 | ($mode:ident, <<) => { 2 }; |
| 272 | ($mode:ident, <<=) => { 3 }; |
| 273 | ($mode:ident, >>) => { 2 }; |
| 274 | ($mode:ident, >>=) => { 3 }; |
| 275 | ($mode:ident, *) => { 1 }; |
| 276 | ($mode:ident, -) => { 1 }; |
| 277 | ($mode:ident, -=) => { 2 }; |
| 278 | ($mode:ident, ~) => { 1 }; |
| 279 | (lenient, $tt:tt) => { 0 }; |
David Tolnay | 04cd0b7 | 2019-04-14 13:22:33 -0700 | [diff] [blame] | 280 | (strict, $tt:tt) => {{ custom_punctuation_unexpected!($tt); 0 }}; |
David Tolnay | f07f817 | 2019-04-14 13:07:52 -0700 | [diff] [blame] | 281 | } |
| 282 | |
| 283 | // Not public API. |
| 284 | #[doc(hidden)] |
| 285 | #[macro_export] |
David Tolnay | 04cd0b7 | 2019-04-14 13:22:33 -0700 | [diff] [blame] | 286 | macro_rules! custom_punctuation_unexpected { |
David Tolnay | f07f817 | 2019-04-14 13:07:52 -0700 | [diff] [blame] | 287 | () => {}; |
| 288 | } |
| 289 | |
| 290 | // Not public API. |
| 291 | #[doc(hidden)] |
| 292 | #[macro_export] |
| 293 | macro_rules! stringify_punct { |
| 294 | ($($tt:tt)+) => { |
| 295 | concat!($(stringify!($tt)),+) |
| 296 | }; |
| 297 | } |
| 298 | |
| 299 | // Not public API. |
| 300 | // Without this, local_inner_macros breaks when looking for concat! |
| 301 | #[doc(hidden)] |
| 302 | #[macro_export] |
David Tolnay | 04cd0b7 | 2019-04-14 13:22:33 -0700 | [diff] [blame] | 303 | macro_rules! custom_punctuation_concat { |
David Tolnay | f07f817 | 2019-04-14 13:07:52 -0700 | [diff] [blame] | 304 | ($($tt:tt)*) => { |
| 305 | concat!($($tt)*) |
| 306 | }; |
| 307 | } |