]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/deriving/encodable.rs
librustc: Update the serializer to work properly with INHTWAMA, removing mutable...
[rust.git] / src / libsyntax / ext / deriving / encodable.rs
1 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use ast;
12 use ast::*;
13 use ext::base::ext_ctxt;
14 use ext::build;
15 use ext::deriving::*;
16 use codemap::{span, spanned};
17 use ast_util;
18 use opt_vec;
19
20 pub fn expand_deriving_encodable(
21     cx: @ext_ctxt,
22     span: span,
23     _mitem: @meta_item,
24     in_items: ~[@item]
25 ) -> ~[@item] {
26     expand_deriving(
27         cx,
28         span,
29         in_items,
30         expand_deriving_encodable_struct_def,
31         expand_deriving_encodable_enum_def
32     )
33 }
34
35 fn create_derived_encodable_impl(
36     cx: @ext_ctxt,
37     span: span,
38     type_ident: ident,
39     generics: &Generics,
40     method: @method
41 ) -> @item {
42     let encoder_ty_param = build::mk_ty_param(
43         cx,
44         cx.ident_of(~"__E"),
45         @opt_vec::with(
46             build::mk_trait_ty_param_bound_global(
47                 cx,
48                 span,
49                 ~[
50                     cx.ident_of(~"std"),
51                     cx.ident_of(~"serialize"),
52                     cx.ident_of(~"Encoder"),
53                 ]
54             )
55         )
56     );
57
58     // All the type parameters need to bound to the trait.
59     let generic_ty_params = opt_vec::with(encoder_ty_param);
60
61     let methods = [method];
62     let trait_path = build::mk_raw_path_global_(
63         span,
64         ~[
65             cx.ident_of(~"std"),
66             cx.ident_of(~"serialize"),
67             cx.ident_of(~"Encodable")
68         ],
69         ~[
70             build::mk_simple_ty_path(cx, span, cx.ident_of(~"__E"))
71         ]
72     );
73     create_derived_impl(
74         cx,
75         span,
76         type_ident,
77         generics,
78         methods,
79         trait_path,
80         generic_ty_params,
81         opt_vec::Empty
82     )
83 }
84
85 // Creates a method from the given set of statements conforming to the
86 // signature of the `encodable` method.
87 fn create_encode_method(
88     cx: @ext_ctxt,
89     span: span,
90     statements: ~[@stmt]
91 ) -> @method {
92     // Create the `e` parameter.
93     let e_arg_type = build::mk_ty_rptr(
94         cx,
95         span,
96         build::mk_simple_ty_path(cx, span, cx.ident_of(~"__E")),
97         ast::m_mutbl
98     );
99     let e_arg = build::mk_arg(cx, span, cx.ident_of(~"__e"), e_arg_type);
100
101     // Create the type of the return value.
102     let output_type = @ast::Ty { id: cx.next_id(), node: ty_nil, span: span };
103
104     // Create the function declaration.
105     let inputs = ~[e_arg];
106     let fn_decl = build::mk_fn_decl(inputs, output_type);
107
108     // Create the body block.
109     let body_block = build::mk_block_(cx, span, statements);
110
111     // Create the method.
112     let self_ty = spanned { node: sty_region(None, m_imm), span: span };
113     let method_ident = cx.ident_of(~"encode");
114     @ast::method {
115         ident: method_ident,
116         attrs: ~[],
117         generics: ast_util::empty_generics(),
118         self_ty: self_ty,
119         purity: impure_fn,
120         decl: fn_decl,
121         body: body_block,
122         id: cx.next_id(),
123         span: span,
124         self_id: cx.next_id(),
125         vis: public
126     }
127 }
128
129 fn call_substructure_encode_method(
130     cx: @ext_ctxt,
131     span: span,
132     self_field: @expr
133 ) -> @ast::expr {
134     // Gather up the parameters we want to chain along.
135     let e_ident = cx.ident_of(~"__e");
136     let e_expr = build::mk_path(cx, span, ~[e_ident]);
137
138     // Call the substructure method.
139     let encode_ident = cx.ident_of(~"encode");
140     build::mk_method_call(
141         cx,
142         span,
143         self_field,
144         encode_ident,
145         ~[e_expr]
146     )
147 }
148
149 fn expand_deriving_encodable_struct_def(
150     cx: @ext_ctxt,
151     span: span,
152     struct_def: &struct_def,
153     type_ident: ident,
154     generics: &Generics
155 ) -> @item {
156     // Create the method.
157     let method = expand_deriving_encodable_struct_method(
158         cx,
159         span,
160         type_ident,
161         struct_def
162     );
163
164     // Create the implementation.
165     create_derived_encodable_impl(
166         cx,
167         span,
168         type_ident,
169         generics,
170         method
171     )
172 }
173
174 fn expand_deriving_encodable_enum_def(
175     cx: @ext_ctxt,
176     span: span,
177     enum_definition: &enum_def,
178     type_ident: ident,
179     generics: &Generics
180 ) -> @item {
181     // Create the method.
182     let method = expand_deriving_encodable_enum_method(
183         cx,
184         span,
185         type_ident,
186         enum_definition
187     );
188
189     // Create the implementation.
190     create_derived_encodable_impl(
191         cx,
192         span,
193         type_ident,
194         generics,
195         method
196     )
197 }
198
199 fn expand_deriving_encodable_struct_method(
200     cx: @ext_ctxt,
201     span: span,
202     type_ident: ident,
203     struct_def: &struct_def
204 ) -> @method {
205     let self_ident = cx.ident_of(~"self");
206
207     // Create the body of the method.
208     let mut idx = 0;
209     let mut statements = ~[];
210     for struct_def.fields.each |struct_field| {
211         match struct_field.node.kind {
212             named_field(ident, _, _) => {
213                 // Create the accessor for this field.
214                 let self_field = build::mk_access(
215                     cx,
216                     span,
217                     ~[self_ident],
218                     ident
219                 );
220
221                 // Call the substructure method.
222                 let encode_expr = call_substructure_encode_method(
223                     cx,
224                     span,
225                     self_field
226                 );
227
228                 let e_ident = cx.ident_of(~"__e");
229                 let e_arg = build::mk_arg(cx,
230                                           span,
231                                           e_ident,
232                                           build::mk_ty_infer(cx, span));
233
234                 let blk_expr = build::mk_lambda(
235                     cx,
236                     span,
237                     build::mk_fn_decl(~[e_arg], build::mk_ty_infer(cx, span)),
238                     encode_expr
239                 );
240
241                 let call_expr = build::mk_method_call(
242                     cx,
243                     span,
244                     build::mk_path(cx, span, ~[cx.ident_of(~"__e")]),
245                     cx.ident_of(~"emit_struct_field"),
246                     ~[
247                         build::mk_base_str(cx, span, cx.str_of(ident)),
248                         build::mk_uint(cx, span, idx),
249                         blk_expr
250                     ]
251                 );
252
253                 statements.push(build::mk_stmt(cx, span, call_expr));
254             }
255             unnamed_field => {
256                 cx.span_unimpl(
257                     span,
258                     ~"unnamed fields with `deriving(Encodable)`"
259                 );
260             }
261         }
262         idx += 1;
263     }
264
265     let e_arg = build::mk_arg(cx,
266                               span,
267                               cx.ident_of(~"__e"),
268                               build::mk_ty_infer(cx, span));
269
270     let emit_struct_stmt = build::mk_method_call(
271         cx,
272         span,
273         build::mk_path(
274             cx,
275             span,
276             ~[cx.ident_of(~"__e")]
277         ),
278         cx.ident_of(~"emit_struct"),
279         ~[
280             build::mk_base_str(cx, span, cx.str_of(type_ident)),
281             build::mk_uint(cx, span, statements.len()),
282             build::mk_lambda_stmts(
283                 cx,
284                 span,
285                 build::mk_fn_decl(~[e_arg], build::mk_ty_infer(cx, span)),
286                 statements
287             ),
288         ]
289     );
290
291     let statements = ~[build::mk_stmt(cx, span, emit_struct_stmt)];
292
293     // Create the method itself.
294     return create_encode_method(cx, span, statements);
295 }
296
297 fn expand_deriving_encodable_enum_method(
298     cx: @ext_ctxt,
299     span: span,
300     type_ident: ast::ident,
301     enum_definition: &enum_def
302 ) -> @method {
303     // Create the arms of the match in the method body.
304     let arms = do enum_definition.variants.mapi |i, variant| {
305         // Create the matching pattern.
306         let pat = create_enum_variant_pattern(cx, span, variant, ~"__self");
307
308         // Feed the discriminant to the encode function.
309         let mut stmts = ~[];
310
311         // Feed each argument in this variant to the encode function
312         // as well.
313         let variant_arg_len = variant_arg_count(cx, span, variant);
314         for uint::range(0, variant_arg_len) |j| {
315             // Create the expression for this field.
316             let field_ident = cx.ident_of(~"__self_" + j.to_str());
317             let field = build::mk_path(cx, span, ~[ field_ident ]);
318
319             // Call the substructure method.
320             let expr = call_substructure_encode_method(cx, span, field);
321
322             let e_ident = cx.ident_of(~"__e");
323             let e_arg = build::mk_arg(cx,
324                                       span,
325                                       e_ident,
326                                       build::mk_ty_infer(cx, span));
327
328             let blk_expr = build::mk_lambda(
329                 cx,
330                 span,
331                 build::mk_fn_decl(~[e_arg], build::mk_ty_infer(cx, span)),
332                 expr
333             );
334
335             let call_expr = build::mk_method_call(
336                 cx,
337                 span,
338                 build::mk_path(cx, span, ~[cx.ident_of(~"__e")]),
339                 cx.ident_of(~"emit_enum_variant_arg"),
340                 ~[
341                     build::mk_uint(cx, span, j),
342                     blk_expr,
343                 ]
344             );
345
346             stmts.push(build::mk_stmt(cx, span, call_expr));
347         }
348
349         // Create the pattern body.
350         let e_arg = build::mk_arg(cx,
351                                   span,
352                                   cx.ident_of(~"__e"),
353                                   build::mk_ty_infer(cx, span));
354         let call_expr = build::mk_method_call(
355             cx,
356             span,
357             build::mk_path(cx, span, ~[cx.ident_of(~"__e")]),
358             cx.ident_of(~"emit_enum_variant"),
359             ~[
360                 build::mk_base_str(cx, span, cx.str_of(variant.node.name)),
361                 build::mk_uint(cx, span, i),
362                 build::mk_uint(cx, span, variant_arg_len),
363                 build::mk_lambda_stmts(
364                     cx,
365                     span,
366                     build::mk_fn_decl(~[e_arg], build::mk_ty_infer(cx, span)),
367                     stmts
368                 )
369             ]
370         );
371
372         let match_body_block = build::mk_simple_block(cx, span, call_expr);
373
374         // Create the arm.
375         ast::arm {
376             pats: ~[pat],
377             guard: None,
378             body: match_body_block,
379         }
380     };
381
382     let e_ident = cx.ident_of(~"__e");
383     let e_arg = build::mk_arg(cx,
384                               span,
385                               e_ident,
386                               build::mk_ty_infer(cx, span));
387
388     // Create the method body.
389     let lambda_expr = build::mk_lambda(
390         cx,
391         span,
392         build::mk_fn_decl(~[e_arg], build::mk_ty_infer(cx, span)),
393         expand_enum_or_struct_match(cx, span, arms)
394     );
395
396     let call_expr = build::mk_method_call(
397         cx,
398         span,
399         build::mk_path(cx, span, ~[cx.ident_of(~"__e")]),
400         cx.ident_of(~"emit_enum"),
401         ~[
402             build::mk_base_str(cx, span, cx.str_of(type_ident)),
403             lambda_expr,
404         ]
405     );
406
407     let stmt = build::mk_stmt(cx, span, call_expr);
408
409     // Create the method.
410     create_encode_method(cx, span, ~[stmt])
411 }