Macro syn::custom_punctuation

source ·
macro_rules! custom_punctuation {
    ($ident:ident, $($tt:tt)+) => { ... };
}
Expand description

Define a type that supports parsing and printing a multi-character symbol as if it were a punctuation token.

§Usage

syn::custom_punctuation!(LeftRightArrow, <=>);

The generated syntax tree node supports the following operations just like any built-in punctuation token.

  • Peekinginput.peek(LeftRightArrow)

  • Parsinginput.parse::<LeftRightArrow>()?

  • Printingquote!( ... #lrarrow ... )

  • Construction from a Spanlet lrarrow = LeftRightArrow(sp)

  • Construction from multiple Spanlet lrarrow = LeftRightArrow([sp, sp, sp])

  • Field access to its spans — let spans = lrarrow.spans

§Example

use proc_macro2::{TokenStream, TokenTree};
use syn::parse::{Parse, ParseStream, Peek, Result};
use syn::punctuated::Punctuated;
use syn::Expr;

syn::custom_punctuation!(PathSeparator, </>);

// expr </> expr </> expr ...
struct PathSegments {
    segments: Punctuated<Expr, PathSeparator>,
}

impl Parse for PathSegments {
    fn parse(input: ParseStream) -> Result<Self> {
        let mut segments = Punctuated::new();

        let first = parse_until(input, PathSeparator)?;
        segments.push_value(syn::parse2(first)?);

        while input.peek(PathSeparator) {
            segments.push_punct(input.parse()?);

            let next = parse_until(input, PathSeparator)?;
            segments.push_value(syn::parse2(next)?);
        }

        Ok(PathSegments { segments })
    }
}

fn parse_until<E: Peek>(input: ParseStream, end: E) -> Result<TokenStream> {
    let mut tokens = TokenStream::new();
    while !input.is_empty() && !input.peek(end) {
        let next: TokenTree = input.parse()?;
        tokens.extend(Some(next));
    }
    Ok(tokens)
}

fn main() {
    let input = r#" a::b </> c::d::e "#;
    let _: PathSegments = syn::parse_str(input).unwrap();
}