libsyntax: Implement #[deriving_clone]

This commit is contained in:
Patrick Walton 2013-03-04 15:33:05 -08:00
parent c639a78dc4
commit 0643df28a3
8 changed files with 333 additions and 16 deletions

View File

@ -155,6 +155,9 @@ pub fn syntax_expander_table() -> SyntaxEnv {
syntax_expanders.insert(@~"deriving_iter_bytes",
@SE(ItemDecorator(
ext::deriving::expand_deriving_iter_bytes)));
syntax_expanders.insert(@~"deriving_clone",
@SE(ItemDecorator(
ext::deriving::expand_deriving_clone)));
// Quasi-quoting expanders
syntax_expanders.insert(@~"quote_tokens",

View File

@ -15,7 +15,7 @@ use core::prelude::*;
use ast;
use ast::{TraitTyParamBound, Ty, and, bind_by_ref, binop, deref, enum_def};
use ast::{enum_variant_kind, expr, expr_match, ident, item, item_};
use ast::{enum_variant_kind, expr, expr_match, ident, impure_fn, item, item_};
use ast::{item_enum, item_impl, item_struct, Generics};
use ast::{m_imm, meta_item, method};
use ast::{named_field, or, pat, pat_ident, pat_wild, public, pure_fn};
@ -84,6 +84,18 @@ pub fn expand_deriving_iter_bytes(cx: ext_ctxt,
expand_deriving_iter_bytes_enum_def)
}
pub fn expand_deriving_clone(cx: ext_ctxt,
span: span,
_: @meta_item,
in_items: ~[@item])
-> ~[@item] {
expand_deriving(cx,
span,
in_items,
expand_deriving_clone_struct_def,
expand_deriving_clone_enum_def)
}
fn expand_deriving(cx: ext_ctxt,
span: span,
in_items: ~[@item],
@ -303,6 +315,21 @@ fn create_derived_iter_bytes_impl(cx: ext_ctxt,
create_derived_impl(cx, span, type_ident, generics, methods, trait_path)
}
fn create_derived_clone_impl(cx: ext_ctxt,
span: span,
type_ident: ident,
generics: &Generics,
method: @method)
-> @item {
let methods = [ method ];
let trait_path = [
cx.ident_of(~"core"),
cx.ident_of(~"clone"),
cx.ident_of(~"Clone"),
];
create_derived_impl(cx, span, type_ident, generics, methods, trait_path)
}
// Creates a method from the given set of statements conforming to the
// signature of the `iter_bytes` method.
fn create_iter_bytes_method(cx: ext_ctxt,
@ -352,6 +379,58 @@ fn create_iter_bytes_method(cx: ext_ctxt,
}
}
// Creates a method from the given expression conforming to the signature of
// the `clone` method.
fn create_clone_method(cx: ext_ctxt,
span: span,
+type_ident: ast::ident,
generics: &Generics,
expr: @ast::expr)
-> @method {
// Create the type parameters of the return value.
let mut output_ty_params = ~[];
for generics.ty_params.each |ty_param| {
let path = build::mk_ty_path(cx, span, ~[ ty_param.ident ]);
output_ty_params.push(path);
}
// Create the type of the return value.
let output_type_path = build::mk_raw_path_(span,
~[ type_ident ],
output_ty_params);
let output_type = ast::ty_path(output_type_path, cx.next_id());
let output_type = @ast::Ty {
id: cx.next_id(),
node: output_type,
span: span
};
// Create the function declaration.
let fn_decl = build::mk_fn_decl(~[], output_type);
// Create the body block.
let body_block = build::mk_simple_block(cx, span, expr);
// Create the self type and method identifier.
let self_ty = spanned { node: sty_region(m_imm), span: span };
let method_ident = cx.ident_of(~"clone");
// Create the method.
@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 create_subpatterns(cx: ext_ctxt,
span: span,
prefix: ~str,
@ -372,6 +451,15 @@ fn create_subpatterns(cx: ext_ctxt,
return dvec::unwrap(subpats);
}
fn is_struct_tuple(struct_def: &struct_def) -> bool {
struct_def.fields.len() > 0 && struct_def.fields.all(|f| {
match f.node.kind {
named_field(*) => false,
unnamed_field => true
}
})
}
fn create_enum_variant_pattern(cx: ext_ctxt,
span: span,
variant: &variant,
@ -488,6 +576,16 @@ fn call_substructure_iter_bytes_method(cx: ext_ctxt,
build::mk_stmt(cx, span, self_call)
}
fn call_substructure_clone_method(cx: ext_ctxt,
span: span,
self_field: @expr)
-> @expr {
// Call the substructure method.
let clone_ident = cx.ident_of(~"clone");
let self_method = build::mk_access_(cx, span, self_field, clone_ident);
build::mk_call_(cx, span, self_method, ~[])
}
fn variant_arg_count(cx: ext_ctxt, span: span, variant: &variant) -> uint {
match variant.node.kind {
tuple_variant_kind(ref args) => args.len(),
@ -508,21 +606,12 @@ fn expand_deriving_eq_struct_def(cx: ext_ctxt,
let eq_ident = cx.ident_of(~"eq");
let ne_ident = cx.ident_of(~"ne");
let is_struct_tuple =
struct_def.fields.len() > 0 && struct_def.fields.all(|f| {
match f.node.kind {
named_field(*) => false,
unnamed_field => true
}
});
let derive_struct_fn = if is_struct_tuple {
let derive_struct_fn = if is_struct_tuple(struct_def) {
expand_deriving_eq_struct_tuple_method
} else {
expand_deriving_eq_struct_method
};
let eq_method = derive_struct_fn(cx,
span,
struct_def,
@ -618,6 +707,48 @@ fn expand_deriving_iter_bytes_enum_def(cx: ext_ctxt,
method);
}
fn expand_deriving_clone_struct_def(cx: ext_ctxt,
span: span,
struct_def: &struct_def,
type_ident: ident,
generics: &Generics)
-> @item {
// Create the method.
let method = if !is_struct_tuple(struct_def) {
expand_deriving_clone_struct_method(cx,
span,
struct_def,
type_ident,
generics)
} else {
expand_deriving_clone_tuple_struct_method(cx,
span,
struct_def,
type_ident,
generics)
};
// Create the implementation.
create_derived_clone_impl(cx, span, type_ident, generics, method)
}
fn expand_deriving_clone_enum_def(cx: ext_ctxt,
span: span,
enum_definition: &enum_def,
type_ident: ident,
generics: &Generics)
-> @item {
// Create the method.
let method = expand_deriving_clone_enum_method(cx,
span,
enum_definition,
type_ident,
generics);
// Create the implementation.
create_derived_clone_impl(cx, span, type_ident, generics, method)
}
fn expand_deriving_eq_struct_method(cx: ext_ctxt,
span: span,
struct_def: &struct_def,
@ -709,6 +840,93 @@ fn expand_deriving_iter_bytes_struct_method(cx: ext_ctxt,
return create_iter_bytes_method(cx, span, statements);
}
fn expand_deriving_clone_struct_method(cx: ext_ctxt,
span: span,
struct_def: &struct_def,
type_ident: ident,
generics: &Generics)
-> @method {
let self_ident = cx.ident_of(~"self");
// Create the new fields.
let mut fields = ~[];
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 call = call_substructure_clone_method(cx,
span,
self_field);
let field = build::Field { ident: ident, ex: call };
fields.push(field);
}
unnamed_field => {
cx.span_bug(span,
~"unnamed fields in \
expand_deriving_clone_struct_method");
}
}
}
// Create the struct literal.
let struct_literal = build::mk_struct_e(cx,
span,
~[ type_ident ],
fields);
create_clone_method(cx, span, type_ident, generics, struct_literal)
}
fn expand_deriving_clone_tuple_struct_method(cx: ext_ctxt,
span: span,
struct_def: &struct_def,
type_ident: ident,
generics: &Generics)
-> @method {
// Create the pattern for the match.
let matching_path = build::mk_raw_path(span, ~[ type_ident ]);
let field_count = struct_def.fields.len();
let subpats = create_subpatterns(cx, span, ~"__self", field_count);
let pat = build::mk_pat_enum(cx, span, matching_path, subpats);
// Create the new fields.
let mut subcalls = ~[];
for uint::range(0, struct_def.fields.len()) |i| {
// Create the expression for this field.
let field_ident = cx.ident_of(~"__self" + i.to_str());
let field = build::mk_path(cx, span, ~[ field_ident ]);
// Call the substructure method.
let subcall = call_substructure_clone_method(cx, span, field);
subcalls.push(subcall);
}
// Create the call to the struct constructor.
let call = build::mk_call(cx, span, ~[ type_ident ], subcalls);
// Create the pattern body.
let match_body_block = build::mk_simple_block(cx, span, call);
// Create the arm.
let arm = ast::arm {
pats: ~[ pat ],
guard: None,
body: match_body_block
};
// Create the method body.
let self_match_expr = expand_enum_or_struct_match(cx, span, ~[ arm ]);
// Create the method.
create_clone_method(cx, span, type_ident, generics, self_match_expr)
}
fn expand_deriving_eq_enum_method(cx: ext_ctxt,
span: span,
enum_definition: &enum_def,
@ -904,6 +1122,17 @@ fn expand_deriving_eq_struct_tuple_method(cx: ext_ctxt,
type_ident, generics, self_match_expr)
}
fn expand_enum_or_struct_match(cx: ext_ctxt,
span: span,
arms: ~[ ast::arm ])
-> @expr {
let self_ident = cx.ident_of(~"self");
let self_expr = build::mk_path(cx, span, ~[ self_ident ]);
let self_expr = build::mk_unary(cx, span, deref, self_expr);
let self_match_expr = expr_match(self_expr, arms);
build::mk_expr(cx, span, self_match_expr)
}
fn expand_deriving_iter_bytes_enum_method(cx: ext_ctxt,
span: span,
enum_definition: &enum_def)
@ -953,14 +1182,54 @@ fn expand_deriving_iter_bytes_enum_method(cx: ext_ctxt,
};
// Create the method body.
let self_ident = cx.ident_of(~"self");
let self_expr = build::mk_path(cx, span, ~[ self_ident ]);
let self_expr = build::mk_unary(cx, span, deref, self_expr);
let self_match_expr = expr_match(self_expr, arms);
let self_match_expr = build::mk_expr(cx, span, self_match_expr);
let self_match_expr = expand_enum_or_struct_match(cx, span, arms);
let self_match_stmt = build::mk_stmt(cx, span, self_match_expr);
// Create the method.
create_iter_bytes_method(cx, span, ~[ self_match_stmt ])
}
fn expand_deriving_clone_enum_method(cx: ext_ctxt,
span: span,
enum_definition: &enum_def,
type_ident: ident,
generics: &Generics)
-> @method {
// Create the arms of the match in the method body.
let arms = do enum_definition.variants.map |variant| {
// Create the matching pattern.
let pat = create_enum_variant_pattern(cx, span, variant, ~"__self");
// Iterate over the variant arguments, creating the subcalls.
let mut subcalls = ~[];
for uint::range(0, variant_arg_count(cx, span, variant)) |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 subcall = call_substructure_clone_method(cx, span, field);
subcalls.push(subcall);
}
// Create the call to the enum variant (if necessary).
let call = if subcalls.len() > 0 {
build::mk_call(cx, span, ~[ variant.node.name ], subcalls)
} else {
build::mk_path(cx, span, ~[ variant.node.name ])
};
// Create the pattern body.
let match_body_block = build::mk_simple_block(cx, span, call);
// Create the arm.
ast::arm { pats: ~[ pat ], guard: None, body: match_body_block }
};
// Create the method body.
let self_match_expr = expand_enum_or_struct_match(cx, span, arms);
// Create the method.
create_clone_method(cx, span, type_ident, generics, self_match_expr)
}

View File

@ -0,0 +1,9 @@
#[deriving_clone]
enum E {
A,
B(()),
C
}
fn main() {}

View File

@ -0,0 +1,9 @@
#[deriving_clone]
enum E<T,U> {
A(T),
B(T,U),
C
}
fn main() {}

View File

@ -0,0 +1,9 @@
#[deriving_clone]
struct S<T> {
foo: (),
bar: (),
baz: T,
}
fn main() {}

View File

@ -0,0 +1,5 @@
#[deriving_clone]
struct S<T>(T, ());
fn main() {}

View File

@ -0,0 +1,8 @@
#[deriving_clone]
struct S {
foo: (),
bar: ()
}
fn main() {}

View File

@ -0,0 +1,5 @@
#[deriving_clone]
struct S((), ());
fn main() {}