1#![recursion_limit="128"]
4
5extern crate proc_macro;
6extern crate syn;
7#[macro_use] extern crate quote;
8
9use syn::{DeriveInput, Variant};
10use proc_macro::TokenStream;
11
12fn impl_match_variants<F>(ast: &DeriveInput, gen_code: F) -> quote::Tokens
13 where F: Fn(&Variant) -> quote::Tokens {
14 if let &syn::Body::Enum(ref variants) = &ast.body {
15 let name = &ast.ident;
16 let mut impl_variants = quote::Tokens::new();
17 for v in variants {
18 let v_ident = &v.ident;
19 let code = gen_code(&v);
20 impl_variants.append(quote! {
21 #name::#v_ident #code,
22 });
23 }
24
25 impl_variants
26 } else {
27 panic!("This derive only works for enums")
28 }
29}
30
31#[proc_macro_derive(Transform)]
33pub fn inner_transform(input: TokenStream) -> TokenStream {
34 let s = input.to_string();
36
37 let ast = syn::parse_macro_input(&s).unwrap();
39
40 let gen = impl_transform(&ast);
42
43 gen.parse().unwrap()
45}
46
47fn impl_transform(ast: &DeriveInput) -> quote::Tokens {
48 let name = &ast.ident;
49 let transform_variants = impl_match_variants(&ast, |_| {
50 quote! { (ref n) => { n.transform(ctx) } }
51 });
52
53 let location_variants = impl_match_variants(&ast, |_| {
54 quote! { (ref n) => { n.location() } }
55 });
56
57 quote! {
58 impl Transform for #name {
59 fn transform(&self, ctx: TransformContext)
60 -> Result<Option<TransformedNode>> {
61 match *self {
62 #transform_variants
63 }
64 }
65
66 fn location(&self) -> AstLocation {
67 match *self {
68 #location_variants
69 }
70 }
71 }
72 }
73}
74
75#[proc_macro_derive(TransformResult)]
77pub fn inner_transform_result(input: TokenStream) -> TokenStream {
78 let s = input.to_string();
80
81 let ast = syn::parse_macro_input(&s).unwrap();
83
84 let gen = impl_transform_result(&ast);
86
87 gen.parse().unwrap()
89}
90
91fn impl_transform_result(ast: &DeriveInput) -> quote::Tokens {
92 let name = &ast.ident;
93 let return_variants = impl_match_variants(&ast, |_| {
94 quote! { (ref n) => { n.return_value() } }
95 });
96
97 let render_variants = impl_match_variants(&ast, |_| {
98 quote! { (ref n) => { n.render(ctx, buf) } }
99 });
100
101 quote! {
102 impl TransformResult for #name {
103 fn return_value(&self) -> ScopeValue {
104 match *self {
105 #return_variants
106 }
107 }
108
109 fn render(&self, ctx: RenderContext, buf: &mut Write) -> Result<()> {
111 match *self {
112 #render_variants
113 }
114 }
115 }
116 }
117}
118
119#[proc_macro_derive(InnerScopeValue)]
121pub fn inner_scope_value(input: TokenStream) -> TokenStream {
122 let s = input.to_string();
124
125 let ast = syn::parse_macro_input(&s).unwrap();
127
128 let gen = impl_inner_scope_value(&ast);
130
131 gen.parse().unwrap()
133}
134
135fn impl_inner_scope_value(ast: &DeriveInput) -> quote::Tokens {
136 let name = &ast.ident;
137 let add_variants = impl_match_variants(&ast, |_| {
138 quote! { (v) => { v.try_add(other.try_into()?) } }
139 });
140 let mul_variants = impl_match_variants(&ast, |_| {
141 quote! { (v) => { v.try_mul(other.try_into()?) } }
142 });
143 let sub_variants = impl_match_variants(&ast, |_| {
144 quote! { (v) => { v.try_sub(other.try_into()?) } }
145 });
146 let div_variants = impl_match_variants(&ast, |_| {
147 quote! { (v) => { v.try_div(other.try_into()?) } }
148 });
149 let cmp_variants = impl_match_variants(&ast, |_| {
150 quote! { (ref v) => { v.try_cmp(other.try_into()?) } }
151 });
152 let eq_variants = impl_match_variants(&ast, |_| {
153 quote! { (ref v) => { v.try_eq(other.try_into()?) } }
154 });
155 let type_name_variants = impl_match_variants(&ast, |_| {
156 quote! { (ref v) => { v.type_name() } }
157 });
158
159 quote! {
160 impl InnerScopeValue for #name {
161 fn try_add(self, other: Self) -> Result<Self> {
162 match self {
163 #add_variants
164 }
165 }
166
167 fn try_sub(self, other: Self) -> Result<Self> {
168 match self {
169 #sub_variants
170 }
171 }
172
173 fn try_mul(self, other: Self) -> Result<Self> {
174 match self {
175 #mul_variants
176 }
177 }
178
179 fn try_div(self, other: Self) -> Result<Self> {
180 match self {
181 #div_variants
182 }
183 }
184
185 fn try_cmp(&self, other: Self) -> Result<Ordering> {
186 match *self {
187 #cmp_variants
188 }
189 }
190
191 fn try_eq(&self, other: Self) -> Result<bool> {
192 match *self {
193 #eq_variants
194 }
195 }
196
197 fn type_name(&self) -> &'static str {
198 match *self {
199 #type_name_variants
200 }
201 }
202 }
203 }
204}