diff options
| author | bors <bors@rust-lang.org> | 2013-04-11 06:55:01 -0700 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2013-04-11 06:55:01 -0700 |
| commit | 2f8b36fc16178b298b23dfa199067ec2dd91df93 (patch) | |
| tree | 685ecf6bd64a084a1827134c9f5966026763b2d3 /src/libsyntax | |
| parent | e0defb8466e624ded66ef00f092a86b638e01152 (diff) | |
| parent | 26ecb30f550a46d52528a7d45d9327ebce938e81 (diff) | |
| download | rust-2f8b36fc16178b298b23dfa199067ec2dd91df93.tar.gz rust-2f8b36fc16178b298b23dfa199067ec2dd91df93.zip | |
auto merge of #5819 : erickt/rust/incoming, r=erickt
Good morning, This first patch series adds support for `#[deriving(Decodable, Encodable)]`, but does not yet remove `#[auto_encode]` and `#[auto_decode]`. I need a snapshot to remove the old code. Along the way it also extends support for tuple structs and struct enum variants. Also, it includes a minor fix to the pretty printer. We decided a while ago to use 4 spaces to indent a match arm instead of 2. This updates the pretty printer to reflect that.
Diffstat (limited to 'src/libsyntax')
| -rw-r--r-- | src/libsyntax/ext/auto_encode.rs | 40 | ||||
| -rw-r--r-- | src/libsyntax/ext/build.rs | 124 | ||||
| -rw-r--r-- | src/libsyntax/ext/deriving/clone.rs | 10 | ||||
| -rw-r--r-- | src/libsyntax/ext/deriving/decodable.rs | 454 | ||||
| -rw-r--r-- | src/libsyntax/ext/deriving/encodable.rs | 388 | ||||
| -rw-r--r-- | src/libsyntax/ext/deriving/eq.rs | 8 | ||||
| -rw-r--r-- | src/libsyntax/ext/deriving/iter_bytes.rs | 8 | ||||
| -rw-r--r-- | src/libsyntax/ext/deriving/mod.rs | 50 | ||||
| -rw-r--r-- | src/libsyntax/print/pprust.rs | 15 |
9 files changed, 1030 insertions, 67 deletions
diff --git a/src/libsyntax/ext/auto_encode.rs b/src/libsyntax/ext/auto_encode.rs index f9dadb560e3..e53a8f361b5 100644 --- a/src/libsyntax/ext/auto_encode.rs +++ b/src/libsyntax/ext/auto_encode.rs @@ -732,12 +732,12 @@ fn mk_struct_ser_impl( ) ); - // ast for `__s.emit_field($(name), $(idx), $(expr_lambda))` + // ast for `__s.emit_struct_field($(name), $(idx), $(expr_lambda))` cx.stmt( cx.expr_method_call( span, cx.expr_var(span, ~"__s"), - cx.ident_of(~"emit_field"), + cx.ident_of(~"emit_struct_field"), ~[ cx.lit_str(span, @cx.str_of(field.ident)), cx.lit_uint(span, idx), @@ -786,11 +786,11 @@ fn mk_struct_deser_impl( ) ); - // ast for `__d.read_field($(name), $(idx), $(expr_lambda))` + // ast for `__d.read_struct_field($(name), $(idx), $(expr_lambda))` let expr: @ast::expr = cx.expr_method_call( span, cx.expr_var(span, ~"__d"), - cx.ident_of(~"read_field"), + cx.ident_of(~"read_struct_field"), ~[ cx.lit_str(span, @cx.str_of(field.ident)), cx.lit_uint(span, idx), @@ -1253,20 +1253,35 @@ mod test { self.add_to_log(CallToEmitEnumVariantArg (idx)); f(); } - fn emit_seq(&self, +_len: uint, f: &fn()) { - self.add_unknown_to_log(); f(); + fn emit_enum_struct_variant(&self, name: &str, id: uint, cnt: uint, f: &fn()) { + self.emit_enum_variant(name, id, cnt, f) } - fn emit_seq_elt(&self, +_idx: uint, f: &fn()) { - self.add_unknown_to_log(); f(); + + fn emit_enum_struct_variant_field(&self, _name: &str, idx: uint, f: &fn()) { + self.emit_enum_variant_arg(idx, f) } fn emit_struct(&self, name: &str, +len: uint, f: &fn()) { self.add_to_log(CallToEmitStruct (name.to_str(),len)); f(); } - fn emit_field(&self, name: &str, +idx: uint, f: &fn()) { + fn emit_struct_field(&self, name: &str, +idx: uint, f: &fn()) { self.add_to_log(CallToEmitField (name.to_str(),idx)); f(); } + fn emit_tuple(&self, _len: uint, f: &fn()) { + self.add_unknown_to_log(); f(); + } + fn emit_tuple_arg(&self, _idx: uint, f: &fn()) { + self.add_unknown_to_log(); f(); + } + + fn emit_tuple_struct(&self, _name: &str, _len: uint, f: &fn()) { + self.add_unknown_to_log(); f(); + } + fn emit_tuple_struct_arg(&self, _idx: uint, f: &fn()) { + self.add_unknown_to_log(); f(); + } + fn emit_option(&self, f: &fn()) { self.add_to_log(CallToEmitOption); f(); @@ -1279,6 +1294,13 @@ mod test { f(); } + fn emit_seq(&self, +_len: uint, f: &fn()) { + self.add_unknown_to_log(); f(); + } + fn emit_seq_elt(&self, +_idx: uint, f: &fn()) { + self.add_unknown_to_log(); f(); + } + fn emit_map(&self, _len: uint, f: &fn()) { self.add_unknown_to_log(); f(); } diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 0f84ac41532..b375adef926 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -64,12 +64,7 @@ pub fn mk_unary(cx: @ext_ctxt, sp: span, op: ast::unop, e: @ast::expr) mk_expr(cx, sp, ast::expr_unary(op, e)) } pub fn mk_raw_path(sp: span, +idents: ~[ast::ident]) -> @ast::Path { - let p = @ast::Path { span: sp, - global: false, - idents: idents, - rp: None, - types: ~[] }; - return p; + mk_raw_path_(sp, idents, ~[]) } pub fn mk_raw_path_(sp: span, +idents: ~[ast::ident], @@ -82,11 +77,16 @@ pub fn mk_raw_path_(sp: span, types: types } } pub fn mk_raw_path_global(sp: span, +idents: ~[ast::ident]) -> @ast::Path { + mk_raw_path_global_(sp, idents, ~[]) +} +pub fn mk_raw_path_global_(sp: span, + +idents: ~[ast::ident], + +types: ~[@ast::Ty]) -> @ast::Path { @ast::Path { span: sp, global: true, idents: idents, rp: None, - types: ~[] } + types: types } } pub fn mk_path(cx: @ext_ctxt, sp: span, +idents: ~[ast::ident]) -> @ast::expr { @@ -271,6 +271,36 @@ pub fn mk_simple_block(cx: @ext_ctxt, span: span, } } +pub fn mk_lambda_(cx: @ext_ctxt, + span: span, + fn_decl: ast::fn_decl, + blk: ast::blk) + -> @ast::expr { + mk_expr(cx, span, ast::expr_fn_block(fn_decl, blk)) +} +pub fn mk_lambda(cx: @ext_ctxt, + span: span, + fn_decl: ast::fn_decl, + expr: @ast::expr) + -> @ast::expr { + let blk = mk_simple_block(cx, span, expr); + mk_lambda_(cx, span, fn_decl, blk) +} +pub fn mk_lambda_stmts(cx: @ext_ctxt, + span: span, + fn_decl: ast::fn_decl, + stmts: ~[@ast::stmt]) + -> @ast::expr { + let blk = mk_block(cx, span, ~[], stmts, None); + mk_lambda(cx, span, fn_decl, blk) +} +pub fn mk_lambda_no_args(cx: @ext_ctxt, + span: span, + expr: @ast::expr) + -> @ast::expr { + let fn_decl = mk_fn_decl(~[], mk_ty_infer(cx, span)); + mk_lambda(cx, span, fn_decl, expr) +} pub fn mk_copy(cx: @ext_ctxt, sp: span, e: @ast::expr) -> @ast::expr { mk_expr(cx, sp, ast::expr_copy(e)) } @@ -280,11 +310,20 @@ pub fn mk_managed(cx: @ext_ctxt, sp: span, e: @ast::expr) -> @ast::expr { pub fn mk_pat(cx: @ext_ctxt, span: span, +pat: ast::pat_) -> @ast::pat { @ast::pat { id: cx.next_id(), node: pat, span: span } } +pub fn mk_pat_wild(cx: @ext_ctxt, span: span) -> @ast::pat { + mk_pat(cx, span, ast::pat_wild) +} +pub fn mk_pat_lit(cx: @ext_ctxt, + span: span, + expr: @ast::expr) -> @ast::pat { + mk_pat(cx, span, ast::pat_lit(expr)) +} pub fn mk_pat_ident(cx: @ext_ctxt, span: span, ident: ast::ident) -> @ast::pat { mk_pat_ident_with_binding_mode(cx, span, ident, ast::bind_by_copy) } + pub fn mk_pat_ident_with_binding_mode(cx: @ext_ctxt, span: span, ident: ast::ident, @@ -337,12 +376,35 @@ pub fn mk_ty_path_global(cx: @ext_ctxt, let ty = @ast::Ty { id: cx.next_id(), node: ty, span: span }; ty } +pub fn mk_ty_rptr(cx: @ext_ctxt, + span: span, + ty: @ast::Ty, + mutbl: ast::mutability) + -> @ast::Ty { + @ast::Ty { + id: cx.next_id(), + span: span, + node: ast::ty_rptr( + None, + ast::mt { ty: ty, mutbl: mutbl } + ), + } +} +pub fn mk_ty_infer(cx: @ext_ctxt, span: span) -> @ast::Ty { + @ast::Ty { + id: cx.next_id(), + node: ast::ty_infer, + span: span, + } +} pub fn mk_trait_ref_global(cx: @ext_ctxt, span: span, +idents: ~[ ast::ident ]) -> @ast::trait_ref { - let path = build::mk_raw_path_global(span, idents); + mk_trait_ref_(cx, build::mk_raw_path_global(span, idents)) +} +pub fn mk_trait_ref_(cx: @ext_ctxt, path: @ast::Path) -> @ast::trait_ref { @ast::trait_ref { path: path, ref_id: cx.next_id() @@ -371,6 +433,16 @@ pub fn mk_arg(cx: @ext_ctxt, pub fn mk_fn_decl(+inputs: ~[ast::arg], output: @ast::Ty) -> ast::fn_decl { ast::fn_decl { inputs: inputs, output: output, cf: ast::return_val } } +pub fn mk_trait_ty_param_bound_global(cx: @ext_ctxt, + span: span, + +idents: ~[ast::ident]) + -> ast::TyParamBound { + ast::TraitTyParamBound(mk_trait_ref_global(cx, span, idents)) +} +pub fn mk_trait_ty_param_bound_(cx: @ext_ctxt, + path: @ast::Path) -> ast::TyParamBound { + ast::TraitTyParamBound(mk_trait_ref_(cx, path)) +} pub fn mk_ty_param(cx: @ext_ctxt, ident: ast::ident, bounds: @OptVec<ast::TyParamBound>) @@ -379,8 +451,38 @@ pub fn mk_ty_param(cx: @ext_ctxt, } pub fn mk_lifetime(cx: @ext_ctxt, span: span, - ident: ast::ident) -> ast::Lifetime -{ + ident: ast::ident) + -> ast::Lifetime { ast::Lifetime { id: cx.next_id(), span: span, ident: ident } } - +pub fn mk_arm(cx: @ext_ctxt, + span: span, + pats: ~[@ast::pat], + expr: @ast::expr) + -> ast::arm { + ast::arm { + pats: pats, + guard: None, + body: mk_simple_block(cx, span, expr) + } +} +pub fn mk_unreachable(cx: @ext_ctxt, span: span) -> @ast::expr { + let loc = cx.codemap().lookup_char_pos(span.lo); + mk_call_global( + cx, + span, + ~[ + cx.ident_of(~"core"), + cx.ident_of(~"sys"), + cx.ident_of(~"begin_unwind"), + ], + ~[ + mk_uniq_str(cx, span, ~"internal error: entered unreachable code"), + mk_uniq_str(cx, span, loc.file.name), + mk_uint(cx, span, loc.line), + ] + ) +} +pub fn mk_unreachable_arm(cx: @ext_ctxt, span: span) -> ast::arm { + mk_arm(cx, span, ~[mk_pat_wild(cx, span)], mk_unreachable(cx, span)) +} diff --git a/src/libsyntax/ext/deriving/clone.rs b/src/libsyntax/ext/deriving/clone.rs index c9e17715999..c8ba6b990e4 100644 --- a/src/libsyntax/ext/deriving/clone.rs +++ b/src/libsyntax/ext/deriving/clone.rs @@ -17,6 +17,7 @@ use ext::build; use ext::deriving::*; use codemap::{span, spanned}; use ast_util; +use opt_vec; use core::uint; @@ -48,12 +49,13 @@ fn create_derived_clone_impl(cx: @ext_ctxt, method: @method) -> @item { let methods = [ method ]; - let trait_path = [ + let trait_path = ~[ cx.ident_of(~"core"), cx.ident_of(~"clone"), cx.ident_of(~"Clone"), ]; - create_derived_impl(cx, span, type_ident, generics, methods, trait_path) + let trait_path = build::mk_raw_path_global(span, trait_path); + create_derived_impl(cx, span, type_ident, generics, methods, trait_path, opt_vec::Empty) } // Creates a method from the given expression conforming to the signature of // the `clone` method. @@ -188,9 +190,7 @@ fn expand_deriving_clone_struct_method(cx: @ext_ctxt, fields.push(field); } unnamed_field => { - cx.span_bug(span, - ~"unnamed fields in \ - expand_deriving_clone_struct_method"); + cx.span_bug(span, ~"unnamed fields in `deriving(Clone)`"); } } } diff --git a/src/libsyntax/ext/deriving/decodable.rs b/src/libsyntax/ext/deriving/decodable.rs new file mode 100644 index 00000000000..11f492316e2 --- /dev/null +++ b/src/libsyntax/ext/deriving/decodable.rs @@ -0,0 +1,454 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::prelude::*; + +use ast; +use ast::*; +use ext::base::ext_ctxt; +use ext::build; +use ext::deriving::*; +use codemap::{span, spanned}; +use ast_util; +use opt_vec; + +use core::uint; + +pub fn expand_deriving_decodable( + cx: @ext_ctxt, + span: span, + _mitem: @meta_item, + in_items: ~[@item] +) -> ~[@item] { + expand_deriving( + cx, + span, + in_items, + expand_deriving_decodable_struct_def, + expand_deriving_decodable_enum_def + ) +} + +fn create_derived_decodable_impl( + cx: @ext_ctxt, + span: span, + type_ident: ident, + generics: &Generics, + method: @method +) -> @item { + let decoder_ty_param = build::mk_ty_param( + cx, + cx.ident_of(~"__D"), + @opt_vec::with( + build::mk_trait_ty_param_bound_global( + cx, + span, + ~[ + cx.ident_of(~"std"), + cx.ident_of(~"serialize"), + cx.ident_of(~"Decoder"), + ] + ) + ) + ); + + // All the type parameters need to bound to the trait. + let generic_ty_params = opt_vec::with(decoder_ty_param); + + let methods = [method]; + let trait_path = build::mk_raw_path_global_( + span, + ~[ + cx.ident_of(~"std"), + cx.ident_of(~"serialize"), + cx.ident_of(~"Decodable") + ], + ~[ + build::mk_simple_ty_path(cx, span, cx.ident_of(~"__D")) + ] + ); + create_derived_impl( + cx, + span, + type_ident, + generics, + methods, + trait_path, + generic_ty_params + ) +} + +// Creates a method from the given set of statements conforming to the +// signature of the `decodable` method. +fn create_decode_method( + cx: @ext_ctxt, + span: span, + type_ident: ast::ident, + generics: &Generics, + expr: @ast::expr +) -> @method { + // Create the `e` parameter. + let d_arg_type = build::mk_ty_rptr( + cx, + span, + build::mk_simple_ty_path(cx, span, cx.ident_of(~"__D")), + ast::m_imm + ); + let d_ident = cx.ident_of(~"__d"); + let d_arg = build::mk_arg(cx, span, d_ident, d_arg_type); + + // Create the type of the return value. + let output_type = create_self_type_with_params( + cx, + span, + type_ident, + generics + ); + + // Create the function declaration. + let inputs = ~[d_arg]; + let fn_decl = build::mk_fn_decl(inputs, output_type); + + // Create the body block. + let body_block = build::mk_simple_block(cx, span, expr); + + // Create the method. + let self_ty = spanned { node: sty_static, span: span }; + let method_ident = cx.ident_of(~"decode"); + @ast::method { + ident: method_ident, + attrs: ~[], + generics: ast_util::empty_generics(), + self_ty: self_ty, + purity: impure_fn, + decl: fn_decl, + body: body_block, + id: cx.next_id(), + span: span, + self_id: cx.next_id(), + vis: public + } +} + +fn call_substructure_decode_method( + cx: @ext_ctxt, + span: span +) -> @ast::expr { + // Call the substructure method. + build::mk_call_( + cx, + span, + build::mk_path_global( + cx, + span, + ~[ + cx.ident_of(~"std"), + cx.ident_of(~"serialize"), + cx.ident_of(~"Decodable"), + cx.ident_of(~"decode"), + ] + ), + ~[ + build::mk_path(cx, span, ~[cx.ident_of(~"__d")]) + ] + ) +} + +fn expand_deriving_decodable_struct_def( + cx: @ext_ctxt, + span: span, + struct_def: &struct_def, + type_ident: ident, + generics: &Generics +) -> @item { + // Create the method. + let method = expand_deriving_decodable_struct_method( + cx, + span, + struct_def, + type_ident, + generics + ); + + // Create the implementation. + create_derived_decodable_impl( + cx, + span, + type_ident, + generics, + method + ) +} + +fn expand_deriving_decodable_enum_def( + cx: @ext_ctxt, + span: span, + enum_definition: &enum_def, + type_ident: ident, + generics: &Generics +) -> @item { + // Create the method. + let method = expand_deriving_decodable_enum_method( + cx, + span, + enum_definition, + type_ident, + generics + ); + + // Create the implementation. + create_derived_decodable_impl( + cx, + span, + type_ident, + generics, + method + ) +} + +fn create_read_struct_field( + cx: @ext_ctxt, + span: span, + idx: uint, + ident: ident +) -> build::Field { + // Call the substructure method. + let decode_expr = call_substructure_decode_method(cx, span); + + let call_expr = build::mk_method_call( + cx, + span, + build::mk_path(cx, span, ~[cx.ident_of(~"__d")]), + cx.ident_of(~"read_struct_field"), + ~[ + build::mk_base_str(cx, span, cx.str_of(ident)), + build::mk_uint(cx, span, idx), + build::mk_lambda_no_args(cx, span, decode_expr), + ] + ); + + build::Field { ident: ident, ex: call_expr } +} + +fn create_read_struct_arg( + cx: @ext_ctxt, + span: span, + idx: uint, + ident: ident +) -> build::Field { + // Call the substructure method. + let decode_expr = call_substructure_decode_method(cx, span); + + let call_expr = build::mk_method_call( + cx, + span, + build::mk_path(cx, span, ~[cx.ident_of(~"__d")]), + cx.ident_of(~"read_struct_arg"), + ~[ + build::mk_uint(cx, span, idx), + build::mk_lambda_no_args(cx, span, decode_expr), + ] + ); + + build::Field { ident: ident, ex: call_expr } +} + +fn expand_deriving_decodable_struct_method( + cx: @ext_ctxt, + span: span, + struct_def: &struct_def, + type_ident: ident, + generics: &Generics +) -> @method { + // Create the body of the method. + let mut i = 0; + let mut fields = ~[]; + for struct_def.fields.each |struct_field| { + match struct_field.node.kind { + named_field(ident, _, _) => { + fields.push(create_read_struct_field(cx, span, i, ident)); + } + unnamed_field => { + cx.span_unimpl( + span, + ~"unnamed fields with `deriving(Decodable)`" + ); + } + } + i += 1; + } + + let read_struct_expr = build::mk_method_call( + cx, + span, + build::mk_path( + cx, + span, + ~[cx.ident_of(~"__d")] + ), + cx.ident_of(~"read_struct"), + ~[ + build::mk_base_str(cx, span, cx.str_of(type_ident)), + build::mk_uint(cx, span, fields.len()), + build::mk_lambda_no_args( + cx, + span, + build::mk_struct_e( + cx, + span, + ~[type_ident], + fields + ) + ), + ] + ); + + // Create the method itself. + create_decode_method(cx, span, type_ident, generics, read_struct_expr) +} + +fn create_read_variant_arg( + cx: @ext_ctxt, + span: span, + idx: uint, + variant: &ast::variant +) -> ast::arm { + // Create the matching pattern. + let pat = build::mk_pat_lit(cx, span, build::mk_uint(cx, span, idx)); + + // Feed each argument in this variant to the decode function + // as well. + let variant_arg_len = variant_arg_count(cx, span, variant); + + let expr = if variant_arg_len == 0 { + build::mk_path(cx, span, ~[variant.node.name]) + } else { + // Feed the discriminant to the decode function. + let mut args = ~[]; + + for uint::range(0, variant_arg_len) |j| { + // Call the substructure method. + let expr = call_substructure_decode_method(cx, span); + + let call_expr = build::mk_method_call( + cx, + span, + build::mk_path(cx, span, ~[cx.ident_of(~"__d")]), + cx.ident_of(~"read_enum_variant_arg"), + ~[ + build::mk_uint(cx, span, j), + build::mk_lambda_no_args(cx, span, expr), + ] + ); + + args.push(call_expr); + } + + build::mk_call( + cx, + span, + ~[variant.node.name], + args + ) + }; + + // Create the arm. + build::mk_arm(cx, span, ~[pat], expr) +} + +fn create_read_enum_variant( + cx: @ext_ctxt, + span: span, + enum_definition: &enum_def +) -> @expr { + // Create a vector that contains all the variant names. + let expr_arm_names = build::mk_base_vec_e( + cx, + span, + do enum_definition.variants.map |variant| { + build::mk_base_str( + cx, + span, + cx.str_of(variant.node.name) + ) + } + ); + + // Create the arms of the match in the method body. + let mut arms = do enum_definition.variants.mapi |i, variant| { + create_read_variant_arg(cx, span, i, variant) + }; + + // Add the impossible case arm. + arms.push(build::mk_unreachable_arm(cx, span)); + + // Create the read_enum_variant expression. + build::mk_method_call( + cx, + span, + build::mk_path(cx, span, ~[cx.ident_of(~"__d")]), + cx.ident_of(~"read_enum_variant"), + ~[ + expr_arm_names, + build::mk_lambda( + cx, + span, + build::mk_fn_decl( + ~[ + build::mk_arg( + cx, + span, + cx.ident_of(~"__i"), + build::mk_ty_infer(cx, span) + ) + ], + build::mk_ty_infer(cx, span) + ), + build::mk_expr( + cx, + span, + ast::expr_match( + build::mk_path(cx, span, ~[cx.ident_of(~"__i")]), + arms + ) + ) + ) + ] + ) +} + +fn expand_deriving_decodable_enum_method( + cx: @ext_ctxt, + span: span, + enum_definition: &enum_def, + type_ident: ast::ident, + generics: &Generics +) -> @method { + let read_enum_variant_expr = create_read_enum_variant( + cx, + span, + enum_definition + ); + + // Create the read_enum expression + let read_enum_expr = build::mk_method_call( + cx, + span, + build::mk_path(cx, span, ~[cx.ident_of(~"__d")]), + cx.ident_of(~"read_enum"), + ~[ + build::mk_base_str(cx, span, cx.str_of(type_ident)), + build::mk_lambda_no_args(cx, span, read_enum_variant_expr), + ] + ); + + // Create the method. + create_decode_method(cx, span, type_ident, generics, read_enum_expr) +} diff --git a/src/libsyntax/ext/deriving/encodable.rs b/src/libsyntax/ext/deriving/encodable.rs new file mode 100644 index 00000000000..81bfb03724f --- /dev/null +++ b/src/libsyntax/ext/deriving/encodable.rs @@ -0,0 +1,388 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::prelude::*; + +use ast; +use ast::*; +use ext::base::ext_ctxt; +use ext::build; +use ext::deriving::*; +use codemap::{span, spanned}; +use ast_util; +use opt_vec; + +use core::uint; + +pub fn expand_deriving_encodable( + cx: @ext_ctxt, + span: span, + _mitem: @meta_item, + in_items: ~[@item] +) -> ~[@item] { + expand_deriving( + cx, + span, + in_items, + expand_deriving_encodable_struct_def, + expand_deriving_encodable_enum_def + ) +} + +fn create_derived_encodable_impl( + cx: @ext_ctxt, + span: span, + type_ident: ident, + generics: &Generics, + method: @method +) -> @item { + let encoder_ty_param = build::mk_ty_param( + cx, + cx.ident_of(~"__E"), + @opt_vec::with( + build::mk_trait_ty_param_bound_global( + cx, + span, + ~[ + cx.ident_of(~"std"), + cx.ident_of(~"serialize"), + cx.ident_of(~"Encoder"), + ] + ) + ) + ); + + // All the type parameters need to bound to the trait. + let generic_ty_params = opt_vec::with(encoder_ty_param); + + let methods = [method]; + let trait_path = build::mk_raw_path_global_( + span, + ~[ + cx.ident_of(~"std"), + cx.ident_of(~"serialize"), + cx.ident_of(~"Encodable") + ], + ~[ + build::mk_simple_ty_path(cx, span, cx.ident_of(~"__E")) + ] + ); + create_derived_impl( + cx, + span, + type_ident, + generics, + methods, + trait_path, + generic_ty_params + ) +} + +// Creates a method from the given set of statements conforming to the +// signature of the `encodable` method. +fn create_encode_method( + cx: @ext_ctxt, + span: span, + +statements: ~[@stmt] +) -> @method { + // Create the `e` parameter. + let e_arg_type = build::mk_ty_rptr( + cx, + span, + build::mk_simple_ty_path(cx, span, cx.ident_of(~"__E")), + ast::m_imm + ); + let e_ident = cx.ident_of(~"__e"); + let e_arg = build::mk_arg(cx, span, e_ident, e_arg_type); + + // Create the type of the return value. + let output_type = @ast::Ty { id: cx.next_id(), node: ty_nil, span: span }; + + // Create the function declaration. + let inputs = ~[e_arg]; + let fn_decl = build::mk_fn_decl(inputs, output_type); + + // Create the body block. + let body_block = build::mk_block_(cx, span, statements); + + // Create the method. + let self_ty = spanned { node: sty_region(None, m_imm), span: span }; + let method_ident = cx.ident_of(~"encode"); + @ast::method { + ident: method_ident, + attrs: ~[], + generics: ast_util::empty_generics(), + self_ty: self_ty, + purity: impure_fn, + decl: fn_decl, + body: body_block, + id: cx.next_id(), + span: span, + self_id: cx.next_id(), + vis: public + } +} + +fn call_substructure_encode_method( + cx: @ext_ctxt, + span: span, + self_field: @expr +) -> @ast::expr { + // Gather up the parameters we want to chain along. + let e_ident = cx.ident_of(~"__e"); + let e_expr = build::mk_path(cx, span, ~[e_ident]); + + // Call the substructure method. + let encode_ident = cx.ident_of(~"encode"); + build::mk_method_call( + cx, + span, + self_field, + encode_ident, + ~[e_expr] + ) +} + +fn expand_deriving_encodable_struct_def( + cx: @ext_ctxt, + span: span, + struct_def: &struct_def, + type_ident: ident, + generics: &Generics +) -> @item { + // Create the method. + let method = expand_deriving_encodable_struct_method( + cx, + span, + type_ident, + struct_def + ); + + // Create the implementation. + create_derived_encodable_impl( + cx, + span, + type_ident, + generics, + method + ) +} + +fn expand_deriving_encodable_enum_def( + cx: @ext_ctxt, + span: span, + enum_definition: &enum_def, + type_ident: ident, + generics: &Generics +) -> @item { + // Create the method. + let method = expand_deriving_encodable_enum_method( + cx, + span, + type_ident, + enum_definition + ); + + // Create the implementation. + create_derived_encodable_impl( + cx, + span, + type_ident, + generics, + method + ) +} + +fn expand_deriving_encodable_struct_method( + cx: @ext_ctxt, + span: span, + type_ident: ident, + struct_def: &struct_def +) -> @method { + let self_ident = cx.ident_of(~"self"); + + // Create the body of the method. + let mut idx = 0; + let mut statements = ~[]; + for struct_def.fields.each |struct_field| { + match struct_field.node.kind { + named_field(ident, _, _) => { + // Create the accessor for this field. + let self_field = build::mk_access( + cx, + span, + ~[self_ident], + ident + ); + + // Call the substructure method. + let encode_expr = call_substructure_encode_method( + cx, + span, + self_field + ); + + let blk_expr = build::mk_lambda( + cx, + span, + build::mk_fn_decl(~[], build::mk_ty_infer(cx, span)), + encode_expr + ); + + let call_expr = build::mk_method_call( + cx, + span, + build::mk_path(cx, span, ~[cx.ident_of(~"__e")]), + cx.ident_of(~"emit_struct_field"), + ~[ + build::mk_base_str(cx, span, cx.str_of(ident)), + build::mk_uint(cx, span, idx), + blk_expr + ] + ); + + statements.push(build::mk_stmt(cx, span, call_expr)); + } + unnamed_field => { + cx.span_unimpl( + span, + ~"unnamed fields with `deriving(Encodable)`" + ); + } + } + idx += 1; + } + + let emit_struct_stmt = build::mk_method_call( + cx, + span, + build::mk_path( + cx, + span, + ~[cx.ident_of(~"__e")] + ), + cx.ident_of(~"emit_struct"), + ~[ + build::mk_base_str(cx, span, cx.str_of(type_ident)), + build::mk_uint(cx, span, statements.len()), + build::mk_lambda_stmts( + cx, + span, + build::mk_fn_decl(~[], build::mk_ty_infer(cx, span)), + statements + ), + ] + ); + + let statements = ~[build::mk_stmt(cx, span, emit_struct_stmt)]; + + // Create the method itself. + return create_encode_method(cx, span, statements); +} + +fn expand_deriving_encodable_enum_method( + cx: @ext_ctxt, + span: span, + type_ident: ast::ident, + enum_definition: &enum_def +) -> @method { + // Create the arms of the match in the method body. + let arms = do enum_definition.variants.mapi |i, variant| { + // Create the matching pattern. + let pat = create_enum_variant_pattern(cx, span, variant, ~"__self"); + + // Feed the discriminant to the encode function. + let mut stmts = ~[]; + + // Feed each argument in this variant to the encode function + // as well. + let variant_arg_len = variant_arg_count(cx, span, variant); + for uint::range(0, variant_arg_len) |j| { + // Create the expression for this field. + let field_ident = cx.ident_of(~"__self" + j.to_str()); + let field = build::mk_path(cx, span, ~[ field_ident ]); + + // Call the substructure method. + let expr = call_substructure_encode_method(cx, span, field); + + let blk_expr = build::mk_lambda( + cx, + span, + build::mk_fn_decl(~[], build::mk_ty_infer(cx, span)), + expr + ); + + let call_expr = build::mk_method_call( + cx, + span, + build::mk_path(cx, span, ~[cx.ident_of(~"__e")]), + cx.ident_of(~"emit_enum_variant_arg"), + ~[ + build::mk_uint(cx, span, j), + blk_expr, + ] + ); + + stmts.push(build::mk_stmt(cx, span, call_expr)); + } + + // Create the pattern body. + let call_expr = build::mk_method_call( + cx, + span, + build::mk_path(cx, span, ~[cx.ident_of(~"__e")]), + cx.ident_of(~"emit_enum_variant"), + ~[ + build::mk_base_str(cx, span, cx.str_of(variant.node.name)), + build::mk_uint(cx, span, i), + build::mk_uint(cx, span, variant_arg_len), + build::mk_lambda_stmts( + cx, + span, + build::mk_fn_decl(~[], build::mk_ty_infer(cx, span)), + stmts + ) + ] + ); + + let match_body_block = build::mk_simple_block(cx, span, call_expr); + + // Create the arm. + ast::arm { + pats: ~[pat], + guard: None, + body: match_body_block, + } + }; + + // Create the method body. + let lambda_expr = build::mk_lambda( + cx, + span, + build::mk_fn_decl(~[], build::mk_ty_infer(cx, span)), + expand_enum_or_struct_match(cx, span, arms) + ); + + let call_expr = build::mk_method_call( + cx, + span, + build::mk_path(cx, span, ~[cx.ident_of(~"__e")]), + cx.ident_of(~"emit_enum"), + ~[ + build::mk_base_str(cx, span, cx.str_of(type_ident)), + lambda_expr, + ] + ); + + let stmt = build::mk_stmt(cx, span, call_expr); + + // Create the method. + create_encode_method(cx, span, ~[stmt]) +} diff --git a/src/libsyntax/ext/deriving/eq.rs b/src/libsyntax/ext/deriving/eq.rs index 07b2835d44c..c427a206c2e 100644 --- a/src/libsyntax/ext/deriving/eq.rs +++ b/src/libsyntax/ext/deriving/eq.rs @@ -17,6 +17,7 @@ use ext::build; use ext::deriving::*; use codemap::{span, spanned}; use ast_util; +use opt_vec; use core::uint; @@ -124,12 +125,13 @@ fn create_derived_eq_impl(cx: @ext_ctxt, ne_method: @method) -> @item { let methods = [ eq_method, ne_method ]; - let trait_path = [ + let trait_path = ~[ cx.ident_of(~"core"), cx.ident_of(~"cmp"), cx.ident_of(~"Eq") ]; - create_derived_impl(cx, span, type_ident, generics, methods, trait_path) + let trait_path = build::mk_raw_path_global(span, trait_path); + create_derived_impl(cx, span, type_ident, generics, methods, trait_path, opt_vec::Empty) } fn call_substructure_eq_method(cx: @ext_ctxt, @@ -289,7 +291,7 @@ fn expand_deriving_eq_struct_method(cx: @ext_ctxt, &mut outer_expr); } unnamed_field => { - cx.span_unimpl(span, ~"unnamed fields with `deriving_eq`"); + cx.span_unimpl(span, ~"unnamed fields with `deriving(Eq)`"); } } } diff --git a/src/libsyntax/ext/deriving/iter_bytes.rs b/src/libsyntax/ext/deriving/iter_bytes.rs index e2a43591ef0..4124e6ee6c1 100644 --- a/src/libsyntax/ext/deriving/iter_bytes.rs +++ b/src/libsyntax/ext/deriving/iter_bytes.rs @@ -17,6 +17,7 @@ use ext::build; use ext::deriving::*; use codemap::{span, spanned}; use ast_util; +use opt_vec; use core::uint; @@ -49,12 +50,13 @@ fn create_derived_iter_bytes_impl(cx: @ext_ctxt, method: @method) -> @item { let methods = [ method ]; - let trait_path = [ + let trait_path = ~[ cx.ident_of(~"core"), cx.ident_of(~"to_bytes"), cx.ident_of(~"IterBytes") ]; - create_derived_impl(cx, span, type_ident, generics, methods, trait_path) + let trait_path = build::mk_raw_path_global(span, trait_path); + create_derived_impl(cx, span, type_ident, generics, methods, trait_path, opt_vec::Empty) } // Creates a method from the given set of statements conforming to the @@ -191,7 +193,7 @@ fn expand_deriving_iter_bytes_struct_method(cx: @ext_ctxt, } unnamed_field => { cx.span_unimpl(span, - ~"unnamed fields with `deriving_iter_bytes`"); + ~"unnamed fields with `deriving(IterBytes)`"); } } } diff --git a/src/libsyntax/ext/deriving/mod.rs b/src/libsyntax/ext/deriving/mod.rs index 4337546930f..63106eae48a 100644 --- a/src/libsyntax/ext/deriving/mod.rs +++ b/src/libsyntax/ext/deriving/mod.rs @@ -14,7 +14,7 @@ use core::prelude::*; use ast; -use ast::{TraitTyParamBound, Ty, bind_by_ref, deref, enum_def}; +use ast::{Ty, bind_by_ref, deref, enum_def}; use ast::{expr, expr_match, ident, item, item_}; use ast::{item_enum, item_impl, item_struct, Generics}; use ast::{m_imm, meta_item, method}; @@ -33,6 +33,8 @@ use core::uint; pub mod clone; pub mod eq; pub mod iter_bytes; +pub mod encodable; +pub mod decodable; type ExpandDerivingStructDefFn<'self> = &'self fn(@ext_ctxt, span, @@ -76,6 +78,10 @@ pub fn expand_meta_deriving(cx: @ext_ctxt, titem, in_items), ~"IterBytes" => iter_bytes::expand_deriving_iter_bytes(cx, titem.span, titem, in_items), + ~"Encodable" => encodable::expand_deriving_encodable(cx, + titem.span, titem, in_items), + ~"Decodable" => decodable::expand_deriving_decodable(cx, + titem.span, titem, in_items), tname => { cx.span_err(titem.span, fmt!("unknown \ `deriving` trait: `%s`", tname)); @@ -153,12 +159,13 @@ pub fn create_self_type_with_params(cx: @ext_ctxt, } pub fn create_derived_impl(cx: @ext_ctxt, - span: span, - type_ident: ident, - generics: &Generics, - methods: &[@method], - trait_path: &[ident]) - -> @item { + span: span, + type_ident: ident, + generics: &Generics, + methods: &[@method], + trait_path: @ast::Path, + mut impl_ty_params: opt_vec::OptVec<ast::TyParam>) + -> @item { /*! * * Given that we are deriving a trait `Tr` for a type `T<'a, ..., @@ -175,29 +182,16 @@ pub fn create_derived_impl(cx: @ext_ctxt, build::mk_lifetime(cx, l.span, l.ident) }); - // Create the type parameters. - let impl_ty_params = generics.ty_params.map(|ty_param| { - let bound = build::mk_trait_ref_global(cx, - span, - trait_path.map(|x| *x)); - let bounds = @opt_vec::with(TraitTyParamBound(bound)); - build::mk_ty_param(cx, ty_param.ident, bounds) - }); - // Create the reference to the trait. - let trait_path = ast::Path { - span: span, - global: true, - idents: trait_path.map(|x| *x), - rp: None, - types: ~[] - }; - let trait_path = @trait_path; - let trait_ref = ast::trait_ref { - path: trait_path, - ref_id: cx.next_id() + let trait_ref = build::mk_trait_ref_(cx, trait_path); + + // Create the type parameters. + for generics.ty_params.each |ty_param| { + let bounds = @opt_vec::with( + build::mk_trait_ty_param_bound_(cx, trait_path) + ); + impl_ty_params.push(build::mk_ty_param(cx, ty_param.ident, bounds)); }; - let trait_ref = @trait_ref; // Create the type of `self`. let self_type = create_self_type_with_params(cx, diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index fa1b9727566..4e7e5144a7d 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -95,7 +95,6 @@ pub fn rust_printer(writer: @io::Writer, intr: @ident_interner) -> @ps { } pub static indent_unit: uint = 4u; -pub static match_indent_unit: uint = 2u; pub static default_columns: uint = 78u; @@ -1227,16 +1226,16 @@ pub fn print_expr(s: @ps, &&expr: @ast::expr) { print_block(s, blk); } ast::expr_match(expr, ref arms) => { - cbox(s, match_indent_unit); + cbox(s, indent_unit); ibox(s, 4); word_nbsp(s, ~"match"); print_expr(s, expr); space(s.s); bopen(s); - let len = (*arms).len(); - for (*arms).eachi |i, arm| { + let len = arms.len(); + for arms.eachi |i, arm| { space(s.s); - cbox(s, match_indent_unit); + cbox(s, indent_unit); ibox(s, 0u); let mut first = true; for arm.pats.each |p| { @@ -1269,7 +1268,7 @@ pub fn print_expr(s: @ps, &&expr: @ast::expr) { ast::expr_block(ref blk) => { // the block will close the pattern's ibox print_block_unclosed_indent( - s, blk, match_indent_unit); + s, blk, indent_unit); } _ => { end(s); // close the ibox for the pattern @@ -1286,10 +1285,10 @@ pub fn print_expr(s: @ps, &&expr: @ast::expr) { } } else { // the block will close the pattern's ibox - print_block_unclosed_indent(s, &arm.body, match_indent_unit); + print_block_unclosed_indent(s, &arm.body, indent_unit); } } - bclose_(s, expr.span, match_indent_unit); + bclose_(s, expr.span, indent_unit); } ast::expr_fn_block(ref decl, ref body) => { // in do/for blocks we don't want to show an empty |
