1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
#![recursion_limit = "128"] extern crate proc_macro; use crate::proc_macro::TokenStream; use quote::ToTokens; use syn::{self, parse_macro_input, parse_quote, AttributeArgs, FnArg, ItemFn, Stmt}; #[proc_macro_attribute] pub fn tracable_parser(attr: TokenStream, item: TokenStream) -> TokenStream { let attr = parse_macro_input!(attr as AttributeArgs); let item = parse_macro_input!(item as ItemFn); impl_tracable_parser(&attr, &item) } fn impl_tracable_parser(_attr: &AttributeArgs, item: &ItemFn) -> TokenStream { let default = impl_tracable_parser_default(&item); let trace = impl_tracable_parser_trace(&item); let mut item = item.clone(); item.block.stmts.clear(); item.block.stmts.push(default); item.block.stmts.push(trace); item.into_token_stream().into() } fn impl_tracable_parser_default(item: &ItemFn) -> Stmt { let body = item.block.as_ref(); parse_quote! { #[cfg(not(feature = "trace"))] { #body } } } fn impl_tracable_parser_trace(item: &ItemFn) -> Stmt { let ident = &item.sig.ident; let input = if let Some(x) = &item.sig.inputs.first() { match x { FnArg::Typed(arg) => &arg.pat, _ => panic!("function with #[tracable_parser] must have an argument"), } } else { panic!("function with #[tracable_parser] must have an argument"); }; let body = item.block.as_ref(); parse_quote! { #[cfg(feature = "trace")] { let (depth, #input) = nom_tracable::forward_trace(#input, stringify!(#ident)); let body_ret = { let body = || { #body }; body() }; nom_tracable::backward_trace(body_ret, stringify!(#ident), depth) } } }