syntax: add support for #[deriving(Decodable)]
This commit is contained in:
parent
5841564bf9
commit
441df26f5a
@ -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))
|
||||
}
|
||||
|
454
src/libsyntax/ext/deriving/decodable.rs
Normal file
454
src/libsyntax/ext/deriving/decodable.rs
Normal file
@ -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)
|
||||
}
|
@ -34,6 +34,7 @@ pub mod clone;
|
||||
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));
|
||||
|
Loading…
x
Reference in New Issue
Block a user