use proc_macro::TokenStream;
use quote::quote;
use syn::parse_macro_input;
#[proc_macro]
pub fn iter(item: TokenStream) -> TokenStream {
let comp = parse_macro_input!(item as Comprehension);
let body = comp.body;
let mut ret = quote! {
std::iter::once(#body)
};
for q in comp.quals.iter().rev() {
match q {
Qual::Generator(pat, iter) => {
ret = quote! {
(#iter).into_iter().flat_map(move |#pat| #ret)
};
}
Qual::LocalDecl(expr_let) => {
ret = quote! {
{
#expr_let;
#ret
}
};
}
Qual::Guard(pred) => {
ret = quote! {
std::iter::once(())
.take(if #pred {1} else {0})
.flat_map(move |_| #ret)
}
}
}
}
ret.into()
}
struct Comprehension {
body: syn::Expr,
quals: Vec<Qual>,
}
enum Qual {
Generator(syn::Pat, syn::Expr),
LocalDecl(syn::ExprLet),
Guard(syn::Expr),
}
impl syn::parse::Parse for Comprehension {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
use syn::{punctuated::Punctuated, Token};
let body = input.parse()?;
input.parse::<syn::Token![;]>()?;
let quals = Punctuated::<Qual, Token![,]>::parse_terminated(input)?
.into_iter()
.collect();
Ok(Comprehension { body, quals })
}
}
impl syn::parse::Parse for Qual {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
parse_generator(input)
.or_else(|_| parse_local_decl(input))
.or_else(|_| parse_guard(input))
}
}
fn parse_generator(input: syn::parse::ParseStream) -> syn::Result<Qual> {
if {
let input = input.fork();
input
.parse::<syn::Pat>()
.and_then(|_| input.parse::<syn::Token![<-]>())
.is_ok()
} {
let pat = input.parse()?;
input.parse::<syn::Token![<-]>()?;
let expr = input.parse()?;
Ok(Qual::Generator(pat, expr))
} else {
Err(syn::Error::new(input.span(), "expect pat"))
}
}
fn parse_local_decl(input: syn::parse::ParseStream) -> syn::Result<Qual> {
if input.peek(syn::Token![let]) {
input.parse().map(Qual::LocalDecl)
} else {
Err(syn::Error::new(input.span(), "expect `let`"))
}
}
fn parse_guard(input: syn::parse::ParseStream) -> syn::Result<Qual> {
input.parse().map(Qual::Guard)
}
#[proc_macro]
pub fn vect(item: TokenStream) -> TokenStream {
let body: proc_macro2::TokenStream = iter(item).into();
let ret = quote! {
#body.collect::<Vec<_>>()
};
ret.into()
}
#[proc_macro]
pub fn sum(item: TokenStream) -> TokenStream {
let body: proc_macro2::TokenStream = iter(item).into();
let ret = quote! {
{
fn sum_helper<T, I>(it: I) -> T
where
T: std::iter::Sum<T>,
I: Iterator<Item = T>,
{
it.sum()
}
sum_helper(#body)
}
};
ret.into()
}
#[proc_macro]
pub fn product(item: TokenStream) -> TokenStream {
let body: proc_macro2::TokenStream = iter(item).into();
let ret = quote! {
{
fn product_helper<T, I>(it: I) -> T
where
T: std::iter::Product<T>,
I: Iterator<Item = T>,
{
it.product()
}
product_helper(#body)
}
};
ret.into()
}