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