zng_var_proc_macros/
expr_var.rsuse proc_macro2::{Group, TokenStream, TokenTree};
use quote::ToTokens;
use syn::{
parse::{Parse, ParseStream},
parse2, parse_macro_input,
spanned::Spanned,
token, Expr, Ident, Path, Token,
};
use crate::util::{token_stream_eq, tokens_to_ident_str};
pub fn expand(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let VarExpr { mod_, vars, expr } = parse_macro_input!(input as VarExpr);
let r = if vars.is_empty() {
if parse2::<Expr>(expr.clone()).is_ok() {
quote_spanned! {expr.span()=>
#mod_::types::expr_var_into(#expr)
}
} else {
quote_spanned! {expr.span()=>
#mod_::types::expr_var_into({#expr})
}
}
} else if vars.len() == 1 {
let (ident, eval) = &vars[0];
if token_stream_eq(expr.clone(), quote!(#ident)) || token_stream_eq(expr.clone(), quote!(*#ident)) {
quote_spanned! {expr.span()=>
#mod_::types::expr_var_as(#eval)
}
} else {
quote_spanned! {expr.span()=>
#mod_::types::expr_var_map(#eval, move |#[allow(non_snake_case)]#ident|{ #expr })
}
}
} else {
let idents = vars.iter().map(|(id, _)| id);
let evals = vars.iter().map(|(_, ev)| ev);
quote_spanned! {expr.span()=>
#mod_::types::expr_var_as(
#mod_::merge_var!{ #({#evals}),* , move |#(#[allow(non_snake_case)]#idents),*| { #expr } }
)
}
};
r.into()
}
struct VarExpr {
mod_: Path,
vars: Vec<(Ident, TokenStream)>,
expr: TokenStream,
}
impl Parse for VarExpr {
fn parse(input: ParseStream) -> syn::Result<Self> {
let mod_ = input.parse().unwrap_or_else(|e| non_user_error!(e));
input.parse::<Token![,]>().unwrap_or_else(|e| non_user_error!(e));
let mut vars = vec![];
let expr = parse_replace_expr(input, &mut vars);
Ok(VarExpr { mod_, vars, expr })
}
}
fn parse_replace_expr(input: ParseStream, vars: &mut Vec<(Ident, TokenStream)>) -> TokenStream {
let mut expr = TokenStream::default();
while !input.is_empty() {
if input.peek(Token![#]) && input.peek2(token::Brace) {
input.parse::<Token![#]>().unwrap();
let var = input.parse::<Group>().unwrap().stream();
if let Some((var_ident, _)) = vars.iter().find(|(_, v)| token_stream_eq(v.clone(), var.clone())) {
var_ident.to_tokens(&mut expr)
} else {
let var_ident = ident_spanned!(var.span()=> "__{}_{}", vars.len(), tokens_to_ident_str(&var));
var_ident.to_tokens(&mut expr);
vars.push((var_ident, var))
}
}
else if input.peek(token::Brace) {
assert_group(|| {
let inner;
let group = syn::braced!(inner in input);
let inner = parse_replace_expr(&inner, vars);
group.surround(&mut expr, |e| e.extend(inner));
Ok(())
});
} else if input.peek(token::Paren) {
assert_group(|| {
let inner;
let group = syn::parenthesized!(inner in input);
let inner = parse_replace_expr(&inner, vars);
group.surround(&mut expr, |e| e.extend(inner));
Ok(())
});
} else if input.peek(token::Bracket) {
assert_group(|| {
let inner;
let group = syn::bracketed!(inner in input);
let inner = parse_replace_expr(&inner, vars);
group.surround(&mut expr, |e| e.extend(inner));
Ok(())
});
}
else {
let tt = input.parse::<TokenTree>().unwrap();
tt.to_tokens(&mut expr)
}
}
expr
}
fn assert_group(f: impl FnOnce() -> syn::parse::Result<()>) {
f().unwrap()
}