use proc_macro2::{Group, TokenStream, TokenTree};
use syn::parse::Parser;
use syn::punctuated::Punctuated;
use syn::{Expr, Token};
use crate::parse::SingletonRef;
pub fn preprocess_singletons(tokens: TokenStream, found: &mut Vec<SingletonRef>) -> TokenStream {
process_singletons(tokens, &mut |ref_token| {
let ident = ref_token.ident.clone();
found.push(ref_token);
TokenTree::Ident(ident)
})
}
pub fn postprocess_singletons(
tokens: TokenStream,
resolved_exprs: impl IntoIterator<Item = TokenStream>,
) -> Punctuated<Expr, Token![,]> {
let mut resolved_exprs_iter = resolved_exprs.into_iter();
let processed = process_singletons(tokens, &mut |_ref_token| {
let span = _ref_token.ident.span();
let expr_tokens = resolved_exprs_iter.next().unwrap();
let mut group = Group::new(proc_macro2::Delimiter::Parenthesis, expr_tokens);
group.set_span(span);
TokenTree::Group(group)
});
Punctuated::parse_terminated.parse2(processed).unwrap()
}
fn process_singletons(
tokens: TokenStream,
map_singleton_fn: &mut impl FnMut(SingletonRef) -> TokenTree,
) -> TokenStream {
let mut iter = tokens.into_iter().peekable();
std::iter::from_fn(|| {
let out = match iter.peek()? {
TokenTree::Group(group) => {
let mut new_group = Group::new(
group.delimiter(),
process_singletons(group.stream(), map_singleton_fn),
);
new_group.set_span(group.span());
let _ = iter.next().unwrap(); TokenTree::Group(new_group)
}
TokenTree::Punct(punct) if '#' == punct.as_char() => {
let tokens = iter.by_ref().collect::<TokenStream>();
let (opt_singleton, tokens_rest) = SingletonRef::try_parse
.parse2(tokens)
.expect("bug: should be infallible");
iter = tokens_rest.into_iter().peekable();
if let Some(singleton) = opt_singleton {
(map_singleton_fn)(singleton)
} else {
iter.next().unwrap()
}
}
_ => iter.next().unwrap(),
};
Some(out)
})
.collect()
}