blob: 4a36f945ab5f61f3080ada93625f4738b198db9c [file] [log] [blame]
David Tolnay9bfd1122019-04-14 12:43:31 -07001/// Define a type that supports parsing and printing a multi-character symbol
David Tolnay98880402019-04-14 13:25:32 -07002/// as if it were a punctuation token.
David Tolnay9bfd1122019-04-14 12:43:31 -07003///
4/// # Usage
5///
David Tolnay9bfd1122019-04-14 12:43:31 -07006/// ```edition2018
David Tolnay98880402019-04-14 13:25:32 -07007/// syn::custom_punctuation!(LeftRightArrow, <=>);
David Tolnay9bfd1122019-04-14 12:43:31 -07008/// ```
9///
10/// The generated syntax tree node supports the following operations just like
11/// any built-in punctuation token.
12///
David Tolnay98880402019-04-14 13:25:32 -070013/// - [Peeking] — `input.peek(LeftRightArrow)`
David Tolnay9bfd1122019-04-14 12:43:31 -070014///
David Tolnay98880402019-04-14 13:25:32 -070015/// - [Parsing] — `input.parse::<LeftRightArrow>()?`
David Tolnay9bfd1122019-04-14 12:43:31 -070016///
David Tolnay98880402019-04-14 13:25:32 -070017/// - [Printing] — `quote!( ... #lrarrow ... )`
David Tolnay9bfd1122019-04-14 12:43:31 -070018///
David Tolnay98880402019-04-14 13:25:32 -070019/// - Construction from a [`Span`] — `let lrarrow = LeftRightArrow(sp)`
David Tolnay9bfd1122019-04-14 12:43:31 -070020///
David Tolnay98880402019-04-14 13:25:32 -070021/// - Construction from multiple [`Span`] — `let lrarrow = LeftRightArrow([sp, sp, sp])`
David Tolnay9bfd1122019-04-14 12:43:31 -070022///
David Tolnay98880402019-04-14 13:25:32 -070023/// - Field access to its spans — `let spans = lrarrow.spans`
David Tolnay9bfd1122019-04-14 12:43:31 -070024///
25/// [Peeking]: parse/struct.ParseBuffer.html#method.peek
26/// [Parsing]: parse/struct.ParseBuffer.html#method.parse
27/// [Printing]: https://docs.rs/quote/0.6/quote/trait.ToTokens.html
28/// [`Span`]: struct.Span.html
29///
30/// # Example
31///
32/// ```edition2018
David Tolnay9bfd1122019-04-14 12:43:31 -070033/// use syn::parse::{Parse, ParseStream, Result};
David Tolnay98880402019-04-14 13:25:32 -070034/// use syn::punctuated::Punctuated;
David Tolnay9bfd1122019-04-14 12:43:31 -070035/// use syn::token::Paren;
David Tolnay98880402019-04-14 13:25:32 -070036/// use syn::{parenthesized, Expr, ExprParen};
David Tolnay9bfd1122019-04-14 12:43:31 -070037///
David Tolnay98880402019-04-14 13:25:32 -070038/// syn::custom_punctuation!(PathSeparator, </>);
David Tolnay9bfd1122019-04-14 12:43:31 -070039///
40/// // (expr) </> (expr) </> (expr) ...
41/// struct PathSegments {
David Tolnay98880402019-04-14 13:25:32 -070042/// segments: Punctuated<Expr, PathSeparator>,
David Tolnay9bfd1122019-04-14 12:43:31 -070043/// }
44///
45/// impl Parse for PathSegments {
46/// fn parse(input: ParseStream) -> Result<Self> {
47/// let mut segments = Punctuated::new();
48///
49/// let la = input.lookahead1();
50/// if la.peek(Paren) {
51/// let content;
52/// let paren_token = parenthesized!(content in input);
53/// let expr = Box::new(content.parse()?);
54/// segments.push_value(Expr::Paren(ExprParen { attrs: vec![], paren_token, expr }));
55/// } else {
56/// return Err(la.error());
57/// }
58///
David Tolnay98880402019-04-14 13:25:32 -070059/// while input.peek(PathSeparator) {
David Tolnay9bfd1122019-04-14 12:43:31 -070060/// segments.push_punct(input.parse()?);
61/// let content;
62/// let paren_token = parenthesized!(content in input);
63/// let expr = Box::new(content.parse()?);
64/// segments.push_value(Expr::Paren(ExprParen { attrs: vec![], paren_token, expr }));
65/// }
66///
67/// Ok(PathSegments { segments })
68/// }
69/// }
70///
David Tolnay98880402019-04-14 13:25:32 -070071/// fn main() {
72/// let input = r#" ("five") </> ("hundred") "#;
73/// let _: PathSegments = syn::parse_str(input).unwrap();
74/// }
David Tolnay9bfd1122019-04-14 12:43:31 -070075/// ```
David Tolnay9bfd1122019-04-14 12:43:31 -070076#[macro_export(local_inner_macros)]
77macro_rules! custom_punctuation {
78 ($ident:ident, $($tt:tt)+) => {
79 pub struct $ident {
David Tolnay76aabde2019-04-14 13:18:33 -070080 pub spans: custom_punctuation_repr!($($tt)+),
David Tolnay9bfd1122019-04-14 12:43:31 -070081 }
82
83 #[doc(hidden)]
84 #[allow(non_snake_case)]
David Tolnay76aabde2019-04-14 13:18:33 -070085 pub fn $ident<__S: $crate::export::IntoSpans<custom_punctuation_repr!($($tt)+)>>(
David Tolnay9bfd1122019-04-14 12:43:31 -070086 spans: __S,
87 ) -> $ident {
David Tolnay76aabde2019-04-14 13:18:33 -070088 let _validate_len = 0 $(+ custom_punctuation_len!(strict, $tt))*;
David Tolnay9bfd1122019-04-14 12:43:31 -070089 $ident {
90 spans: $crate::export::IntoSpans::into_spans(spans)
91 }
92 }
93
94 impl $crate::export::Default for $ident {
95 fn default() -> Self {
96 $ident($crate::export::Span::call_site())
97 }
98 }
99
100 impl_parse_for_custom_punctuation!($ident, $($tt)+);
101 impl_to_tokens_for_custom_punctuation!($ident, $($tt)+);
102 impl_clone_for_custom_punctuation!($ident, $($tt)+);
103 impl_extra_traits_for_custom_punctuation!($ident, $($tt)+);
104 };
105}
106
107// Not public API.
108#[cfg(feature = "parsing")]
109#[doc(hidden)]
110#[macro_export(local_inner_macros)]
111macro_rules! impl_parse_for_custom_punctuation {
David Tolnay3c557672019-04-14 13:07:01 -0700112 ($ident:ident, $($tt:tt)+) => {
David Tolnay9bfd1122019-04-14 12:43:31 -0700113 impl $crate::token::CustomToken for $ident {
114 fn peek(cursor: $crate::buffer::Cursor) -> bool {
115 $crate::token::parsing::peek_punct(cursor, stringify_punct!($($tt)+))
116 }
117
118 fn display() -> &'static $crate::export::str {
David Tolnay04cd0b72019-04-14 13:22:33 -0700119 custom_punctuation_concat!("`", stringify_punct!($($tt)+), "`")
David Tolnay9bfd1122019-04-14 12:43:31 -0700120 }
121 }
122
123 impl $crate::parse::Parse for $ident {
124 fn parse(input: $crate::parse::ParseStream) -> $crate::parse::Result<$ident> {
David Tolnay76aabde2019-04-14 13:18:33 -0700125 let spans: custom_punctuation_repr!($($tt)+) =
David Tolnay9bfd1122019-04-14 12:43:31 -0700126 $crate::token::parsing::punct(input, stringify_punct!($($tt)+))?;
127 Ok($ident(spans))
128 }
129 }
130 };
131}
132
133// Not public API.
134#[cfg(not(feature = "parsing"))]
135#[doc(hidden)]
136#[macro_export]
137macro_rules! impl_parse_for_custom_punctuation {
David Tolnay3c557672019-04-14 13:07:01 -0700138 ($ident:ident, $($tt:tt)+) => {};
David Tolnay9bfd1122019-04-14 12:43:31 -0700139}
140
141// Not public API.
142#[cfg(feature = "printing")]
143#[doc(hidden)]
144#[macro_export(local_inner_macros)]
145macro_rules! impl_to_tokens_for_custom_punctuation {
David Tolnay3c557672019-04-14 13:07:01 -0700146 ($ident:ident, $($tt:tt)+) => {
David Tolnay9bfd1122019-04-14 12:43:31 -0700147 impl $crate::export::ToTokens for $ident {
148 fn to_tokens(&self, tokens: &mut $crate::export::TokenStream2) {
149 $crate::token::printing::punct(stringify_punct!($($tt)+), &self.spans, tokens)
150 }
151 }
152 };
153}
154
155// Not public API.
156#[cfg(not(feature = "printing"))]
157#[doc(hidden)]
158#[macro_export]
159macro_rules! impl_to_tokens_for_custom_punctuation {
David Tolnay3c557672019-04-14 13:07:01 -0700160 ($ident:ident, $($tt:tt)+) => {};
David Tolnay9bfd1122019-04-14 12:43:31 -0700161}
162
163// Not public API.
164#[cfg(feature = "clone-impls")]
165#[doc(hidden)]
166#[macro_export]
167macro_rules! impl_clone_for_custom_punctuation {
David Tolnay3c557672019-04-14 13:07:01 -0700168 ($ident:ident, $($tt:tt)+) => {
David Tolnay9bfd1122019-04-14 12:43:31 -0700169 impl $crate::export::Copy for $ident {}
170
171 impl $crate::export::Clone for $ident {
172 fn clone(&self) -> Self {
173 *self
174 }
175 }
176 };
177}
178
179// Not public API.
180#[cfg(not(feature = "clone-impls"))]
181#[doc(hidden)]
182#[macro_export]
183macro_rules! impl_clone_for_custom_punctuation {
David Tolnay3c557672019-04-14 13:07:01 -0700184 ($ident:ident, $($tt:tt)+) => {};
David Tolnay9bfd1122019-04-14 12:43:31 -0700185}
186
187// Not public API.
188#[cfg(feature = "extra-traits")]
189#[doc(hidden)]
190#[macro_export(local_inner_macros)]
191macro_rules! impl_extra_traits_for_custom_punctuation {
David Tolnay3c557672019-04-14 13:07:01 -0700192 ($ident:ident, $($tt:tt)+) => {
David Tolnay9bfd1122019-04-14 12:43:31 -0700193 impl $crate::export::Debug for $ident {
194 fn fmt(&self, f: &mut $crate::export::Formatter) -> $crate::export::fmt::Result {
195 $crate::export::Formatter::write_str(f, stringify_punct!($($tt)+))
196 }
197 }
198
199 impl $crate::export::Eq for $ident {}
200
201 impl $crate::export::PartialEq for $ident {
202 fn eq(&self, _other: &Self) -> $crate::export::bool {
203 true
204 }
205 }
206
207 impl $crate::export::Hash for $ident {
208 fn hash<__H: $crate::export::Hasher>(&self, _state: &mut __H) {}
209 }
210 };
211}
212
213// Not public API.
214#[cfg(not(feature = "extra-traits"))]
215#[doc(hidden)]
216#[macro_export]
217macro_rules! impl_extra_traits_for_custom_punctuation {
David Tolnay3c557672019-04-14 13:07:01 -0700218 ($ident:ident, $($tt:tt)+) => {};
David Tolnay9bfd1122019-04-14 12:43:31 -0700219}
David Tolnayf07f8172019-04-14 13:07:52 -0700220
221// Not public API.
222#[doc(hidden)]
223#[macro_export(local_inner_macros)]
David Tolnay76aabde2019-04-14 13:18:33 -0700224macro_rules! custom_punctuation_repr {
225 ($($tt:tt)+) => {
226 [$crate::export::Span; 0 $(+ custom_punctuation_len!(lenient, $tt))+]
227 };
228}
229
230// Not public API.
231#[doc(hidden)]
232#[macro_export(local_inner_macros)]
233macro_rules! custom_punctuation_len {
David Tolnayf07f8172019-04-14 13:07:52 -0700234 ($mode:ident, +) => { 1 };
235 ($mode:ident, +=) => { 2 };
236 ($mode:ident, &) => { 1 };
237 ($mode:ident, &&) => { 2 };
238 ($mode:ident, &=) => { 2 };
239 ($mode:ident, @) => { 1 };
240 ($mode:ident, !) => { 1 };
241 ($mode:ident, ^) => { 1 };
242 ($mode:ident, ^=) => { 2 };
243 ($mode:ident, :) => { 1 };
244 ($mode:ident, ::) => { 2 };
245 ($mode:ident, ,) => { 1 };
246 ($mode:ident, /) => { 1 };
247 ($mode:ident, /=) => { 2 };
248 ($mode:ident, .) => { 1 };
249 ($mode:ident, ..) => { 2 };
250 ($mode:ident, ...) => { 3 };
251 ($mode:ident, ..=) => { 3 };
252 ($mode:ident, =) => { 1 };
253 ($mode:ident, ==) => { 2 };
254 ($mode:ident, >=) => { 2 };
255 ($mode:ident, >) => { 1 };
256 ($mode:ident, <=) => { 2 };
257 ($mode:ident, <) => { 1 };
258 ($mode:ident, *=) => { 2 };
259 ($mode:ident, !=) => { 2 };
260 ($mode:ident, |) => { 1 };
261 ($mode:ident, |=) => { 2 };
262 ($mode:ident, ||) => { 2 };
263 ($mode:ident, #) => { 1 };
264 ($mode:ident, ?) => { 1 };
265 ($mode:ident, ->) => { 2 };
266 ($mode:ident, <-) => { 2 };
267 ($mode:ident, %) => { 1 };
268 ($mode:ident, %=) => { 2 };
269 ($mode:ident, =>) => { 2 };
270 ($mode:ident, ;) => { 1 };
271 ($mode:ident, <<) => { 2 };
272 ($mode:ident, <<=) => { 3 };
273 ($mode:ident, >>) => { 2 };
274 ($mode:ident, >>=) => { 3 };
275 ($mode:ident, *) => { 1 };
276 ($mode:ident, -) => { 1 };
277 ($mode:ident, -=) => { 2 };
278 ($mode:ident, ~) => { 1 };
279 (lenient, $tt:tt) => { 0 };
David Tolnay04cd0b72019-04-14 13:22:33 -0700280 (strict, $tt:tt) => {{ custom_punctuation_unexpected!($tt); 0 }};
David Tolnayf07f8172019-04-14 13:07:52 -0700281}
282
283// Not public API.
284#[doc(hidden)]
285#[macro_export]
David Tolnay04cd0b72019-04-14 13:22:33 -0700286macro_rules! custom_punctuation_unexpected {
David Tolnayf07f8172019-04-14 13:07:52 -0700287 () => {};
288}
289
290// Not public API.
291#[doc(hidden)]
292#[macro_export]
293macro_rules! stringify_punct {
294 ($($tt:tt)+) => {
295 concat!($(stringify!($tt)),+)
296 };
297}
298
299// Not public API.
300// Without this, local_inner_macros breaks when looking for concat!
301#[doc(hidden)]
302#[macro_export]
David Tolnay04cd0b72019-04-14 13:22:33 -0700303macro_rules! custom_punctuation_concat {
David Tolnayf07f8172019-04-14 13:07:52 -0700304 ($($tt:tt)*) => {
305 concat!($($tt)*)
306 };
307}