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