diff --git a/src/libsyntax/ext/deriving/decodable.rs b/src/libsyntax/ext/deriving/decodable.rs index 4ae4533956e..1991b2456d9 100644 --- a/src/libsyntax/ext/deriving/decodable.rs +++ b/src/libsyntax/ext/deriving/decodable.rs @@ -14,410 +14,150 @@ */ use core::prelude::*; - -use ast::*; -use ast; -use ast_util; -use codemap::{span, spanned}; -use ext::base::ExtCtxt; -use ext::build::AstBuilder; -use ext::deriving::*; -use opt_vec; - +use core::vec; use core::uint; -pub fn expand_deriving_decodable( - cx: @ExtCtxt, - 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 - ) -} +use ast::{meta_item, item, expr, m_mutbl}; +use codemap::span; +use ext::base::ExtCtxt; +use ext::build::AstBuilder; +use ext::deriving::generic::*; -fn create_derived_decodable_impl( - cx: @ExtCtxt, - span: span, - type_ident: ident, - generics: &Generics, - method: @method -) -> @item { - let decoder_ty_param = cx.typaram( - cx.ident_of("__D"), - @opt_vec::with( - cx.typarambound( - cx.path_global( - span, - ~[ - cx.ident_of("extra"), - 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 = cx.path_all( - span, - true, - ~[ - cx.ident_of("extra"), - cx.ident_of("serialize"), - cx.ident_of("Decodable") - ], - None, - ~[ - cx.ty_ident(span, cx.ident_of("__D")) +pub fn expand_deriving_decodable(cx: @ExtCtxt, + span: span, + mitem: @meta_item, + in_items: ~[@item]) -> ~[@item] { + let trait_def = TraitDef { + path: Path::new_(~["extra", "serialize", "Decodable"], None, + ~[~Literal(Path::new_local("__D"))], true), + additional_bounds: ~[], + generics: LifetimeBounds { + lifetimes: ~[], + bounds: ~[("__D", ~[Path::new(~["extra", "serialize", "Decoder"])])], + }, + methods: ~[ + MethodDef { + name: "decode", + generics: LifetimeBounds::empty(), + explicit_self: None, + args: ~[Ptr(~Literal(Path::new_local("__D")), + Borrowed(None, m_mutbl))], + ret_ty: Self, + const_nonmatching: true, + combine_substructure: decodable_substructure, + }, ] - ); - create_derived_impl( - cx, - span, - type_ident, - generics, - methods, - trait_path, - Generics { ty_params: generic_ty_params, lifetimes: opt_vec::Empty }, - opt_vec::Empty - ) + }; + + expand_deriving_generic(cx, span, mitem, in_items, + &trait_def) } -// Creates a method from the given set of statements conforming to the -// signature of the `decodable` method. -fn create_decode_method( - cx: @ExtCtxt, - span: span, - type_ident: ast::ident, - generics: &Generics, - expr: @ast::expr -) -> @method { - // Create the `e` parameter. - let d_arg_type = cx.ty_rptr( - span, - cx.ty_ident(span, cx.ident_of("__D")), - None, - ast::m_mutbl - ); - let d_ident = cx.ident_of("__d"); - let d_arg = cx.arg(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 = cx.fn_decl(inputs, output_type); - - // Create the body block. - let body_block = cx.blk_expr(expr); - - // Create the method. - let explicit_self = spanned { node: sty_static, span: span }; - let method_ident = cx.ident_of("decode"); - @ast::method { - ident: method_ident, - attrs: ~[], - generics: ast_util::empty_generics(), - explicit_self: explicit_self, - 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: @ExtCtxt, - span: span -) -> @ast::expr { - // Call the substructure method. - cx.expr_call( - span, - cx.expr_path( - cx.path_global( - span, - ~[ - cx.ident_of("extra"), +fn decodable_substructure(cx: @ExtCtxt, span: span, + substr: &Substructure) -> @expr { + let decoder = substr.nonself_args[0]; + let recurse = ~[cx.ident_of("extra"), cx.ident_of("serialize"), cx.ident_of("Decodable"), - cx.ident_of("decode"), - ] - ) - ), - ~[ - cx.expr_ident(span, cx.ident_of("__d")) - ] - ) -} + cx.ident_of("decode")]; + // throw an underscore in front to suppress unused variable warnings + let blkarg = cx.ident_of("_d"); + let blkdecoder = cx.expr_ident(span, blkarg); + let calldecode = cx.expr_call_global(span, recurse, ~[blkdecoder]); + let lambdadecode = cx.lambda_expr_1(span, calldecode, blkarg); -fn expand_deriving_decodable_struct_def( - cx: @ExtCtxt, - 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 - ); + return match *substr.fields { + StaticStruct(_, ref summary) => { + let nfields = match *summary { + Left(n) => n, Right(ref fields) => fields.len() + }; + let read_struct_field = cx.ident_of("read_struct_field"); - // Create the implementation. - create_derived_decodable_impl( - cx, - span, - type_ident, - generics, - method - ) -} + let getarg = |name: ~str, field: uint| { + cx.expr_method_call(span, blkdecoder, read_struct_field, + ~[cx.expr_str(span, name), + cx.expr_uint(span, field), + lambdadecode]) + }; -fn expand_deriving_decodable_enum_def( - cx: @ExtCtxt, - 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 - ); + let result = match *summary { + Left(n) => { + if n == 0 { + cx.expr_ident(span, substr.type_ident) + } else { + let mut fields = vec::with_capacity(n); + for uint::range(0, n) |i| { + fields.push(getarg(fmt!("_field%u", i), i)); + } + cx.expr_call_ident(span, substr.type_ident, fields) + } + } + Right(ref fields) => { + let fields = do fields.mapi |i, f| { + cx.field_imm(span, *f, getarg(cx.str_of(*f), i)) + }; + cx.expr_struct_ident(span, substr.type_ident, fields) + } + }; - // Create the implementation. - create_derived_decodable_impl( - cx, - span, - type_ident, - generics, - method - ) -} + cx.expr_method_call(span, decoder, cx.ident_of("read_struct"), + ~[cx.expr_str(span, cx.str_of(substr.type_ident)), + cx.expr_uint(span, nfields), + cx.lambda_expr_1(span, result, blkarg)]) + } + StaticEnum(_, ref fields) => { + let variant = cx.ident_of("i"); -fn create_read_struct_field( - cx: @ExtCtxt, - span: span, - idx: uint, - ident: ident -) -> ast::field { - // Call the substructure method. - let decode_expr = call_substructure_decode_method(cx, span); + let mut arms = ~[]; + let mut variants = ~[]; + let rvariant_arg = cx.ident_of("read_enum_variant_arg"); - let d_id = cx.ident_of("__d"); + for fields.eachi |i, f| { + let (name, parts) = match *f { (i, ref p) => (i, p) }; + variants.push(cx.expr_str(span, cx.str_of(name))); - let call_expr = cx.expr_method_call( - span, - cx.expr_ident(span, d_id), - cx.ident_of("read_struct_field"), - ~[ - cx.expr_str(span, cx.str_of(ident)), - cx.expr_uint(span, idx), - cx.lambda_expr_1(span, decode_expr, d_id) - ] - ); + let getarg = |field: uint| { + cx.expr_method_call(span, blkdecoder, rvariant_arg, + ~[cx.expr_uint(span, field), + lambdadecode]) + }; - cx.field_imm(span, ident, call_expr) -} - -fn create_read_struct_arg( - cx: @ExtCtxt, - span: span, - idx: uint, - ident: ident -) -> ast::field { - // Call the substructure method. - let decode_expr = call_substructure_decode_method(cx, span); - - let call_expr = cx.expr_method_call( - span, - cx.expr_ident(span, cx.ident_of("__d")), - cx.ident_of("read_struct_arg"), - ~[ - cx.expr_uint(span, idx), - cx.lambda_expr_0(span, decode_expr), - ] - ); - - cx.field_imm(span, ident, call_expr) -} - -fn expand_deriving_decodable_struct_method( - cx: @ExtCtxt, - 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)`" - ); + let decoded = match *parts { + Left(n) => { + if n == 0 { + cx.expr_ident(span, name) + } else { + let mut fields = vec::with_capacity(n); + for uint::range(0, n) |i| { + fields.push(getarg(i)); + } + cx.expr_call_ident(span, name, fields) + } + } + Right(ref fields) => { + let fields = do fields.mapi |i, f| { + cx.field_imm(span, *f, getarg(i)) + }; + cx.expr_struct_ident(span, name, fields) + } + }; + arms.push(cx.arm(span, + ~[cx.pat_lit(span, cx.expr_uint(span, i))], + decoded)); } + + arms.push(cx.arm_unreachable(span)); + + let result = cx.expr_match(span, cx.expr_ident(span, variant), arms); + let lambda = cx.lambda_expr(span, ~[blkarg, variant], result); + let variant_vec = cx.expr_vec(span, variants); + let result = cx.expr_method_call(span, blkdecoder, + cx.ident_of("read_enum_variant"), + ~[variant_vec, lambda]); + cx.expr_method_call(span, decoder, cx.ident_of("read_enum"), + ~[cx.expr_str(span, cx.str_of(substr.type_ident)), + cx.lambda_expr_1(span, result, blkarg)]) } - i += 1; - } - - let d_id = cx.ident_of("__d"); - - let read_struct_expr = cx.expr_method_call( - span, - cx.expr_ident(span, d_id), - cx.ident_of("read_struct"), - ~[ - cx.expr_str(span, cx.str_of(type_ident)), - cx.expr_uint(span, fields.len()), - cx.lambda_expr_1( - span, - cx.expr_struct_ident(span, type_ident, fields), - d_id) - ] - ); - - // Create the method itself. - create_decode_method(cx, span, type_ident, generics, read_struct_expr) -} - -fn create_read_variant_arg( - cx: @ExtCtxt, - span: span, - idx: uint, - variant: &ast::variant -) -> ast::arm { - // Create the matching pattern. - let pat = cx.pat_lit(span, cx.expr_uint(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 { - cx.expr_ident(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 d_id = cx.ident_of("__d"); - - let call_expr = cx.expr_method_call( - span, - cx.expr_ident(span, d_id), - cx.ident_of("read_enum_variant_arg"), - ~[ - cx.expr_uint(span, j), - cx.lambda_expr_1(span, expr, d_id), - ] - ); - - args.push(call_expr); - } - - cx.expr_call_ident(span, variant.node.name, args) + _ => cx.bug("expected StaticEnum or StaticStruct in deriving(Decodable)") }; - - // Create the arm. - cx.arm(span, ~[pat], expr) -} - -fn create_read_enum_variant( - cx: @ExtCtxt, - span: span, - enum_definition: &enum_def -) -> @expr { - // Create a vector that contains all the variant names. - let expr_arm_names = cx.expr_vec( - span, - do enum_definition.variants.map |variant| { - cx.expr_str( - 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(cx.arm_unreachable(span)); - - // Create the read_enum_variant expression. - cx.expr_method_call( - span, - cx.expr_ident(span, cx.ident_of("__d")), - cx.ident_of("read_enum_variant"), - ~[ - expr_arm_names, - cx.lambda_expr(span, - ~[cx.ident_of("__d"), cx.ident_of("__i")], - cx.expr_match(span, cx.expr_ident(span, cx.ident_of("__i")), arms)) - ] - ) -} - -fn expand_deriving_decodable_enum_method( - cx: @ExtCtxt, - 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 - ); - - let d_id = cx.ident_of("__d"); - - // Create the read_enum expression - let read_enum_expr = cx.expr_method_call( - span, - cx.expr_ident(span, d_id), - cx.ident_of("read_enum"), - ~[ - cx.expr_str(span, cx.str_of(type_ident)), - cx.lambda_expr_1(span, read_enum_variant_expr, d_id) - ] - ); - - // 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 index 6ca7d0b7b28..b9c4bf7bf26 100644 --- a/src/libsyntax/ext/deriving/encodable.rs +++ b/src/libsyntax/ext/deriving/encodable.rs @@ -77,578 +77,113 @@ fn decode(d: &D) -> spanned { use core::prelude::*; -use ast; -use ast::*; +use ast::{meta_item, item, expr, m_imm, m_mutbl}; +use codemap::span; use ext::base::ExtCtxt; use ext::build::AstBuilder; -use ext::deriving::*; -use codemap::{span, spanned}; -use ast_util; -use opt_vec; +use ext::deriving::generic::*; -pub fn expand_deriving_encodable( - cx: @ExtCtxt, - 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: @ExtCtxt, - span: span, - type_ident: ident, - generics: &Generics, - method: @method -) -> @item { - let encoder_ty_param = cx.typaram( - cx.ident_of("__E"), - @opt_vec::with( - cx.typarambound( - cx.path_global( - span, - ~[ - cx.ident_of("extra"), - 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 = cx.path_all( - span, - true, - ~[ - cx.ident_of("extra"), - cx.ident_of("serialize"), - cx.ident_of("Encodable") - ], - None, - ~[ - cx.ty_ident(span, cx.ident_of("__E")) +pub fn expand_deriving_encodable(cx: @ExtCtxt, + span: span, + mitem: @meta_item, + in_items: ~[@item]) -> ~[@item] { + let trait_def = TraitDef { + path: Path::new_(~["extra", "serialize", "Encodable"], None, + ~[~Literal(Path::new_local("__E"))], true), + additional_bounds: ~[], + generics: LifetimeBounds { + lifetimes: ~[], + bounds: ~[("__E", ~[Path::new(~["extra", "serialize", "Encoder"])])], + }, + methods: ~[ + MethodDef { + name: "encode", + generics: LifetimeBounds::empty(), + explicit_self: Some(Some(Borrowed(None, m_imm))), + args: ~[Ptr(~Literal(Path::new_local("__E")), + Borrowed(None, m_mutbl))], + ret_ty: nil_ty(), + const_nonmatching: true, + combine_substructure: encodable_substructure, + }, ] - ); - create_derived_impl( - cx, - span, - type_ident, - generics, - methods, - trait_path, - Generics { ty_params: generic_ty_params, lifetimes: opt_vec::Empty }, - opt_vec::Empty - ) -} - -// Creates a method from the given set of statements conforming to the -// signature of the `encodable` method. -fn create_encode_method( - cx: @ExtCtxt, - span: span, - statements: ~[@stmt] -) -> @method { - // Create the `e` parameter. - let e_arg_type = cx.ty_rptr( - span, - cx.ty_ident(span, cx.ident_of("__E")), - None, - ast::m_mutbl - ); - let e_arg = cx.arg(span, cx.ident_of("__e"), e_arg_type); - - // Create the type of the return value. - let output_type = cx.ty_nil(); - - // Create the function declaration. - let inputs = ~[e_arg]; - let fn_decl = cx.fn_decl(inputs, output_type); - - // Create the body block. - let body_block = cx.blk(span, statements, None); - - // Create the method. - let explicit_self = 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(), - explicit_self: explicit_self, - 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: @ExtCtxt, - 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 = cx.expr_ident(span, e_ident); - - // Call the substructure method. - let encode_ident = cx.ident_of("encode"); - cx.expr_method_call( - span, - self_field, - encode_ident, - ~[e_expr] - ) -} - -fn expand_deriving_encodable_struct_def( - cx: @ExtCtxt, - 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: @ExtCtxt, - 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: @ExtCtxt, - span: span, - type_ident: ident, - struct_def: &struct_def -) -> @method { - // 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 = cx.expr_field_access(span, - cx.expr_self(span), - ident); - - // Call the substructure method. - let encode_expr = call_substructure_encode_method( - cx, - span, - self_field - ); - - let e_ident = cx.ident_of("__e"); - - let call_expr = cx.expr_method_call( - span, - cx.expr_ident(span, e_ident), - cx.ident_of("emit_struct_field"), - ~[ - cx.expr_str(span, cx.str_of(ident)), - cx.expr_uint(span, idx), - cx.lambda_expr_1(span, encode_expr, e_ident) - ] - ); - - statements.push(cx.stmt_expr(call_expr)); - } - unnamed_field => { - cx.span_unimpl( - span, - "unnamed fields with `deriving(Encodable)`" - ); - } - } - idx += 1; - } - - let e_id = cx.ident_of("__e"); - let emit_struct_stmt = cx.expr_method_call( - span, - cx.expr_ident(span, e_id), - cx.ident_of("emit_struct"), - ~[ - cx.expr_str(span, cx.str_of(type_ident)), - cx.expr_uint(span, statements.len()), - cx.lambda_stmts_1(span, statements, e_id), - ] - ); - - let statements = ~[cx.stmt_expr(emit_struct_stmt)]; - - // Create the method itself. - return create_encode_method(cx, span, statements); -} - -fn expand_deriving_encodable_enum_method( - cx: @ExtCtxt, - 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, fields) = create_enum_variant_pattern(cx, span, variant, "__self", ast::m_imm); - - // 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 fields.eachi |j, &(_, field)| { - // Call the substructure method. - let expr = call_substructure_encode_method(cx, span, field); - - let e_ident = cx.ident_of("__e"); - let call_expr = cx.expr_method_call( - span, - cx.expr_ident(span, e_ident), - cx.ident_of("emit_enum_variant_arg"), - ~[ - cx.expr_uint(span, j), - cx.lambda_expr_1(span, expr, e_ident), - ] - ); - - stmts.push(cx.stmt_expr(call_expr)); - } - - // Create the pattern body. - let e_id = cx.ident_of("__e"); - - let call_expr = cx.expr_method_call( - span, - cx.expr_ident(span, e_id), - cx.ident_of("emit_enum_variant"), - ~[ - cx.expr_str(span, cx.str_of(variant.node.name)), - cx.expr_uint(span, i), - cx.expr_uint(span, variant_arg_len), - cx.lambda_stmts_1(span, stmts, e_id) - ] - ); - - //let match_body_block = cx.blk_expr(call_expr); - - // Create the arm. - cx.arm(span, ~[pat], call_expr) //match_body_block) }; - let e_ident = cx.ident_of("__e"); - - // Create the method body. - let lambda_expr = cx.lambda_expr_1( - span, - expand_enum_or_struct_match(cx, span, arms), - e_ident); - - let call_expr = cx.expr_method_call( - span, - cx.expr_ident(span, e_ident), - cx.ident_of("emit_enum"), - ~[ - cx.expr_str(span, cx.str_of(type_ident)), - lambda_expr, - ] - ); - - let stmt = cx.stmt_expr(call_expr); - - // Create the method. - create_encode_method(cx, span, ~[stmt]) + expand_deriving_generic(cx, span, mitem, in_items, + &trait_def) } -#[cfg(test)] -mod test { - extern mod extra; - use core::option::{None, Some}; - use extra::serialize::Encodable; - use extra::serialize::Encoder; +fn encodable_substructure(cx: @ExtCtxt, span: span, + substr: &Substructure) -> @expr { + let encoder = substr.nonself_args[0]; + // throw an underscore in front to suppress unused variable warnings + let blkarg = cx.ident_of("_e"); + let blkencoder = cx.expr_ident(span, blkarg); + let encode = cx.ident_of("encode"); - // just adding the ones I want to test, for now: - #[deriving(Eq)] - pub enum call { - CallToEmitEnum(~str), - CallToEmitEnumVariant(~str, uint, uint), - CallToEmitEnumVariantArg(uint), - CallToEmitUint(uint), - CallToEmitNil, - CallToEmitStruct(~str,uint), - CallToEmitField(~str,uint), - CallToEmitOption, - CallToEmitOptionNone, - CallToEmitOptionSome, - // all of the ones I was too lazy to handle: - CallToOther - } - // using `@mut` rather than changing the - // type of self in every method of every encoder everywhere. - pub struct TestEncoder {call_log : @mut ~[call]} + return match *substr.fields { + Struct(ref fields) => { + let emit_struct_field = cx.ident_of("emit_struct_field"); + let mut stmts = ~[]; + for fields.eachi |i, f| { + let (name, val) = match *f { + (Some(id), e, _) => (cx.str_of(id), e), + (None, e, _) => (fmt!("_field%u", i), e) + }; + let enc = cx.expr_method_call(span, val, encode, ~[blkencoder]); + let lambda = cx.lambda_expr_1(span, enc, blkarg); + let call = cx.expr_method_call(span, blkencoder, + emit_struct_field, + ~[cx.expr_str(span, name), + cx.expr_uint(span, i), + lambda]); + stmts.push(cx.stmt_expr(call)); + } - pub impl TestEncoder { - // these self's should be &mut self's, as well.... - fn add_to_log (&self, c : call) { - self.call_log.push(copy c); - } - fn add_unknown_to_log (&self) { - self.add_to_log (CallToOther) - } - } - - impl Encoder for TestEncoder { - fn emit_nil(&mut self) { self.add_to_log(CallToEmitNil) } - - fn emit_uint(&mut self, v: uint) { - self.add_to_log(CallToEmitUint(v)); - } - fn emit_u64(&mut self, _v: u64) { self.add_unknown_to_log(); } - fn emit_u32(&mut self, _v: u32) { self.add_unknown_to_log(); } - fn emit_u16(&mut self, _v: u16) { self.add_unknown_to_log(); } - fn emit_u8(&mut self, _v: u8) { self.add_unknown_to_log(); } - - fn emit_int(&mut self, _v: int) { self.add_unknown_to_log(); } - fn emit_i64(&mut self, _v: i64) { self.add_unknown_to_log(); } - fn emit_i32(&mut self, _v: i32) { self.add_unknown_to_log(); } - fn emit_i16(&mut self, _v: i16) { self.add_unknown_to_log(); } - fn emit_i8(&mut self, _v: i8) { self.add_unknown_to_log(); } - - fn emit_bool(&mut self, _v: bool) { self.add_unknown_to_log(); } - - fn emit_f64(&mut self, _v: f64) { self.add_unknown_to_log(); } - fn emit_f32(&mut self, _v: f32) { self.add_unknown_to_log(); } - fn emit_float(&mut self, _v: float) { self.add_unknown_to_log(); } - - fn emit_char(&mut self, _v: char) { self.add_unknown_to_log(); } - fn emit_str(&mut self, _v: &str) { self.add_unknown_to_log(); } - - fn emit_enum(&mut self, name: &str, f: &fn(&mut TestEncoder)) { - self.add_to_log(CallToEmitEnum(name.to_str())); - f(self); + let blk = cx.lambda_stmts_1(span, stmts, blkarg); + cx.expr_method_call(span, encoder, cx.ident_of("emit_struct"), + ~[cx.expr_str(span, cx.str_of(substr.type_ident)), + cx.expr_uint(span, fields.len()), + blk]) } - fn emit_enum_variant(&mut self, - name: &str, - id: uint, - cnt: uint, - f: &fn(&mut TestEncoder)) { - self.add_to_log(CallToEmitEnumVariant(name.to_str(), id, cnt)); - f(self); + EnumMatching(idx, variant, ref fields) => { + // We're not generating an AST that the borrow checker is expecting, + // so we need to generate a unique local variable to take the + // mutable loan out on, otherwise we get conflicts which don't + // actually exist. + let me = cx.stmt_let(span, false, blkarg, encoder); + let encoder = cx.expr_ident(span, blkarg); + let emit_variant_arg = cx.ident_of("emit_enum_variant_arg"); + let mut stmts = ~[]; + for fields.eachi |i, f| { + let val = match *f { (_, e, _) => e }; + let enc = cx.expr_method_call(span, val, encode, ~[blkencoder]); + let lambda = cx.lambda_expr_1(span, enc, blkarg); + let call = cx.expr_method_call(span, blkencoder, + emit_variant_arg, + ~[cx.expr_uint(span, i), + lambda]); + stmts.push(cx.stmt_expr(call)); + } + + let blk = cx.lambda_stmts_1(span, stmts, blkarg); + let name = cx.expr_str(span, cx.str_of(variant.node.name)); + let call = cx.expr_method_call(span, blkencoder, + cx.ident_of("emit_enum_variant"), + ~[name, + cx.expr_uint(span, idx), + cx.expr_uint(span, fields.len()), + blk]); + let blk = cx.lambda_expr_1(span, call, blkarg); + let ret = cx.expr_method_call(span, encoder, + cx.ident_of("emit_enum"), + ~[cx.expr_str(span, + cx.str_of(substr.type_ident)), + blk]); + cx.expr_blk(cx.blk(span, ~[me], Some(ret))) } - fn emit_enum_variant_arg(&mut self, - idx: uint, - f: &fn(&mut TestEncoder)) { - self.add_to_log(CallToEmitEnumVariantArg(idx)); - f(self); - } - - fn emit_enum_struct_variant(&mut self, - name: &str, - id: uint, - cnt: uint, - f: &fn(&mut TestEncoder)) { - self.emit_enum_variant(name, id, cnt, f) - } - - fn emit_enum_struct_variant_field(&mut self, - _name: &str, - idx: uint, - f: &fn(&mut TestEncoder)) { - self.emit_enum_variant_arg(idx, f) - } - - fn emit_struct(&mut self, - name: &str, - len: uint, - f: &fn(&mut TestEncoder)) { - self.add_to_log(CallToEmitStruct (name.to_str(),len)); - f(self); - } - fn emit_struct_field(&mut self, - name: &str, - idx: uint, - f: &fn(&mut TestEncoder)) { - self.add_to_log(CallToEmitField (name.to_str(),idx)); - f(self); - } - - fn emit_tuple(&mut self, _len: uint, f: &fn(&mut TestEncoder)) { - self.add_unknown_to_log(); - f(self); - } - fn emit_tuple_arg(&mut self, _idx: uint, f: &fn(&mut TestEncoder)) { - self.add_unknown_to_log(); - f(self); - } - - fn emit_tuple_struct(&mut self, - _name: &str, - _len: uint, - f: &fn(&mut TestEncoder)) { - self.add_unknown_to_log(); - f(self); - } - - fn emit_tuple_struct_arg(&mut self, - _idx: uint, - f: &fn(&mut TestEncoder)) { - self.add_unknown_to_log(); - f(self); - } - - fn emit_option(&mut self, f: &fn(&mut TestEncoder)) { - self.add_to_log(CallToEmitOption); - f(self); - } - fn emit_option_none(&mut self) { - self.add_to_log(CallToEmitOptionNone); - } - fn emit_option_some(&mut self, f: &fn(&mut TestEncoder)) { - self.add_to_log(CallToEmitOptionSome); - f(self); - } - - fn emit_seq(&mut self, _len: uint, f: &fn(&mut TestEncoder)) { - self.add_unknown_to_log(); - f(self); - } - fn emit_seq_elt(&mut self, _idx: uint, f: &fn(&mut TestEncoder)) { - self.add_unknown_to_log(); - f(self); - } - - fn emit_map(&mut self, _len: uint, f: &fn(&mut TestEncoder)) { - self.add_unknown_to_log(); - f(self); - } - fn emit_map_elt_key(&mut self, _idx: uint, f: &fn(&mut TestEncoder)) { - self.add_unknown_to_log(); - f(self); - } - fn emit_map_elt_val(&mut self, _idx: uint, f: &fn(&mut TestEncoder)) { - self.add_unknown_to_log(); - f(self); - } - } - - - fn to_call_log>(val: E) -> ~[call] { - let mut te = TestEncoder { - call_log: @mut ~[] - }; - val.encode(&mut te); - copy *te.call_log - } - - #[deriving(Encodable)] - enum Written { - Book(uint,uint), - Magazine(~str) - } - - #[test] - fn test_encode_enum() { - assert_eq!( - to_call_log(Book(34,44)), - ~[ - CallToEmitEnum(~"Written"), - CallToEmitEnumVariant(~"Book",0,2), - CallToEmitEnumVariantArg(0), - CallToEmitUint(34), - CallToEmitEnumVariantArg(1), - CallToEmitUint(44), - ] - ); - } - - pub struct BPos(uint); - - #[deriving(Encodable)] - pub struct HasPos { pos : BPos } - - #[test] - fn test_encode_newtype() { - assert_eq!( - to_call_log(HasPos { pos:BPos(48) }), - ~[ - CallToEmitStruct(~"HasPos",1), - CallToEmitField(~"pos",0), - CallToEmitUint(48), - ] - ); - } - - #[test] - fn test_encode_option() { - let mut v = None; - - assert_eq!( - to_call_log(v), - ~[ - CallToEmitOption, - CallToEmitOptionNone, - ] - ); - - v = Some(54u); - assert_eq!( - to_call_log(v), - ~[ - CallToEmitOption, - CallToEmitOptionSome, - CallToEmitUint(54) - ] - ); - } + _ => cx.bug("expected Struct or EnumMatching in deriving(Encodable)") + }; } diff --git a/src/test/run-pass/deriving-encodable-decodable.rs b/src/test/run-pass/deriving-encodable-decodable.rs new file mode 100644 index 00000000000..5458dcfa027 --- /dev/null +++ b/src/test/run-pass/deriving-encodable-decodable.rs @@ -0,0 +1,75 @@ +// Copyright 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// This actually tests a lot more than just encodable/decodable, but it gets the +// job done at least + +extern mod extra; + +use std::io; +use std::rand::{random, Rand}; +use extra::serialize::*; +use extra::ebml; +use extra::ebml::writer::Encoder; +use extra::ebml::reader::Decoder; + +#[deriving(Encodable, Decodable, Eq, Rand)] +struct A; +#[deriving(Encodable, Decodable, Eq, Rand)] +struct B(int); +#[deriving(Encodable, Decodable, Eq, Rand)] +struct C(int, int, uint); + +#[deriving(Encodable, Decodable, Eq, Rand)] +struct D { + a: int, + b: uint, +} + +#[deriving(Encodable, Decodable, Eq, Rand)] +enum E { + E1, + E2(uint), + E3(D), + E4{ x: uint }, +} + +#[deriving(Encodable, Decodable, Eq, Rand)] +enum F { F1 } + +#[deriving(Encodable, Decodable, Eq, Rand)] +struct G { + t: T +} + +fn roundtrip + Decodable>() { + let obj: T = random(); + let bytes = do io::with_bytes_writer |w| { + let mut e = Encoder(w); + obj.encode(&mut e); + }; + let doc = ebml::reader::Doc(@bytes); + let mut dec = Decoder(doc); + let obj2 = Decodable::decode(&mut dec); + assert!(obj == obj2); +} + +pub fn main() { + roundtrip::(); + roundtrip::(); + roundtrip::(); + roundtrip::(); + + for 20.times { + roundtrip::(); + roundtrip::(); + roundtrip::>(); + } +}