1 /// Define a type that supports parsing and printing a multi-character symbol
2 /// as if it were a punctuation token.
7 /// syn::custom_punctuation!(LeftRightArrow, <=>);
10 /// The generated syntax tree node supports the following operations just like
11 /// any built-in punctuation token.
13 /// - [Peeking] — `input.peek(LeftRightArrow)`
15 /// - [Parsing] — `input.parse::<LeftRightArrow>()?`
17 /// - [Printing] — `quote!( ... #lrarrow ... )`
19 /// - Construction from a [`Span`] — `let lrarrow = LeftRightArrow(sp)`
21 /// - Construction from multiple [`Span`] — `let lrarrow = LeftRightArrow([sp, sp, sp])`
23 /// - Field access to its spans — `let spans = lrarrow.spans`
25 /// [Peeking]: crate::parse::ParseBuffer::peek
26 /// [Parsing]: crate::parse::ParseBuffer::parse
27 /// [Printing]: quote::ToTokens
28 /// [`Span`]: proc_macro2::Span
33 /// use proc_macro2::{TokenStream, TokenTree};
34 /// use syn::parse::{Parse, ParseStream, Peek, Result};
35 /// use syn::punctuated::Punctuated;
38 /// syn::custom_punctuation!(PathSeparator, </>);
40 /// // expr </> expr </> expr ...
41 /// struct PathSegments {
42 /// segments: Punctuated<Expr, PathSeparator>,
45 /// impl Parse for PathSegments {
46 /// fn parse(input: ParseStream) -> Result<Self> {
47 /// let mut segments = Punctuated::new();
49 /// let first = parse_until(input, PathSeparator)?;
50 /// segments.push_value(syn::parse2(first)?);
52 /// while input.peek(PathSeparator) {
53 /// segments.push_punct(input.parse()?);
55 /// let next = parse_until(input, PathSeparator)?;
56 /// segments.push_value(syn::parse2(next)?);
59 /// Ok(PathSegments { segments })
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));
73 /// let input = r#" a::b </> c::d::e "#;
74 /// let _: PathSegments = syn::parse_str(input).unwrap();
78 macro_rules
! custom_punctuation
{
79 ($ident
:ident
, $
($tt
:tt
)+) => {
81 pub spans
: $
crate::custom_punctuation_repr
!($
($tt
)+),
85 #[allow(dead_code, non_snake_case)]
86 pub fn $ident
<__S
: $
crate::__private
::IntoSpans
<$
crate::custom_punctuation_repr
!($
($tt
)+)>>(
89 let _validate_len
= 0 $
(+ $
crate::custom_punctuation_len
!(strict
, $tt
))*;
91 spans
: $
crate::__private
::IntoSpans
::into_spans(spans
)
95 impl $
crate::__private
::Default
for $ident
{
96 fn default() -> Self {
97 $
ident($
crate::__private
::Span
::call_site())
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
)+);
109 #[cfg(feature = "parsing")]
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
)+))
119 fn display() -> &'
static $
crate::__private
::str {
120 concat
!("`", $
crate::stringify_punct
!($
($tt
)+), "`")
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
)+))?
;
135 #[cfg(not(feature = "parsing"))]
138 macro_rules
! impl_parse_for_custom_punctuation
{
139 ($ident
:ident
, $
($tt
:tt
)+) => {}
;
143 #[cfg(feature = "printing")]
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
)
157 #[cfg(not(feature = "printing"))]
160 macro_rules
! impl_to_tokens_for_custom_punctuation
{
161 ($ident
:ident
, $
($tt
:tt
)+) => {}
;
165 #[cfg(feature = "clone-impls")]
168 macro_rules
! impl_clone_for_custom_punctuation
{
169 ($ident
:ident
, $
($tt
:tt
)+) => {
170 impl $
crate::__private
::Copy
for $ident {}
172 #[allow(clippy::expl_impl_clone_on_copy)]
173 impl $
crate::__private
::Clone
for $ident
{
174 fn clone(&self) -> Self {
182 #[cfg(not(feature = "clone-impls"))]
185 macro_rules
! impl_clone_for_custom_punctuation
{
186 ($ident
:ident
, $
($tt
:tt
)+) => {}
;
190 #[cfg(feature = "extra-traits")]
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
))
201 impl $
crate::__private
::Eq
for $ident {}
203 impl $
crate::__private
::PartialEq
for $ident
{
204 fn eq(&self, _other
: &Self) -> $
crate::__private
::bool
{
209 impl $
crate::__private
::Hash
for $ident
{
210 fn hash
<__H
: $
crate::__private
::Hasher
>(&self, _state
: &mut __H
) {}
216 #[cfg(not(feature = "extra-traits"))]
219 macro_rules
! impl_extra_traits_for_custom_punctuation
{
220 ($ident
:ident
, $
($tt
:tt
)+) => {}
;
226 macro_rules
! custom_punctuation_repr
{
228 [$
crate::__private
::Span
; 0 $
(+ $
crate::custom_punctuation_len
!(lenient
, $tt
))+]
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 }
};
289 macro_rules
! custom_punctuation_unexpected
{
296 macro_rules
! stringify_punct
{
298 concat
!($
(stringify
!($tt
)),+)