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
extern crate proc_macro;
#[macro_use]
extern crate nom;

use proc_macro::TokenStream;
use quote::ToTokens;

mod generator;
mod parser;

#[proc_macro_derive(Escape, attributes(escape))]
pub fn derive(input: TokenStream) -> TokenStream {
    let ast: &syn::DeriveInput = &syn::parse(input).unwrap();
    build(ast).parse().unwrap()
}

/// Takes a `syn::DeriveInput` and generates source code for it
///
/// Reads the metadata from the `escape()` attribute.
fn build(ast: &syn::DeriveInput) -> String {
    // Check that an attribute called `escape()` exists and that it is
    // the proper type (list).
    let mut meta = None;
    for attr in &ast.attrs {
        match attr.interpret_meta() {
            Some(m) => {
                if m.name() == "escape" {
                    meta = Some(m);
                }
            }
            None => {
                let mut tokens = quote::__rt::TokenStream::new();
                attr.to_tokens(&mut tokens);
                panic!("unable to interpret attribute: {}", tokens);
            }
        }
    }

    let meta_list = match meta.expect("no attribute 'escape' found") {
        syn::Meta::List(inner) => inner,
        _ => panic!("attribute 'escape' has incorrect type"),
    };

    let mut avx = true;
    let mut pairs = None;
    let mut print = false;
    let mut simd = true;
    let mut sized = false;
    for nm_item in meta_list.nested {
        if let syn::NestedMeta::Meta(ref item) = nm_item {
            if let syn::Meta::NameValue(ref pair) = item {
                match pair.ident.to_string().as_ref() {
                    "avx" => {
                        if let syn::Lit::Bool(ref s) = pair.lit {
                            avx = s.value
                        } else {
                            panic!("avx value must be boolean literal")
                        }
                    }
                    "pairs" => {
                        if let syn::Lit::Str(ref s) = pair.lit {
                            pairs = Some(s.value());
                        } else {
                            panic!("pairs value must be string literal")
                        }
                    }
                    "print" => {
                        if let syn::Lit::Bool(ref s) = pair.lit {
                            print = s.value;
                        } else {
                            panic!("print value must be boolean literal")
                        }
                    }
                    "simd" => {
                        if let syn::Lit::Bool(ref s) = pair.lit {
                            simd = s.value;
                        } else {
                            panic!("simd value must be boolean literal")
                        }
                    }
                    "sized" => {
                        if let syn::Lit::Bool(ref s) = pair.lit {
                            sized = s.value;
                        } else {
                            panic!("sized value must be boolean literal")
                        }
                    }
                    attr => panic!("unsupported annotation key '{}' found", attr),
                }
            }
        }
    }

    let code = generator::generate(
        &parser::parse(&pairs.expect("pairs not found in attributes")),
        sized,
        simd,
        avx,
    );

    if print {
        eprintln!("{}", code);
    }

    code
}