proc_macro_hack_impl/
lib.rs1extern crate proc_macro;
2use proc_macro::TokenStream;
3
4struct Hack {
5 attrs: String,
6 name: String,
7 name_impl: String,
8}
9
10#[proc_macro_derive(ProcMacroHackExpr)]
11pub fn hack_expr(input: TokenStream) -> TokenStream {
12 let hack = parse(input);
13
14 let rules = format!("
15 {attrs}
16 #[macro_export]
17 macro_rules! {name} {{
18 ($($tt:tt)*) => {{{{
19 #[derive({name_impl})]
20 #[allow(unused)]
21 enum ProcMacroHack {{
22 Input = (stringify!($($tt)*), 0).1
23 }}
24
25 proc_macro_call!()
26 }}}}
27 }}
28 ", attrs=hack.attrs, name=hack.name, name_impl=hack.name_impl);
29
30 rules.parse().unwrap()
31}
32
33#[proc_macro_derive(ProcMacroHackItem)]
34pub fn hack_item(input: TokenStream) -> TokenStream {
35 let hack = parse(input);
36
37 let rules = format!("
38 {attrs}
39 #[macro_export]
40 macro_rules! {name} {{
41 ($($tt:tt)*) => {{
42 #[derive({name_impl})]
43 #[allow(unused)]
44 enum ProcMacroHack {{
45 Input = (stringify!($($tt)*), 0).1
46 }}
47 }}
48 }}
49 ", attrs=hack.attrs, name=hack.name, name_impl=hack.name_impl);
50
51 rules.parse().unwrap()
52}
53
54fn parse(input: TokenStream) -> Hack {
64 let source = input.to_string();
65
66 let mut front = source.split_whitespace();
67 let next = front.next().unwrap();
68 if next == "#[allow(unused" {
69 assert_eq!(Some(","), front.next());
70 } else {
71 assert_eq!("#[allow(unused,", next);
72 }
73 assert_eq!(Some("non_camel_case_types)]"), front.next());
74
75 let mut back = source.split_whitespace().rev();
76 assert_eq!(Some("}"), back.next());
77 let name_impl = back.next().unwrap();
78 assert_eq!(Some("{"), back.next());
79 let name = back.next().unwrap();
80 assert_eq!(Some("enum"), back.next());
81
82 let start_attrs = source.find(']').unwrap() + 1;
83 let end = source.rfind('}').unwrap();
84 let end = source[..end].rfind(name_impl).unwrap();
85 let end = source[..end].rfind('{').unwrap();
86 let end = source[..end].rfind(name).unwrap();
87 let end = source[..end].rfind("enum").unwrap();
88
89 Hack {
90 attrs: source[start_attrs..end].trim().to_owned(),
91 name: name.to_owned(),
92 name_impl: name_impl.to_owned(),
93 }
94}