diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 0f84ac41532..530bb8f1541 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,29 @@ 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_copy(cx: @ext_ctxt, sp: span, e: @ast::expr) -> @ast::expr { mk_expr(cx, sp, ast::expr_copy(e)) } @@ -337,12 +360,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 +417,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) 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 or the MIT license +// , 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/mod.rs b/src/libsyntax/ext/deriving/mod.rs index 95bca7ff230..5242d542087 100644 --- a/src/libsyntax/ext/deriving/mod.rs +++ b/src/libsyntax/ext/deriving/mod.rs @@ -33,6 +33,7 @@ use core::uint; pub mod clone; pub mod eq; pub mod iter_bytes; +pub mod encodable; type ExpandDerivingStructDefFn<'self> = &'self fn(@ext_ctxt, span, @@ -76,6 +77,8 @@ 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), tname => { cx.span_err(titem.span, fmt!("unknown \ `deriving` trait: `%s`", tname));