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
#![feature(proc_macro)]
#![allow(unused_imports)]
extern crate proc_macro;
extern crate syn;
#[macro_use] extern crate quote;
use syn::{Body, VariantData};
use proc_macro::TokenStream;
use quote::{ToTokens, Tokens};
#[proc_macro_derive(CNodeable)]
pub fn cnodeable(input: TokenStream) -> TokenStream {
let s = input.to_string();
let ast = syn::parse_macro_input(&s).unwrap();
let gen = impl_cnodeable(&ast);
gen.parse().unwrap()
}
fn impl_cnodeable(ast: &syn::MacroInput) -> quote::Tokens {
match ast.body {
Body::Enum(ref variants) => {
let mut arms = Vec::new();
let mut arms_ref = Vec::new();
for var in variants {
let mut has_node = false;
let mut args = Vec::new();
let mut args_ref = Vec::new();
if let &syn::VariantData::Tuple(ref inner) = &var.data {
for item in inner {
let mut tokens = Tokens::new();
item.to_tokens(&mut tokens);
let arg = tokens.to_string();
if arg == "a" {
has_node = true;
args.push(quote!(node));
args_ref.push(quote!(ref node));
} else {
args.push(quote!(_));
args_ref.push(quote!(_));
}
}
} else {
unreachable!("Expected enum tuple.");
}
let name = &var.ident;
if !has_node {
arms.push(quote! { #name ( node ) => node.into_node_info(), });
arms_ref.push(quote! { #name ( ref node ) => node.node_info(), });
} else {
arms.push(quote! { #name ( #(#args),* ) => node, });
arms_ref.push(quote! { #name ( #(#args_ref),* ) => node, });
}
}
let name = &ast.ident;
quote! {
impl CNode for #name<NodeInfo> {
fn node_info(&self) -> &NodeInfo {
match *self {
#(#arms_ref)*
}
}
fn into_node_info(self) -> NodeInfo {
match self {
#(#arms)*
}
}
}
}
}
Body::Struct(ref var) => {
let node_pos: Option<usize> = if let &syn::VariantData::Tuple(ref inner) = var {
inner.iter().position(|item| {
let mut tokens = Tokens::new();
item.to_tokens(&mut tokens);
let arg = tokens.to_string();
arg == "a" || arg == "pub a"
})
} else {
unreachable!("Expected struct tuple.");
};
let (args, args_ref) = if node_pos.is_none() {
(quote!( (self.0).into_node_info() ),
quote!( (self.0).node_info() ))
} else if let Some(pos) = node_pos {
let expr = syn::parse_expr(&format!("{}", pos)).unwrap();
(quote!( self.#expr ),
quote!( &self.#expr ))
} else {
unreachable!("Expected struct entry to be valid");
};
let name = &ast.ident;
quote! {
impl CNode for #name<NodeInfo> {
fn node_info(&self) -> &NodeInfo {
#args_ref
}
fn into_node_info(self) -> NodeInfo {
#args
}
}
}
}
}
}