nom_recursive_macros/
lib.rs

1#![recursion_limit = "128"]
2
3extern crate proc_macro;
4
5use crate::proc_macro::TokenStream;
6use quote::ToTokens;
7use syn::{self, parse_macro_input, parse_quote, AttributeArgs, FnArg, ItemFn, Stmt};
8
9/// Custom attribute for recursive parser
10#[proc_macro_attribute]
11pub fn recursive_parser(attr: TokenStream, item: TokenStream) -> TokenStream {
12    let attr = parse_macro_input!(attr as AttributeArgs);
13    let item = parse_macro_input!(item as ItemFn);
14    impl_recursive_parser(&attr, &item)
15}
16
17fn impl_recursive_parser(_attr: &AttributeArgs, item: &ItemFn) -> TokenStream {
18    let before = impl_recursive_parser_bofore(&item);
19    let body = impl_recursive_parser_body(&item);
20
21    let mut item = item.clone();
22
23    item.block.stmts.clear();
24    item.block.stmts.push(before);
25    item.block.stmts.push(body);
26
27    item.into_token_stream().into()
28}
29
30fn impl_recursive_parser_bofore(item: &ItemFn) -> Stmt {
31    let ident = &item.sig.ident;
32
33    let input = if let Some(x) = &item.sig.inputs.first() {
34        match x {
35            FnArg::Typed(arg) => &arg.pat,
36            _ => panic!("function with #[recursive_parser] must have an argument"),
37        }
38    } else {
39        panic!("function with #[recursive_parser] must have an argument");
40    };
41
42    parse_quote! {
43        let #input = {
44            let id = nom_recursive::RECURSIVE_STORAGE.with(|storage| {
45                storage.borrow_mut().get(stringify!(#ident))
46            });
47            use nom_recursive::HasRecursiveInfo;
48            let mut info = #input.get_recursive_info();
49
50            use nom::AsBytes;
51            let ptr = #input.as_bytes().as_ptr();
52
53            if ptr != info.get_ptr() {
54                #[cfg(feature = "trace")]
55                {
56                    use nom_tracable::Tracable;
57                    nom_tracable::custom_trace(&#input, stringify!(#ident), "recursion flag clear", "\u{001b}[1;36m")
58                };
59                info.clear_flags();
60                info.set_ptr(ptr);
61            }
62
63            if info.check_flag(id) {
64                #[cfg(feature = "trace")]
65                {
66                    use nom_tracable::Tracable;
67                    nom_tracable::custom_trace(&#input, stringify!(#ident), "recursion detected", "\u{001b}[1;36m")
68                };
69                return Err(nom::Err::Error(nom::error::make_error(#input, nom::error::ErrorKind::Fix)));
70            }
71            #[cfg(feature = "trace")]
72            {
73                use nom_tracable::Tracable;
74                nom_tracable::custom_trace(&#input, stringify!(#ident), "recursion flag set", "\u{001b}[1;36m")
75            };
76            info.set_flag(id);
77
78            #input.set_recursive_info(info)
79        };
80    }
81}
82
83fn impl_recursive_parser_body(item: &ItemFn) -> Stmt {
84    let body = item.block.as_ref();
85    parse_quote! {
86        #body
87    }
88}