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 | |