libsyntax: Implement deriving
for enums with N-ary variants. r=brson
This commit is contained in:
parent
809bd3e5ef
commit
e0876fdfc1
@ -157,11 +157,22 @@ fn mk_copy(cx: ext_ctxt, sp: span, e: @ast::expr) -> @ast::expr {
|
||||
fn mk_managed(cx: ext_ctxt, sp: span, e: @ast::expr) -> @ast::expr {
|
||||
mk_expr(cx, sp, ast::expr_unary(ast::box(ast::m_imm), e))
|
||||
}
|
||||
fn mk_pat_ident(cx: ext_ctxt, span: span, ident: ast::ident) -> @ast::pat {
|
||||
let path = build::mk_raw_path(span, ~[ ident ]);
|
||||
let pat = ast::pat_ident(ast::bind_by_value, path, None);
|
||||
fn mk_pat(cx: ext_ctxt, span: span, +pat: ast::pat_) -> @ast::pat {
|
||||
@{ id: cx.next_id(), node: move pat, span: span }
|
||||
}
|
||||
fn mk_pat_ident(cx: ext_ctxt, span: span, ident: ast::ident) -> @ast::pat {
|
||||
let path = mk_raw_path(span, ~[ ident ]);
|
||||
let pat = ast::pat_ident(ast::bind_by_value, path, None);
|
||||
mk_pat(cx, span, move pat)
|
||||
}
|
||||
fn mk_pat_enum(cx: ext_ctxt,
|
||||
span: span,
|
||||
path: @ast::path,
|
||||
+subpats: ~[@ast::pat])
|
||||
-> @ast::pat {
|
||||
let pat = ast::pat_enum(path, Some(move subpats));
|
||||
mk_pat(cx, span, move pat)
|
||||
}
|
||||
fn mk_bool(cx: ext_ctxt, span: span, value: bool) -> @ast::expr {
|
||||
let lit_expr = ast::expr_lit(@{ node: ast::lit_bool(value), span: span });
|
||||
build::mk_expr(cx, span, move lit_expr)
|
||||
|
@ -1,13 +1,15 @@
|
||||
/// The compiler code necessary to implement the #[deriving_eq] and
|
||||
/// #[deriving_ord] extensions.
|
||||
|
||||
use ast::{and, bind_by_value, binop, blk, default_blk, deref, enum_def, expr};
|
||||
use ast::{and, bind_by_ref, binop, blk, default_blk, deref, enum_def};
|
||||
use ast::{enum_variant_kind, expr};
|
||||
use ast::{expr_, expr_addr_of, expr_binary, expr_call, expr_field, expr_lit};
|
||||
use ast::{expr_match, expr_path, expr_unary, ident, infer, item, item_};
|
||||
use ast::{item_class, item_enum, item_impl, lit_bool, m_imm, meta_item};
|
||||
use ast::{method, named_field, or, pat, pat_ident, pat_wild, path, public};
|
||||
use ast::{pure_fn, re_anon, return_val, struct_def, sty_region, ty_path};
|
||||
use ast::{ty_rptr, unnamed_field};
|
||||
use ast::{pure_fn, re_anon, return_val, struct_def, struct_variant_kind};
|
||||
use ast::{sty_region, tuple_variant_kind, ty_path};
|
||||
use ast::{ty_rptr, unnamed_field, variant};
|
||||
use base::ext_ctxt;
|
||||
use codemap::span;
|
||||
use parse::token::special_idents::clownshoes_extensions;
|
||||
@ -174,6 +176,99 @@ fn create_derived_impl(cx: ext_ctxt,
|
||||
return create_impl_item(cx, span, move impl_item);
|
||||
}
|
||||
|
||||
fn create_enum_variant_pattern(cx: ext_ctxt,
|
||||
span: span,
|
||||
variant: &ast::variant,
|
||||
prefix: ~str)
|
||||
-> @ast::pat {
|
||||
let variant_ident = variant.node.name;
|
||||
match variant.node.kind {
|
||||
tuple_variant_kind(ref variant_args) => {
|
||||
if variant_args.len() == 0 {
|
||||
return build::mk_pat_ident(cx, span, variant_ident);
|
||||
}
|
||||
|
||||
let subpats = dvec::DVec();
|
||||
for variant_args.each |_variant_arg| {
|
||||
// Create the subidentifier.
|
||||
let index = subpats.len().to_str();
|
||||
let ident = cx.ident_of(prefix + index);
|
||||
|
||||
// Create the subpattern.
|
||||
let subpath = build::mk_raw_path(span, ~[ ident ]);
|
||||
let subpat = pat_ident(bind_by_ref(m_imm), subpath, None);
|
||||
let subpat = build::mk_pat(cx, span, move subpat);
|
||||
subpats.push(subpat);
|
||||
}
|
||||
|
||||
let matching_path = build::mk_raw_path(span, ~[ variant_ident ]);
|
||||
let subpats = dvec::unwrap(move subpats);
|
||||
return build::mk_pat_enum(cx, span, matching_path, move subpats);
|
||||
}
|
||||
struct_variant_kind(*) => {
|
||||
cx.span_unimpl(span, ~"struct variants for `deriving`");
|
||||
}
|
||||
enum_variant_kind(*) => {
|
||||
cx.span_unimpl(span, ~"enum variants for `deriving`");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn call_substructure_method(cx: ext_ctxt,
|
||||
span: span,
|
||||
self_field: @expr,
|
||||
other_field_ref: @expr,
|
||||
method_ident: ident,
|
||||
junction: Junction,
|
||||
chain_expr: &mut Option<@expr>) {
|
||||
// Call the substructure method.
|
||||
let self_method = build::mk_access_(cx, span, self_field, method_ident);
|
||||
let self_call = build::mk_call_(cx,
|
||||
span,
|
||||
self_method,
|
||||
~[ other_field_ref ]);
|
||||
|
||||
// Connect to the outer expression if necessary.
|
||||
*chain_expr = match *chain_expr {
|
||||
None => Some(self_call),
|
||||
Some(copy old_outer_expr) => {
|
||||
let binop = junction.to_binop();
|
||||
let chain_expr = build::mk_binary(cx,
|
||||
span,
|
||||
binop,
|
||||
old_outer_expr,
|
||||
self_call);
|
||||
Some(chain_expr)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn finish_chain_expr(cx: ext_ctxt,
|
||||
span: span,
|
||||
chain_expr: Option<@expr>,
|
||||
junction: Junction)
|
||||
-> @expr {
|
||||
match chain_expr {
|
||||
None => {
|
||||
match junction {
|
||||
Conjunction => build::mk_bool(cx, span, true),
|
||||
Disjunction => build::mk_bool(cx, span, false),
|
||||
}
|
||||
}
|
||||
Some(ref outer_expr) => *outer_expr,
|
||||
}
|
||||
}
|
||||
|
||||
fn variant_arg_count(cx: ext_ctxt, span: span, variant: &variant) -> uint {
|
||||
match variant.node.kind {
|
||||
tuple_variant_kind(args) => args.len(),
|
||||
struct_variant_kind(struct_def) => struct_def.fields.len(),
|
||||
enum_variant_kind(*) => {
|
||||
cx.span_bug(span, ~"variant_arg_count: enum variants deprecated")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_deriving_struct_def(cx: ext_ctxt,
|
||||
span: span,
|
||||
struct_def: &struct_def,
|
||||
@ -209,8 +304,6 @@ fn expand_deriving_struct_method(cx: ext_ctxt,
|
||||
let self_ident = cx.ident_of(~"self");
|
||||
let other_ident = cx.ident_of(~"__other");
|
||||
|
||||
let binop = junction.to_binop();
|
||||
|
||||
// Create the body of the method.
|
||||
let mut outer_expr = None;
|
||||
for struct_def.fields.each |struct_field| {
|
||||
@ -232,27 +325,13 @@ fn expand_deriving_struct_method(cx: ext_ctxt,
|
||||
ident);
|
||||
|
||||
// Call the substructure method.
|
||||
let self_method = build::mk_access_(cx,
|
||||
span,
|
||||
self_field,
|
||||
method_ident);
|
||||
let self_call = build::mk_call_(cx,
|
||||
span,
|
||||
self_method,
|
||||
~[ other_field_ref ]);
|
||||
|
||||
// Connect to the outer expression if necessary.
|
||||
outer_expr = match outer_expr {
|
||||
None => Some(self_call),
|
||||
Some(old_outer_expr) => {
|
||||
let chain_expr = build::mk_binary(cx,
|
||||
span,
|
||||
binop,
|
||||
old_outer_expr,
|
||||
self_call);
|
||||
Some(chain_expr)
|
||||
}
|
||||
};
|
||||
call_substructure_method(cx,
|
||||
span,
|
||||
self_field,
|
||||
other_field_ref,
|
||||
method_ident,
|
||||
junction,
|
||||
&mut outer_expr);
|
||||
}
|
||||
unnamed_field => {
|
||||
cx.span_unimpl(span, ~"unnamed fields with `deriving_eq`");
|
||||
@ -261,12 +340,7 @@ fn expand_deriving_struct_method(cx: ext_ctxt,
|
||||
}
|
||||
|
||||
// Create the method itself.
|
||||
let body;
|
||||
match outer_expr {
|
||||
None => cx.span_unimpl(span, ~"empty structs with `deriving_eq`"),
|
||||
Some(outer_expr) => body = outer_expr,
|
||||
}
|
||||
|
||||
let body = finish_chain_expr(cx, span, outer_expr, junction);
|
||||
return create_method(cx, span, method_ident, type_ident, body);
|
||||
}
|
||||
|
||||
@ -305,8 +379,6 @@ fn expand_deriving_enum_method(cx: ext_ctxt,
|
||||
let self_ident = cx.ident_of(~"self");
|
||||
let other_ident = cx.ident_of(~"__other");
|
||||
|
||||
let _binop = junction.to_binop();
|
||||
|
||||
let is_eq;
|
||||
match junction {
|
||||
Conjunction => is_eq = true,
|
||||
@ -317,13 +389,40 @@ fn expand_deriving_enum_method(cx: ext_ctxt,
|
||||
let self_arms = dvec::DVec();
|
||||
for enum_definition.variants.each |self_variant| {
|
||||
let other_arms = dvec::DVec();
|
||||
let self_variant_ident = self_variant.node.name;
|
||||
|
||||
// Create the matching pattern.
|
||||
let matching_pat = build::mk_pat_ident(cx, span, self_variant_ident);
|
||||
let matching_pat = create_enum_variant_pattern(cx,
|
||||
span,
|
||||
self_variant,
|
||||
~"__other");
|
||||
|
||||
// Create the matching pattern body.
|
||||
let matching_body_expr = build::mk_bool(cx, span, is_eq);
|
||||
let mut matching_body_expr = None;
|
||||
for uint::range(0, variant_arg_count(cx, span, self_variant)) |i| {
|
||||
// Create the expression for the other field.
|
||||
let other_field_ident = cx.ident_of(~"__other" + i.to_str());
|
||||
let other_field = build::mk_path(cx,
|
||||
span,
|
||||
~[ other_field_ident ]);
|
||||
|
||||
// Create the expression for this field.
|
||||
let self_field_ident = cx.ident_of(~"__self" + i.to_str());
|
||||
let self_field = build::mk_path(cx, span, ~[ self_field_ident ]);
|
||||
|
||||
// Call the substructure method.
|
||||
call_substructure_method(cx,
|
||||
span,
|
||||
self_field,
|
||||
other_field,
|
||||
method_ident,
|
||||
junction,
|
||||
&mut matching_body_expr);
|
||||
}
|
||||
|
||||
let matching_body_expr = finish_chain_expr(cx,
|
||||
span,
|
||||
matching_body_expr,
|
||||
junction);
|
||||
let matching_body_block = build::mk_simple_block(cx,
|
||||
span,
|
||||
matching_body_expr);
|
||||
@ -358,7 +457,10 @@ fn expand_deriving_enum_method(cx: ext_ctxt,
|
||||
other_arms.push(move nonmatching_arm);
|
||||
|
||||
// Create the self pattern.
|
||||
let self_pat = build::mk_pat_ident(cx, span, self_variant_ident);
|
||||
let self_pat = create_enum_variant_pattern(cx,
|
||||
span,
|
||||
self_variant,
|
||||
~"__self");
|
||||
|
||||
// Create the self pattern body.
|
||||
let other_expr = build::mk_path(cx, span, ~[ other_ident ]);
|
||||
|
15
src/test/run-pass/deriving-via-extension-enum.rs
Normal file
15
src/test/run-pass/deriving-via-extension-enum.rs
Normal file
@ -0,0 +1,15 @@
|
||||
#[deriving_eq]
|
||||
enum Foo {
|
||||
Bar(int, int),
|
||||
Baz(float, float)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a = Bar(1, 2);
|
||||
let b = Bar(1, 2);
|
||||
assert a == b;
|
||||
assert !(a != b);
|
||||
assert a.eq(&b);
|
||||
assert !a.ne(&b);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user