blob: 8d7d4443f8ab73d6d61dde6c397374919e8932c8 [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
David Tolnay01c04102019-04-14 14:01:34 -070028/// [`Span`]: https://docs.rs/proc-macro2/0.4/proc_macro2/struct.Span.html
David Tolnay9bfd1122019-04-14 12:43:31 -070029///
30/// # Example
31///
32/// ```edition2018
David Tolnaye2584712019-04-14 13:45:53 -070033/// use proc_macro2::{TokenStream, TokenTree};
34/// use syn::parse::{Parse, ParseStream, Peek, Result};
David Tolnay98880402019-04-14 13:25:32 -070035/// use syn::punctuated::Punctuated;
David Tolnaye2584712019-04-14 13:45:53 -070036/// use syn::Expr;
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///
David Tolnaye2584712019-04-14 13:45:53 -070040/// // expr </> expr </> expr ...
David Tolnay9bfd1122019-04-14 12:43:31 -070041/// 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///
David Tolnaye2584712019-04-14 13:45:53 -070049/// let first = parse_until(input, PathSeparator)?;
50/// segments.push_value(syn::parse2(first)?);
David Tolnay9bfd1122019-04-14 12:43:31 -070051///
David Tolnay98880402019-04-14 13:25:32 -070052/// while input.peek(PathSeparator) {
David Tolnay9bfd1122019-04-14 12:43:31 -070053/// segments.push_punct(input.parse()?);
David Tolnaye2584712019-04-14 13:45:53 -070054///
55/// let next = parse_until(input, PathSeparator)?;
56/// segments.push_value(syn::parse2(next)?);
David Tolnay9bfd1122019-04-14 12:43:31 -070057/// }
58///
59/// Ok(PathSegments { segments })
60/// }
61/// }
62///
David Tolnaye2584712019-04-14 13:45:53 -070063/// 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///
David Tolnay98880402019-04-14 13:25:32 -070072/// fn main() {
David Tolnaye2584712019-04-14 13:45:53 -070073/// let input = r#" a::b </> c::d::e "#;
David Tolnay98880402019-04-14 13:25:32 -070074/// let _: PathSegments = syn::parse_str(input).unwrap();
75/// }
David Tolnay9bfd1122019-04-14 12:43:31 -070076/// ```
David Tolnay9bfd1122019-04-14 12:43:31 -070077#[macro_export(local_inner_macros)]
78macro_rules! custom_punctuation {
79 ($ident:ident, $($tt:tt)+) => {
80 pub struct $ident {
David Tolnay76aabde2019-04-14 13:18:33 -070081 pub spans: custom_punctuation_repr!($($tt)+),
David Tolnay9bfd1122019-04-14 12:43:31 -070082 }
83
84 #[doc(hidden)]
85 #[allow(non_snake_case)]
David Tolnay76aabde2019-04-14 13:18:33 -070086 pub fn $ident<__S: $crate::export::IntoSpans<custom_punctuation_repr!($($tt)+)>>(
David Tolnay9bfd1122019-04-14 12:43:31 -070087 spans: __S,
88 ) -> $ident {
David Tolnay76aabde2019-04-14 13:18:33 -070089 let _validate_len = 0 $(+ custom_punctuation_len!(strict, $tt))*;
David Tolnay9bfd1122019-04-14 12:43:31 -070090 $ident {
91 spans: $crate::export::IntoSpans::into_spans(spans)
92 }
93 }
94
95 impl $crate::export::Default for $ident {
96 fn default() -> Self {
97 $ident($crate::export::Span::call_site())
98 }
99 }
100
101 impl_parse_for_custom_punctuation!($ident, $($tt)+);
102 impl_to_tokens_for_custom_punctuation!($ident, $($tt)+);
103 impl_clone_for_custom_punctuation!($ident, $($tt)+);
104 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(local_inner_macros)]
112macro_rules! impl_parse_for_custom_punctuation {
David Tolnay3c557672019-04-14 13:07:01 -0700113 ($ident:ident, $($tt:tt)+) => {
David Tolnay9bfd1122019-04-14 12:43:31 -0700114 impl $crate::token::CustomToken for $ident {
115 fn peek(cursor: $crate::buffer::Cursor) -> bool {
116 $crate::token::parsing::peek_punct(cursor, stringify_punct!($($tt)+))
117 }
118
119 fn display() -> &'static $crate::export::str {
David Tolnay04cd0b72019-04-14 13:22:33 -0700120 custom_punctuation_concat!("`", stringify_punct!($($tt)+), "`")
David Tolnay9bfd1122019-04-14 12:43:31 -0700121 }
122 }
123
124 impl $crate::parse::Parse for $ident {
125 fn parse(input: $crate::parse::ParseStream) -> $crate::parse::Result<$ident> {
David Tolnay76aabde2019-04-14 13:18:33 -0700126 let spans: custom_punctuation_repr!($($tt)+) =
David Tolnay9bfd1122019-04-14 12:43:31 -0700127 $crate::token::parsing::punct(input, 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]
138macro_rules! impl_parse_for_custom_punctuation {
David Tolnay3c557672019-04-14 13:07:01 -0700139 ($ident:ident, $($tt:tt)+) => {};
David Tolnay9bfd1122019-04-14 12:43:31 -0700140}
141
142// Not public API.
143#[cfg(feature = "printing")]
144#[doc(hidden)]
145#[macro_export(local_inner_macros)]
146macro_rules! impl_to_tokens_for_custom_punctuation {
David Tolnay3c557672019-04-14 13:07:01 -0700147 ($ident:ident, $($tt:tt)+) => {
David Tolnay9bfd1122019-04-14 12:43:31 -0700148 impl $crate::export::ToTokens for $ident {
149 fn to_tokens(&self, tokens: &mut $crate::export::TokenStream2) {
150 $crate::token::printing::punct(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]
160macro_rules! impl_to_tokens_for_custom_punctuation {
David Tolnay3c557672019-04-14 13:07:01 -0700161 ($ident:ident, $($tt:tt)+) => {};
David Tolnay9bfd1122019-04-14 12:43:31 -0700162}
163
164// Not public API.
165#[cfg(feature = "clone-impls")]
166#[doc(hidden)]
167#[macro_export]
168macro_rules! impl_clone_for_custom_punctuation {
David Tolnay3c557672019-04-14 13:07:01 -0700169 ($ident:ident, $($tt:tt)+) => {
David Tolnay9bfd1122019-04-14 12:43:31 -0700170 impl $crate::export::Copy for $ident {}
171
172 impl $crate::export::Clone for $ident {
173 fn clone(&self) -> Self {
174 *self
175 }
176 }
177 };
178}
179
180// Not public API.
181#[cfg(not(feature = "clone-impls"))]
182#[doc(hidden)]
183#[macro_export]
184macro_rules! impl_clone_for_custom_punctuation {
David Tolnay3c557672019-04-14 13:07:01 -0700185 ($ident:ident, $($tt:tt)+) => {};
David Tolnay9bfd1122019-04-14 12:43:31 -0700186}
187
188// Not public API.
189#[cfg(feature = "extra-traits")]
190#[doc(hidden)]
David Tolnay5ac27f12019-04-14 13:55:45 -0700191#[macro_export]
David Tolnay9bfd1122019-04-14 12:43:31 -0700192macro_rules! impl_extra_traits_for_custom_punctuation {
David Tolnay3c557672019-04-14 13:07:01 -0700193 ($ident:ident, $($tt:tt)+) => {
David Tolnay9bfd1122019-04-14 12:43:31 -0700194 impl $crate::export::Debug for $ident {
195 fn fmt(&self, f: &mut $crate::export::Formatter) -> $crate::export::fmt::Result {
David Tolnay5ac27f12019-04-14 13:55:45 -0700196 $crate::export::Formatter::write_str(f, stringify!($ident))
David Tolnay9bfd1122019-04-14 12:43:31 -0700197 }
198 }
199
200 impl $crate::export::Eq for $ident {}
201
202 impl $crate::export::PartialEq for $ident {
203 fn eq(&self, _other: &Self) -> $crate::export::bool {
204 true
205 }
206 }
207
208 impl $crate::export::Hash for $ident {
209 fn hash<__H: $crate::export::Hasher>(&self, _state: &mut __H) {}
210 }
211 };
212}
213
214// Not public API.
215#[cfg(not(feature = "extra-traits"))]
216#[doc(hidden)]
217#[macro_export]
218macro_rules! impl_extra_traits_for_custom_punctuation {
David Tolnay3c557672019-04-14 13:07:01 -0700219 ($ident:ident, $($tt:tt)+) => {};
David Tolnay9bfd1122019-04-14 12:43:31 -0700220}
David Tolnayf07f8172019-04-14 13:07:52 -0700221
222// Not public API.
223#[doc(hidden)]
224#[macro_export(local_inner_macros)]
David Tolnay76aabde2019-04-14 13:18:33 -0700225macro_rules! custom_punctuation_repr {
226 ($($tt:tt)+) => {
227 [$crate::export::Span; 0 $(+ custom_punctuation_len!(lenient, $tt))+]
228 };
229}
230
231// Not public API.
232#[doc(hidden)]
233#[macro_export(local_inner_macros)]
David Tolnayd8a47212019-04-14 13:59:22 -0700234#[cfg_attr(rustfmt, rustfmt_skip)]
David Tolnay76aabde2019-04-14 13:18:33 -0700235macro_rules! custom_punctuation_len {
David Tolnayf07f8172019-04-14 13:07:52 -0700236 ($mode:ident, +) => { 1 };
237 ($mode:ident, +=) => { 2 };
238 ($mode:ident, &) => { 1 };
239 ($mode:ident, &&) => { 2 };
240 ($mode:ident, &=) => { 2 };
241 ($mode:ident, @) => { 1 };
242 ($mode:ident, !) => { 1 };
243 ($mode:ident, ^) => { 1 };
244 ($mode:ident, ^=) => { 2 };
245 ($mode:ident, :) => { 1 };
246 ($mode:ident, ::) => { 2 };
247 ($mode:ident, ,) => { 1 };
248 ($mode:ident, /) => { 1 };
249 ($mode:ident, /=) => { 2 };
250 ($mode:ident, .) => { 1 };
251 ($mode:ident, ..) => { 2 };
252 ($mode:ident, ...) => { 3 };
253 ($mode:ident, ..=) => { 3 };
254 ($mode:ident, =) => { 1 };
255 ($mode:ident, ==) => { 2 };
256 ($mode:ident, >=) => { 2 };
257 ($mode:ident, >) => { 1 };
258 ($mode:ident, <=) => { 2 };
259 ($mode:ident, <) => { 1 };
260 ($mode:ident, *=) => { 2 };
261 ($mode:ident, !=) => { 2 };
262 ($mode:ident, |) => { 1 };
263 ($mode:ident, |=) => { 2 };
264 ($mode:ident, ||) => { 2 };
265 ($mode:ident, #) => { 1 };
266 ($mode:ident, ?) => { 1 };
267 ($mode:ident, ->) => { 2 };
268 ($mode:ident, <-) => { 2 };
269 ($mode:ident, %) => { 1 };
270 ($mode:ident, %=) => { 2 };
271 ($mode:ident, =>) => { 2 };
272 ($mode:ident, ;) => { 1 };
273 ($mode:ident, <<) => { 2 };
274 ($mode:ident, <<=) => { 3 };
275 ($mode:ident, >>) => { 2 };
276 ($mode:ident, >>=) => { 3 };
277 ($mode:ident, *) => { 1 };
278 ($mode:ident, -) => { 1 };
279 ($mode:ident, -=) => { 2 };
280 ($mode:ident, ~) => { 1 };
281 (lenient, $tt:tt) => { 0 };
David Tolnay04cd0b72019-04-14 13:22:33 -0700282 (strict, $tt:tt) => {{ custom_punctuation_unexpected!($tt); 0 }};
David Tolnayf07f8172019-04-14 13:07:52 -0700283}
284
285// Not public API.
286#[doc(hidden)]
287#[macro_export]
David Tolnay04cd0b72019-04-14 13:22:33 -0700288macro_rules! custom_punctuation_unexpected {
David Tolnayf07f8172019-04-14 13:07:52 -0700289 () => {};
290}
291
292// Not public API.
293#[doc(hidden)]
294#[macro_export]
295macro_rules! stringify_punct {
296 ($($tt:tt)+) => {
297 concat!($(stringify!($tt)),+)
298 };
299}
300
301// Not public API.
302// Without this, local_inner_macros breaks when looking for concat!
303#[doc(hidden)]
304#[macro_export]
David Tolnay04cd0b72019-04-14 13:22:33 -0700305macro_rules! custom_punctuation_concat {
David Tolnayf07f8172019-04-14 13:07:52 -0700306 ($($tt:tt)*) => {
307 concat!($($tt)*)
308 };
309}