nom_packrat_macros/
lib.rs1#![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#[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}