diff options
| author | Patrick Walton <pcwalton@mimiga.net> | 2012-11-20 12:59:37 -0800 |
|---|---|---|
| committer | Patrick Walton <pcwalton@mimiga.net> | 2012-11-21 10:27:13 -0800 |
| commit | e0876fdfc1976f74526c486deb77523edbc216aa (patch) | |
| tree | c31cf448b3c2bf8ee6408ae835272e4a8f366984 /src/libsyntax/ext | |
| parent | 809bd3e5efa00a7a3f924bc3999568d67574e4e7 (diff) | |
| download | rust-e0876fdfc1976f74526c486deb77523edbc216aa.tar.gz rust-e0876fdfc1976f74526c486deb77523edbc216aa.zip | |
libsyntax: Implement `deriving` for enums with N-ary variants. r=brson
Diffstat (limited to 'src/libsyntax/ext')
| -rw-r--r-- | src/libsyntax/ext/build.rs | 15 | ||||
| -rw-r--r-- | src/libsyntax/ext/deriving.rs | 178 |
2 files changed, 153 insertions, 40 deletions
diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index e613263a52c..f08bc3cc7bd 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -157,10 +157,21 @@ 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(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 = build::mk_raw_path(span, ~[ ident ]); + let path = mk_raw_path(span, ~[ ident ]); let pat = ast::pat_ident(ast::bind_by_value, path, None); - @{ id: cx.next_id(), node: move pat, span: span } + 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 }); diff --git a/src/libsyntax/ext/deriving.rs b/src/libsyntax/ext/deriving.rs index 735f2cf9ae1..f3c2acff932 100644 --- a/src/libsyntax/ext/deriving.rs +++ b/src/libsyntax/ext/deriving.rs @@ -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 ]); |
