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
extern crate proc_macro;

use self::proc_macro::{TokenStream as TokenStream1, TokenTree};
use proc_macro2::TokenStream;
use proc_macro_error::proc_macro_error;
use quote::quote;

mod chains;
mod check;
mod file;
mod patterns;
mod script;
mod token;

use crate::{
    chains::ChainIter,
    check::Checker,
    patterns::{all, caps},
    script::Script,
};

use proc_macro_hack::proc_macro_hack;

fn to_ident(tt: &TokenTree) -> TokenStream {
    let s: TokenStream1 = tt.clone().into();
    s.into()
}

fn gen_all(script: &Script) -> TokenStream {
    let mut s = TokenStream::new();

    let chains = ChainIter::new(script);
    let pchains = chains.pchains();

    for c in chains {
        s.extend(c.gen());
    }

    for c in pchains {
        s.extend(c.gen());
    }

    s
}

#[proc_macro_error]
#[proc_macro_hack]
pub fn lua(input: TokenStream1) -> TokenStream1 {
    let script = Script::new(input, true);

    Checker::new()
        .defines(all(&script).map(|(_, arg)| arg.as_lua().into()).collect())
        .check(&script);

    let defs = gen_all(&script);

    let body_str = script.script();
    let script_str = script.wrap();

    let args = all(&script).map(|(_, arg)| {
        let arg = arg.as_lua().to_string();
        quote! { #arg }
    });

    let caps = caps(&script).map(|(_, arg)| {
        let arg = to_ident(arg.as_rust());
        quote! { #arg }
    });

    let script_code = quote! {
        {
            use redis_lua::Script;

            #defs

            Chain0::new(redis_lua::Info::new(#script_str, #body_str, &[#(#args),*]), (), #(#caps),*)
        }
    };
    script_code.into()
}

#[proc_macro_error]
#[proc_macro_hack]
pub fn lua_s(input: TokenStream1) -> TokenStream1 {
    let script = Script::new(input, false);

    Checker::new().define("ARGV").check(&script);

    let script = script.script();
    let script_code = quote! {
        #script
    };
    script_code.into()
}