]> git.lizzy.rs Git - rust.git/commitdiff
syntax: add support for #[deriving(Decodable)]
authorErick Tryzelaar <erick.tryzelaar@gmail.com>
Tue, 9 Apr 2013 01:53:39 +0000 (18:53 -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/decodable.rs [new file with mode: 0644]
src/libsyntax/ext/deriving/mod.rs

index 530bb8f15417aea85a08b31c7ca5782b5a203262..b375adef9263d32bef39ad166d094526f45e4362 100644 (file)
@@ -279,10 +279,10 @@ pub fn mk_lambda_(cx: @ext_ctxt,
     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 {
+                 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)
 }
@@ -294,6 +294,13 @@ pub fn mk_lambda_stmts(cx: @ext_ctxt,
     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))
 }
@@ -303,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,
@@ -435,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/decodable.rs b/src/libsyntax/ext/deriving/decodable.rs
new file mode 100644 (file)
index 0000000..11f4923
--- /dev/null
@@ -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)
+}
index 5242d54208758fbaa78307bc995d98bf806cd4a5..63106eae48ae852d4672f8573d8dd19ab799dcef 100644 (file)
@@ -34,6 +34,7 @@
 pub mod eq;
 pub mod iter_bytes;
 pub mod encodable;
+pub mod decodable;
 
 type ExpandDerivingStructDefFn<'self> = &'self fn(@ext_ctxt,
                                                   span,
@@ -79,6 +80,8 @@ pub fn expand_meta_deriving(cx: @ext_ctxt,
                                 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));