custom_punctuation.rs 9.0 KB

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