]>
Commit | Line | Data |
---|---|---|
353b0b11 FG |
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 | pub spans: $crate::custom_punctuation_repr!($($tt)+), | |
82 | } | |
83 | ||
84 | #[doc(hidden)] | |
85 | #[allow(dead_code, non_snake_case)] | |
86 | pub fn $ident<__S: $crate::__private::IntoSpans<$crate::custom_punctuation_repr!($($tt)+)>>( | |
87 | spans: __S, | |
88 | ) -> $ident { | |
89 | let _validate_len = 0 $(+ $crate::custom_punctuation_len!(strict, $tt))*; | |
90 | $ident { | |
91 | spans: $crate::__private::IntoSpans::into_spans(spans) | |
92 | } | |
93 | } | |
94 | ||
95 | impl $crate::__private::Default for $ident { | |
96 | fn default() -> Self { | |
97 | $ident($crate::__private::Span::call_site()) | |
98 | } | |
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::token::CustomToken for $ident { | |
115 | fn peek(cursor: $crate::buffer::Cursor) -> bool { | |
116 | $crate::token::parsing::peek_punct(cursor, $crate::stringify_punct!($($tt)+)) | |
117 | } | |
118 | ||
119 | fn display() -> &'static $crate::__private::str { | |
120 | concat!("`", $crate::stringify_punct!($($tt)+), "`") | |
121 | } | |
122 | } | |
123 | ||
124 | impl $crate::parse::Parse for $ident { | |
125 | fn parse(input: $crate::parse::ParseStream) -> $crate::parse::Result<$ident> { | |
126 | let spans: $crate::custom_punctuation_repr!($($tt)+) = | |
127 | $crate::token::parsing::punct(input, $crate::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 { | |
139 | ($ident:ident, $($tt:tt)+) => {}; | |
140 | } | |
141 | ||
142 | // Not public API. | |
143 | #[cfg(feature = "printing")] | |
144 | #[doc(hidden)] | |
145 | #[macro_export] | |
146 | macro_rules! impl_to_tokens_for_custom_punctuation { | |
147 | ($ident:ident, $($tt:tt)+) => { | |
148 | impl $crate::__private::ToTokens for $ident { | |
149 | fn to_tokens(&self, tokens: &mut $crate::__private::TokenStream2) { | |
150 | $crate::token::printing::punct($crate::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 { | |
161 | ($ident:ident, $($tt:tt)+) => {}; | |
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 { | |
169 | ($ident:ident, $($tt:tt)+) => { | |
170 | impl $crate::__private::Copy for $ident {} | |
171 | ||
172 | #[allow(clippy::expl_impl_clone_on_copy)] | |
173 | impl $crate::__private::Clone for $ident { | |
174 | fn clone(&self) -> Self { | |
175 | *self | |
176 | } | |
177 | } | |
178 | }; | |
179 | } | |
180 | ||
181 | // Not public API. | |
182 | #[cfg(not(feature = "clone-impls"))] | |
183 | #[doc(hidden)] | |
184 | #[macro_export] | |
185 | macro_rules! impl_clone_for_custom_punctuation { | |
186 | ($ident:ident, $($tt:tt)+) => {}; | |
187 | } | |
188 | ||
189 | // Not public API. | |
190 | #[cfg(feature = "extra-traits")] | |
191 | #[doc(hidden)] | |
192 | #[macro_export] | |
193 | macro_rules! impl_extra_traits_for_custom_punctuation { | |
194 | ($ident:ident, $($tt:tt)+) => { | |
195 | impl $crate::__private::Debug for $ident { | |
196 | fn fmt(&self, f: &mut $crate::__private::Formatter) -> $crate::__private::fmt::Result { | |
197 | $crate::__private::Formatter::write_str(f, stringify!($ident)) | |
198 | } | |
199 | } | |
200 | ||
201 | impl $crate::__private::Eq for $ident {} | |
202 | ||
203 | impl $crate::__private::PartialEq for $ident { | |
204 | fn eq(&self, _other: &Self) -> $crate::__private::bool { | |
205 | true | |
206 | } | |
207 | } | |
208 | ||
209 | impl $crate::__private::Hash for $ident { | |
210 | fn hash<__H: $crate::__private::Hasher>(&self, _state: &mut __H) {} | |
211 | } | |
212 | }; | |
213 | } | |
214 | ||
215 | // Not public API. | |
216 | #[cfg(not(feature = "extra-traits"))] | |
217 | #[doc(hidden)] | |
218 | #[macro_export] | |
219 | macro_rules! impl_extra_traits_for_custom_punctuation { | |
220 | ($ident:ident, $($tt:tt)+) => {}; | |
221 | } | |
222 | ||
223 | // Not public API. | |
224 | #[doc(hidden)] | |
225 | #[macro_export] | |
226 | macro_rules! custom_punctuation_repr { | |
227 | ($($tt:tt)+) => { | |
228 | [$crate::__private::Span; 0 $(+ $crate::custom_punctuation_len!(lenient, $tt))+] | |
229 | }; | |
230 | } | |
231 | ||
232 | // Not public API. | |
233 | #[doc(hidden)] | |
234 | #[macro_export] | |
235 | #[rustfmt::skip] | |
236 | macro_rules! custom_punctuation_len { | |
237 | ($mode:ident, +) => { 1 }; | |
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, ^) => { 1 }; | |
245 | ($mode:ident, ^=) => { 2 }; | |
246 | ($mode:ident, :) => { 1 }; | |
247 | ($mode:ident, ::) => { 2 }; | |
248 | ($mode:ident, ,) => { 1 }; | |
249 | ($mode:ident, /) => { 1 }; | |
250 | ($mode:ident, /=) => { 2 }; | |
251 | ($mode:ident, .) => { 1 }; | |
252 | ($mode:ident, ..) => { 2 }; | |
253 | ($mode:ident, ...) => { 3 }; | |
254 | ($mode:ident, ..=) => { 3 }; | |
255 | ($mode:ident, =) => { 1 }; | |
256 | ($mode:ident, ==) => { 2 }; | |
257 | ($mode:ident, >=) => { 2 }; | |
258 | ($mode:ident, >) => { 1 }; | |
259 | ($mode:ident, <=) => { 2 }; | |
260 | ($mode:ident, <) => { 1 }; | |
261 | ($mode:ident, *=) => { 2 }; | |
262 | ($mode:ident, !=) => { 2 }; | |
263 | ($mode:ident, |) => { 1 }; | |
264 | ($mode:ident, |=) => { 2 }; | |
265 | ($mode:ident, ||) => { 2 }; | |
266 | ($mode:ident, #) => { 1 }; | |
267 | ($mode:ident, ?) => { 1 }; | |
268 | ($mode:ident, ->) => { 2 }; | |
269 | ($mode:ident, <-) => { 2 }; | |
270 | ($mode:ident, %) => { 1 }; | |
271 | ($mode:ident, %=) => { 2 }; | |
272 | ($mode:ident, =>) => { 2 }; | |
273 | ($mode:ident, ;) => { 1 }; | |
274 | ($mode:ident, <<) => { 2 }; | |
275 | ($mode:ident, <<=) => { 3 }; | |
276 | ($mode:ident, >>) => { 2 }; | |
277 | ($mode:ident, >>=) => { 3 }; | |
278 | ($mode:ident, *) => { 1 }; | |
279 | ($mode:ident, -) => { 1 }; | |
280 | ($mode:ident, -=) => { 2 }; | |
281 | ($mode:ident, ~) => { 1 }; | |
282 | (lenient, $tt:tt) => { 0 }; | |
283 | (strict, $tt:tt) => {{ $crate::custom_punctuation_unexpected!($tt); 0 }}; | |
284 | } | |
285 | ||
286 | // Not public API. | |
287 | #[doc(hidden)] | |
288 | #[macro_export] | |
289 | macro_rules! custom_punctuation_unexpected { | |
290 | () => {}; | |
291 | } | |
292 | ||
293 | // Not public API. | |
294 | #[doc(hidden)] | |
295 | #[macro_export] | |
296 | macro_rules! stringify_punct { | |
297 | ($($tt:tt)+) => { | |
298 | concat!($(stringify!($tt)),+) | |
299 | }; | |
300 | } |