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
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#![recursion_limit = "128"]

extern crate proc_macro;

use crate::proc_macro::TokenStream;
use quote::{quote, ToTokens};
use syn::{self, parse_macro_input, parse_quote, AttributeArgs, FnArg, ItemFn, Stmt, Type};

/// Declare packrat storage
///
/// # Arguments
/// * An output type of parser. The type must implement `Clone`.
///
/// # Examples
///
/// ```compile_fail
/// storage!(String);
/// ```
#[proc_macro]
pub fn storage(item: TokenStream) -> TokenStream {
    let t = parse_macro_input!(item as Type);
    let gen = quote! {
        thread_local!(
            pub(crate) static PACKRAT_STORAGE: core::cell::RefCell<
                std::collections::HashMap<(&'static str, *const u8), Option<(#t, usize)>>
            > = {
                core::cell::RefCell::new(std::collections::HashMap::new())
            }
        );
    };
    gen.into()
}

/// Custom attribute for packrat parser
#[proc_macro_attribute]
pub fn packrat_parser(attr: TokenStream, item: TokenStream) -> TokenStream {
    let attr = parse_macro_input!(attr as AttributeArgs);
    let item = parse_macro_input!(item as ItemFn);
    impl_packrat_parser(&attr, &item)
}

fn impl_packrat_parser(_attr: &AttributeArgs, item: &ItemFn) -> TokenStream {
    let before = impl_packrat_parser_bofore(&item);
    let body = impl_packrat_parser_body(&item);
    let after = impl_packrat_parser_after(&item);

    let mut item = item.clone();

    item.block.stmts.clear();
    item.block.stmts.push(before);
    item.block.stmts.push(body);
    item.block.stmts.push(after);

    item.into_token_stream().into()
}

fn impl_packrat_parser_bofore(item: &ItemFn) -> Stmt {
    let ident = &item.ident;

    let input = if let Some(x) = &item.decl.inputs.first() {
        match x.value() {
            FnArg::Captured(arg) => &arg.pat,
            _ => panic!("function with #[packrat_parser] must have an argument"),
        }
    } else {
        panic!("function with #[packrat_parser] must have an argument");
    };

    parse_quote! {
        let org_input = if let Some(x) = crate::PACKRAT_STORAGE.with(|storage| {
            use nom::AsBytes;
            if let Some(x) = storage.borrow_mut().get(&(stringify!(#ident), #input.as_bytes().as_ptr())) {
                if let Some((x, y)) = x {
                    return Some(Some((x.clone(), *y)))
                } else {
                    return Some(None)
                }
            } else {
                return None
            }
        }) {
            if let Some((x, y)) = x {
                use nom::InputTake;
                let (s, _) = #input.take_split(y);
                use std::convert::TryInto;
                let x = x.try_into().map_err(|_| nom::Err::Error(nom::error::make_error(#input, nom::error::ErrorKind::Fix)))?;
                return Ok((s, x))
            } else {
                return Err(nom::Err::Error(nom::error::make_error(#input, nom::error::ErrorKind::Fix)));
            }
        } else {
            #input
        };
    }
}

fn impl_packrat_parser_body(item: &ItemFn) -> Stmt {
    let body = item.block.as_ref();
    parse_quote! {
        let body_ret = {
            let body = || { #body };
            body()
        };
    }
}

fn impl_packrat_parser_after(item: &ItemFn) -> Stmt {
    let ident = &item.ident;

    parse_quote! {
        {
            use nom::AsBytes;
            let ptr = org_input.as_bytes().as_ptr();
            if let Ok((s, x)) = &body_ret {
                use nom::Offset;
                let len = org_input.offset(&s);
                crate::PACKRAT_STORAGE.with(|storage| {
                    storage.borrow_mut().insert((stringify!(#ident), ptr), Some(((*x).clone().into(), len)));
                });
            } else {
                crate::PACKRAT_STORAGE.with(|storage| {
                    storage.borrow_mut().insert((stringify!(#ident), ptr), None);
                });
            }
            body_ret
        }
    }
}