]> git.lizzy.rs Git - rust.git/commitdiff
syntax: add support for #[deriving(Encodable)]
authorErick Tryzelaar <erick.tryzelaar@gmail.com>
Wed, 10 Apr 2013 23:31:51 +0000 (16:31 -0700)
committerErick Tryzelaar <erick.tryzelaar@gmail.com>
Wed, 10 Apr 2013 23:32:09 +0000 (16:32 -0700)
src/libsyntax/ext/build.rs
src/libsyntax/ext/deriving/encodable.rs [new file with mode: 0644]
src/libsyntax/ext/deriving/mod.rs

index 0f84ac41532726413745e0c5cbcf7bce2cb3d673..530bb8f15417aea85a08b31c7ca5782b5a203262 100644 (file)
@@ -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<ast::TyParamBound>)
diff --git a/src/libsyntax/ext/deriving/encodable.rs b/src/libsyntax/ext/deriving/encodable.rs
new file mode 100644 (file)
index 0000000..81bfb03
--- /dev/null
@@ -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])
+}
index 95bca7ff230c7bf00956e92892abdbada299333c..5242d54208758fbaa78307bc995d98bf806cd4a5 100644 (file)
@@ -33,6 +33,7 @@
 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));