tinyparse_macro 0.3.3

Macros for tinyparse
Documentation
use syn::{Expr, parse::Parse, punctuated::Punctuated, Token, parse_macro_input, Generics, GenericParam, parse_quote, spanned::Spanned};
use quote::{quote, quote_spanned, format_ident};

struct Args {
    parsers: Vec<Expr>,
}

impl Parse for Args {
    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
        let exprs = Punctuated::<Expr, Token![,]>::parse_terminated(input)?;
        Ok(Args {
            parsers: exprs.into_iter().collect(),
        })
    }
}

/// Constructs a new parsers that runs the specified parsers in order. The result is then returned in a tuple. This is similar to the `all` method in the tinyparse `common` module.
#[proc_macro]
pub fn seq(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let args = parse_macro_input!(input as Args);

    let mut generics = Generics::default();
    generics.params.push(GenericParam::Lifetime(parse_quote!('a)));
    for i in 0..args.parsers.len() { 
        let ident = format_ident!("R{}", i);
        generics.params.push(GenericParam::Type(parse_quote!(#ident)));
    }
    
    
    let (impl_generics, _, _) = generics.split_for_impl();

    let args_decl = args.parsers.iter().enumerate().map(|(n, parser)| {
        let var_name = format_ident!("p{}", n);
        let var_generic = format_ident!("R{}", n);
        quote_spanned! {parser.span()=>
            #var_name: impl Parse<'a, #var_generic>
        }
    });

    let tup_type_insides = (0..args.parsers.len()).map(|i| {
        let ident = format_ident!("R{}", i);
        ident
    });

    let parse_one = args.parsers.iter().enumerate().map(|(n, parser)| {
        let res_name = format_ident!("r{}", n);
        let parser_name = format_ident!("p{}", n);
        quote_spanned! {parser.span()=>
            let (span, #res_name) = #parser_name.parse(span)?;
        }
    });

    let typ_return = args.parsers.iter().enumerate().map(|(n, _)| {
        format_ident!("r{}", n)
    });

    let call_expr = args.parsers.iter();
    let expanded_fn = quote! {
        {
            fn __seq_impl #impl_generics(#(#args_decl),*) -> impl Parse<'a, (#(#tup_type_insides),*)> {
                move |span: Span<'a>| {
                    #(#parse_one)*

                    Ok((span, (#(#typ_return),*)))
                }
            }
            __seq_impl(#(#call_expr),*)
        }
    };

    proc_macro::TokenStream::from(expanded_fn)
}