nom_packrat_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 packrat parser
10#[proc_macro_attribute]
11pub fn packrat_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_packrat_parser(&attr, &item)
15}
16
17fn impl_packrat_parser(_attr: &AttributeArgs, item: &ItemFn) -> TokenStream {
18    let before = impl_packrat_parser_bofore(&item);
19    let body = impl_packrat_parser_body(&item);
20    let after = impl_packrat_parser_after(&item);
21
22    let mut item = item.clone();
23
24    item.block.stmts.clear();
25    item.block.stmts.push(before);
26    item.block.stmts.push(body);
27    item.block.stmts.push(after);
28
29    item.into_token_stream().into()
30}
31
32fn impl_packrat_parser_bofore(item: &ItemFn) -> Stmt {
33    let ident = &item.sig.ident;
34
35    let input = if let Some(x) = &item.sig.inputs.first() {
36        match x {
37            FnArg::Typed(arg) => &arg.pat,
38            _ => panic!("function with #[packrat_parser] must have an argument"),
39        }
40    } else {
41        panic!("function with #[packrat_parser] must have an argument");
42    };
43
44    parse_quote! {
45        let org_input = if let Some(x) = crate::PACKRAT_STORAGE.with(|storage| {
46            use nom::AsBytes;
47            use nom_packrat::HasExtraState;
48            let ptr = #input.as_bytes().as_ptr();
49            let extra = #input.get_extra_state();
50            if let Some(x) = storage.borrow_mut().get(&(stringify!(#ident), ptr, extra)) {
51                if let Some((x, y)) = x {
52                    return Some(Some((x.clone(), *y)))
53                } else {
54                    return Some(None)
55                }
56            } else {
57                return None
58            }
59        }) {
60            if let Some((x, y)) = x {
61                use nom::InputTake;
62                let (s, _) = #input.take_split(y);
63                use std::convert::TryInto;
64                let x = x.try_into().map_err(|_| nom::Err::Error(nom::error::make_error(#input, nom::error::ErrorKind::Fix)))?;
65                #[cfg(feature = "trace")]
66                {
67                    use nom_tracable::Tracable;
68                    nom_tracable::custom_trace(&#input, stringify!(#ident), "packrat cache hit (accepted)", "\u{001b}[1;33m")
69                };
70                return Ok((s, x))
71            } else {
72                #[cfg(feature = "trace")]
73                {
74                    use nom_tracable::Tracable;
75                    nom_tracable::custom_trace(&#input, stringify!(#ident), "packrat cache hit (rejected)", "\u{001b}[1;33m")
76                };
77                return Err(nom::Err::Error(nom::error::make_error(#input, nom::error::ErrorKind::Fix)));
78            }
79        } else {
80            #input
81        };
82    }
83}
84
85fn impl_packrat_parser_body(item: &ItemFn) -> Stmt {
86    let body = item.block.as_ref();
87    parse_quote! {
88        let body_ret = {
89            let body = || { #body };
90            body()
91        };
92    }
93}
94
95fn impl_packrat_parser_after(item: &ItemFn) -> Stmt {
96    let ident = &item.sig.ident;
97
98    parse_quote! {
99        {
100            use nom::AsBytes;
101            use nom_packrat::HasExtraState;
102            let ptr = org_input.as_bytes().as_ptr();
103            let extra = org_input.get_extra_state();
104            if let Ok((s, x)) = &body_ret {
105                use nom::Offset;
106                let len = org_input.offset(&s);
107                crate::PACKRAT_STORAGE.with(|storage| {
108                    storage.borrow_mut().insert((stringify!(#ident), ptr, extra), Some(((*x).clone().into(), len)));
109                });
110                #[cfg(feature = "trace")]
111                {
112                    use nom_tracable::Tracable;
113                    nom_tracable::custom_trace(&org_input, stringify!(#ident), "packrat cache store (accepted)", "\u{001b}[1;33m");
114                };
115            } else {
116                crate::PACKRAT_STORAGE.with(|storage| {
117                    storage.borrow_mut().insert((stringify!(#ident), ptr, extra), None);
118                });
119                #[cfg(feature = "trace")]
120                {
121                    use nom_tracable::Tracable;
122                    nom_tracable::custom_trace(&org_input, stringify!(#ident), "packrat cache store (rejected)", "\u{001b}[1;33m");
123                };
124            }
125            body_ret
126        }
127    }
128}