diff options
| author | Patrick Walton <pcwalton@mimiga.net> | 2012-11-20 18:27:13 -0800 |
|---|---|---|
| committer | Patrick Walton <pcwalton@mimiga.net> | 2012-11-21 10:27:34 -0800 |
| commit | 57588edf3bc2450fd1daff4ec2565fe5edbedcef (patch) | |
| tree | d9b8fede85d1d85f31731d1c83131ed4400362d1 /src/libsyntax | |
| parent | b053f0b5e68233f664d087f06fc5ebaec2f19f97 (diff) | |
| download | rust-57588edf3bc2450fd1daff4ec2565fe5edbedcef.tar.gz rust-57588edf3bc2450fd1daff4ec2565fe5edbedcef.zip | |
libsyntax: Implement deriving via a syntax extension for the `IterBytes` trait. r=brson
Diffstat (limited to 'src/libsyntax')
| -rw-r--r-- | src/libsyntax/ext/base.rs | 3 | ||||
| -rw-r--r-- | src/libsyntax/ext/build.rs | 47 | ||||
| -rw-r--r-- | src/libsyntax/ext/deriving.rs | 489 |
3 files changed, 416 insertions, 123 deletions
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index ba5d34bfdb4..480cbfe0060 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -100,6 +100,9 @@ fn syntax_expander_table() -> HashMap<~str, syntax_extension> { syntax_expanders.insert(~"deriving_eq", item_decorator( ext::deriving::expand_deriving_eq)); + syntax_expanders.insert(~"deriving_iter_bytes", + item_decorator( + ext::deriving::expand_deriving_iter_bytes)); // Quasi-quoting expanders syntax_expanders.insert(~"quote_tokens", diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index f08bc3cc7bd..6656007b50e 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -141,6 +141,18 @@ fn mk_block(cx: ext_ctxt, sp: span, span: sp }; mk_expr(cx, sp, ast::expr_block(blk)) } +fn mk_block_(cx: ext_ctxt, sp: span, +stmts: ~[@ast::stmt]) -> ast::blk { + { + node: { + view_items: ~[], + stmts: move stmts, + expr: None, + id: cx.next_id(), + rules: ast::default_blk + }, + span: sp + } +} fn mk_simple_block(cx: ext_ctxt, span: span, expr: @ast::expr) -> ast::blk { let block = { view_items: ~[], @@ -177,4 +189,39 @@ 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) } +fn mk_stmt(cx: ext_ctxt, span: span, expr: @ast::expr) -> @ast::stmt { + let stmt_ = ast::stmt_semi(expr, cx.next_id()); + @{ node: move stmt_, span: span } +} +fn mk_ty_path(cx: ext_ctxt, + span: span, + idents: ~[ ast::ident ]) + -> @ast::Ty { + let ty = build::mk_raw_path(span, idents); + let ty = ast::ty_path(ty, cx.next_id()); + let ty = @{ id: cx.next_id(), node: move ty, span: span }; + ty +} +fn mk_simple_ty_path(cx: ext_ctxt, + span: span, + ident: ast::ident) + -> @ast::Ty { + mk_ty_path(cx, span, ~[ ident ]) +} +fn mk_arg(cx: ext_ctxt, + span: span, + ident: ast::ident, + ty: @ast::Ty) + -> ast::arg { + let arg_pat = mk_pat_ident(cx, span, ident); + { + mode: ast::infer(cx.next_id()), + ty: ty, + pat: arg_pat, + id: cx.next_id() + } +} +fn mk_fn_decl(+inputs: ~[ast::arg], output: @ast::Ty) -> ast::fn_decl { + { inputs: move inputs, output: output, cf: ast::return_val } +} diff --git a/src/libsyntax/ext/deriving.rs b/src/libsyntax/ext/deriving.rs index 8e5c8253716..cf8aa764eea 100644 --- a/src/libsyntax/ext/deriving.rs +++ b/src/libsyntax/ext/deriving.rs @@ -1,5 +1,5 @@ /// The compiler code necessary to implement the #[deriving_eq] and -/// #[deriving_ord] extensions. +/// #[deriving_iter_bytes] extensions. use ast::{and, bind_by_ref, binop, blk, default_blk, deref, enum_def}; use ast::{enum_variant_kind, expr}; @@ -7,9 +7,9 @@ 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, struct_variant_kind}; -use ast::{sty_region, tuple_variant_kind, ty_path}; -use ast::{ty_rptr, unnamed_field, variant}; +use ast::{pure_fn, re_anon, return_val, stmt, struct_def}; +use ast::{struct_variant_kind, sty_by_ref, sty_region, tuple_variant_kind}; +use ast::{ty_nil, ty_path, ty_rptr, unnamed_field, variant}; use base::ext_ctxt; use codemap::span; use parse::token::special_idents::clownshoes_extensions; @@ -28,11 +28,47 @@ impl Junction { } } +type ExpandDerivingStructDefFn = &fn(ext_ctxt, + span, + x: &struct_def, + ident) + -> @item; +type ExpandDerivingEnumDefFn = &fn(ext_ctxt, + span, + x: &enum_def, + ident) + -> @item; + pub fn expand_deriving_eq(cx: ext_ctxt, span: span, _mitem: meta_item, in_items: ~[@item]) -> ~[@item] { + expand_deriving(cx, + span, + in_items, + expand_deriving_eq_struct_def, + expand_deriving_eq_enum_def) +} + +pub fn expand_deriving_iter_bytes(cx: ext_ctxt, + span: span, + _mitem: meta_item, + in_items: ~[@item]) + -> ~[@item] { + expand_deriving(cx, + span, + in_items, + expand_deriving_iter_bytes_struct_def, + expand_deriving_iter_bytes_enum_def) +} + +fn expand_deriving(cx: ext_ctxt, + span: span, + in_items: ~[@item], + expand_deriving_struct_def: ExpandDerivingStructDefFn, + expand_deriving_enum_def: ExpandDerivingEnumDefFn) + -> ~[@item] { let result = dvec::DVec(); for in_items.each |item| { result.push(copy *item); @@ -68,33 +104,21 @@ fn create_impl_item(cx: ext_ctxt, span: span, +item: item_) -> @item { /// Creates a method from the given expression, the signature of which /// conforms to the `eq` or `ne` method. -fn create_method(cx: ext_ctxt, - span: span, - method_ident: ident, - type_ident: ident, - body: @expr) - -> @method { +fn create_eq_method(cx: ext_ctxt, + span: span, + method_ident: ident, + type_ident: ident, + body: @expr) + -> @method { // Create the type of the `other` parameter. - let arg_path_type = build::mk_raw_path(span, ~[ type_ident ]); - let arg_path_type = ty_path(arg_path_type, cx.next_id()); - let arg_path_type = @{ - id: cx.next_id(), - node: move arg_path_type, - span: span - }; + let arg_path_type = build::mk_simple_ty_path(cx, span, type_ident); let arg_region = @{ id: cx.next_id(), node: re_anon }; let arg_type = ty_rptr(arg_region, { ty: arg_path_type, mutbl: m_imm }); let arg_type = @{ id: cx.next_id(), node: move arg_type, span: span }; // Create the `other` parameter. let other_ident = cx.ident_of(~"__other"); - let arg_pat = build::mk_pat_ident(cx, span, other_ident); - let arg = { - mode: infer(cx.next_id()), - ty: arg_type, - pat: arg_pat, - id: cx.next_id() - }; + let arg = build::mk_arg(cx, span, other_ident, arg_type); // Create the type of the return value. let bool_ident = cx.ident_of(~"bool"); @@ -107,11 +131,7 @@ fn create_method(cx: ext_ctxt, }; // Create the function declaration. - let fn_decl = { - inputs: ~[ move arg ], - output: output_type, - cf: return_val - }; + let fn_decl = build::mk_fn_decl(~[ move arg ], output_type); // Create the body block. let body_block = build::mk_simple_block(cx, span, body); @@ -136,28 +156,20 @@ fn create_method(cx: ext_ctxt, fn create_derived_impl(cx: ext_ctxt, span: span, type_ident: ident, - eq_method: @method, - ne_method: @method) + methods: &[@method], + trait_path: &[ast::ident]) -> @item { - // Create the reference to the `core::cmp::Eq` trait. - let core_ident = cx.ident_of(~"core"); - let cmp_ident = cx.ident_of(~"cmp"); - let eq_ident = cx.ident_of(~"Eq"); - let core_cmp_eq_idents = ~[ - move core_ident, - move cmp_ident, - move eq_ident - ]; - let core_cmp_eq_path = { + // Create the reference to the trait. + let trait_path = { span: span, global: false, - idents: move core_cmp_eq_idents, + idents: trait_path.map(|x| *x), rp: None, types: ~[] }; - let core_cmp_eq_path = @move core_cmp_eq_path; + let trait_path = @move trait_path; let trait_ref = { - path: core_cmp_eq_path, + path: trait_path, ref_id: cx.next_id(), impl_id: cx.next_id(), }; @@ -172,10 +184,87 @@ fn create_derived_impl(cx: ext_ctxt, let impl_item = item_impl(~[], Some(trait_ref), self_type, - ~[ eq_method, ne_method ]); + methods.map(|x| *x)); return create_impl_item(cx, span, move impl_item); } +fn create_derived_eq_impl(cx: ext_ctxt, + span: span, + type_ident: ident, + eq_method: @method, + ne_method: @method) + -> @item { + let methods = [ eq_method, ne_method ]; + let trait_path = [ + cx.ident_of(~"core"), + cx.ident_of(~"cmp"), + cx.ident_of(~"Eq") + ]; + create_derived_impl(cx, span, type_ident, methods, trait_path) +} + +fn create_derived_iter_bytes_impl(cx: ext_ctxt, + span: span, + type_ident: ident, + method: @method) + -> @item { + let trait_path = [ + cx.ident_of(~"core"), + cx.ident_of(~"to_bytes"), + cx.ident_of(~"IterBytes") + ]; + create_derived_impl(cx, span, type_ident, [ method ], 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, + span: span, + +statements: ~[@stmt]) + -> @method { + // Create the `lsb0` parameter. + let bool_ident = cx.ident_of(~"bool"); + let lsb0_arg_type = build::mk_simple_ty_path(cx, span, bool_ident); + let lsb0_ident = cx.ident_of(~"__lsb0"); + let lsb0_arg = build::mk_arg(cx, span, lsb0_ident, lsb0_arg_type); + + // Create the `f` parameter. + let core_ident = cx.ident_of(~"core"); + let to_bytes_ident = cx.ident_of(~"to_bytes"); + let cb_ident = cx.ident_of(~"Cb"); + let core_to_bytes_cb_ident = ~[ core_ident, to_bytes_ident, cb_ident ]; + let f_arg_type = build::mk_ty_path(cx, span, core_to_bytes_cb_ident); + let f_ident = cx.ident_of(~"__f"); + let f_arg = build::mk_arg(cx, span, f_ident, f_arg_type); + + // Create the type of the return value. + let output_type = @{ id: cx.next_id(), node: ty_nil, span: span }; + + // Create the function declaration. + let inputs = ~[ move lsb0_arg, move f_arg ]; + let fn_decl = build::mk_fn_decl(move inputs, output_type); + + // Create the body block. + let body_block = build::mk_block_(cx, span, move statements); + + // Create the method. + let self_ty = { node: sty_by_ref, span: span }; + let method_ident = cx.ident_of(~"iter_bytes"); + return @{ + ident: method_ident, + attrs: ~[], + tps: ~[], + self_ty: self_ty, + purity: pure_fn, + decl: move fn_decl, + body: move body_block, + id: cx.next_id(), + span: span, + self_id: cx.next_id(), + vis: public + } +} + fn create_enum_variant_pattern(cx: ext_ctxt, span: span, variant: &ast::variant, @@ -214,13 +303,13 @@ fn create_enum_variant_pattern(cx: ext_ctxt, } } -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>) { +fn call_substructure_eq_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, @@ -243,11 +332,11 @@ fn call_substructure_method(cx: ext_ctxt, }; } -fn finish_chain_expr(cx: ext_ctxt, - span: span, - chain_expr: Option<@expr>, - junction: Junction) - -> @expr { +fn finish_eq_chain_expr(cx: ext_ctxt, + span: span, + chain_expr: Option<@expr>, + junction: Junction) + -> @expr { match chain_expr { None => { match junction { @@ -259,6 +348,31 @@ fn finish_chain_expr(cx: ext_ctxt, } } +fn call_substructure_iter_bytes_method(cx: ext_ctxt, + span: span, + self_field: @expr) + -> @stmt { + // Gather up the parameters we want to chain along. + let lsb0_ident = cx.ident_of(~"__lsb0"); + let f_ident = cx.ident_of(~"__f"); + let lsb0_expr = build::mk_path(cx, span, ~[ lsb0_ident ]); + let f_expr = build::mk_path(cx, span, ~[ f_ident ]); + + // Call the substructure method. + let iter_bytes_ident = cx.ident_of(~"iter_bytes"); + let self_method = build::mk_access_(cx, + span, + self_field, + iter_bytes_ident); + let self_call = build::mk_call_(cx, + span, + self_method, + ~[ lsb0_expr, f_expr ]); + + // Create a statement out of this expression. + build::mk_stmt(cx, span, self_call) +} + fn variant_arg_count(cx: ext_ctxt, span: span, variant: &variant) -> uint { match variant.node.kind { tuple_variant_kind(args) => args.len(), @@ -269,38 +383,66 @@ fn variant_arg_count(cx: ext_ctxt, span: span, variant: &variant) -> uint { } } -fn expand_deriving_struct_def(cx: ext_ctxt, - span: span, - struct_def: &struct_def, - type_ident: ident) - -> @item { +fn expand_deriving_eq_struct_def(cx: ext_ctxt, + span: span, + struct_def: &struct_def, + type_ident: ident) + -> @item { // Create the methods. let eq_ident = cx.ident_of(~"eq"); let ne_ident = cx.ident_of(~"ne"); - let eq_method = expand_deriving_struct_method(cx, - span, - struct_def, - eq_ident, - type_ident, - Conjunction); - let ne_method = expand_deriving_struct_method(cx, - span, - struct_def, - ne_ident, - type_ident, - Disjunction); + let eq_method = expand_deriving_eq_struct_method(cx, + span, + struct_def, + eq_ident, + type_ident, + Conjunction); + let ne_method = expand_deriving_eq_struct_method(cx, + span, + struct_def, + ne_ident, + type_ident, + Disjunction); // Create the implementation. - return create_derived_impl(cx, span, type_ident, eq_method, ne_method); + return create_derived_eq_impl(cx, span, type_ident, eq_method, ne_method); } -fn expand_deriving_struct_method(cx: ext_ctxt, - span: span, - struct_def: &struct_def, - method_ident: ident, - type_ident: ident, - junction: Junction) - -> @method { +fn expand_deriving_iter_bytes_struct_def(cx: ext_ctxt, + span: span, + struct_def: &struct_def, + type_ident: ident) + -> @item { + // Create the method. + let method = expand_deriving_iter_bytes_struct_method(cx, + span, + struct_def); + + // Create the implementation. + return create_derived_iter_bytes_impl(cx, span, type_ident, method); +} + +fn expand_deriving_iter_bytes_enum_def(cx: ext_ctxt, + span: span, + enum_definition: &enum_def, + type_ident: ident) + -> @item { + // Create the method. + let method = expand_deriving_iter_bytes_enum_method(cx, + span, + enum_definition); + + // Create the implementation. + return create_derived_iter_bytes_impl(cx, span, type_ident, method); +} + +fn expand_deriving_eq_struct_method(cx: ext_ctxt, + span: span, + struct_def: &struct_def, + method_ident: ident, + type_ident: ident, + junction: Junction) + -> @method { let self_ident = cx.ident_of(~"self"); let other_ident = cx.ident_of(~"__other"); @@ -325,13 +467,13 @@ fn expand_deriving_struct_method(cx: ext_ctxt, ident); // Call the substructure method. - call_substructure_method(cx, - span, - self_field, - other_field_ref, - method_ident, - junction, - &mut outer_expr); + call_substructure_eq_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`"); @@ -340,42 +482,77 @@ fn expand_deriving_struct_method(cx: ext_ctxt, } // Create the method itself. - let body = finish_chain_expr(cx, span, outer_expr, junction); - return create_method(cx, span, method_ident, type_ident, body); + let body = finish_eq_chain_expr(cx, span, outer_expr, junction); + return create_eq_method(cx, span, method_ident, type_ident, body); } -fn expand_deriving_enum_def(cx: ext_ctxt, - span: span, - enum_definition: &enum_def, - type_ident: ident) - -> @item { +fn expand_deriving_iter_bytes_struct_method(cx: ext_ctxt, + span: span, + struct_def: &struct_def) + -> @method { + let self_ident = cx.ident_of(~"self"); + + // Create the body of the method. + let statements = dvec::DVec(); + 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 stmt = call_substructure_iter_bytes_method(cx, + span, + self_field); + statements.push(stmt); + } + unnamed_field => { + cx.span_unimpl(span, + ~"unnamed fields with `deriving_iter_bytes`"); + } + } + } + + // Create the method itself. + let statements = dvec::unwrap(move statements); + return create_iter_bytes_method(cx, span, move statements); +} + +fn expand_deriving_eq_enum_def(cx: ext_ctxt, + span: span, + enum_definition: &enum_def, + type_ident: ident) + -> @item { // Create the methods. let eq_ident = cx.ident_of(~"eq"); let ne_ident = cx.ident_of(~"ne"); - let eq_method = expand_deriving_enum_method(cx, + let eq_method = expand_deriving_eq_enum_method(cx, span, enum_definition, eq_ident, type_ident, Conjunction); - let ne_method = expand_deriving_enum_method(cx, - span, - enum_definition, - ne_ident, - type_ident, - Disjunction); + let ne_method = expand_deriving_eq_enum_method(cx, + span, + enum_definition, + ne_ident, + type_ident, + Disjunction); // Create the implementation. - return create_derived_impl(cx, span, type_ident, eq_method, ne_method); + return create_derived_eq_impl(cx, span, type_ident, eq_method, ne_method); } -fn expand_deriving_enum_method(cx: ext_ctxt, - span: span, - enum_definition: &enum_def, - method_ident: ident, - type_ident: ident, - junction: Junction) - -> @method { +fn expand_deriving_eq_enum_method(cx: ext_ctxt, + span: span, + enum_definition: &enum_def, + method_ident: ident, + type_ident: ident, + junction: Junction) + -> @method { let self_ident = cx.ident_of(~"self"); let other_ident = cx.ident_of(~"__other"); @@ -410,19 +587,19 @@ fn expand_deriving_enum_method(cx: ext_ctxt, 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); + call_substructure_eq_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_expr = finish_eq_chain_expr(cx, + span, + matching_body_expr, + junction); let matching_body_block = build::mk_simple_block(cx, span, matching_body_expr); @@ -491,6 +668,72 @@ fn expand_deriving_enum_method(cx: ext_ctxt, let self_match_expr = build::mk_expr(cx, span, move self_match_expr); // Create the method. - return create_method(cx, span, method_ident, type_ident, self_match_expr); + return create_eq_method(cx, + span, + method_ident, + type_ident, + self_match_expr); +} + +fn expand_deriving_iter_bytes_enum_method(cx: ext_ctxt, + span: span, + enum_definition: &enum_def) + -> @method { + // Create the arms of the match in the method body. + let arms = dvec::DVec(); + for enum_definition.variants.eachi |i, variant| { + // Create the matching pattern. + let pat = create_enum_variant_pattern(cx, span, variant, ~"__self"); + + // Determine the discriminant. We will feed this value to the byte + // iteration function. + let discriminant; + match variant.node.disr_expr { + Some(copy disr_expr) => discriminant = disr_expr, + None => discriminant = build::mk_uint(cx, span, i), + } + + // Feed the discriminant to the byte iteration function. + let stmts = dvec::DVec(); + let discrim_stmt = call_substructure_iter_bytes_method(cx, + span, + discriminant); + stmts.push(discrim_stmt); + + // Feed each argument in this variant to the byte iteration function + // as well. + 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 stmt = call_substructure_iter_bytes_method(cx, span, field); + stmts.push(stmt); + } + + // Create the pattern body. + let stmts = dvec::unwrap(move stmts); + let match_body_block = build::mk_block_(cx, span, move stmts); + + // Create the arm. + let arm = { + pats: ~[ pat ], + guard: None, + body: move match_body_block + }; + arms.push(move arm); + } + + // Create the method body. + let self_ident = cx.ident_of(~"self"); + let self_expr = build::mk_path(cx, span, ~[ self_ident ]); + let arms = dvec::unwrap(move arms); + let self_match_expr = expr_match(self_expr, move arms); + let self_match_expr = build::mk_expr(cx, span, move self_match_expr); + let self_match_stmt = build::mk_stmt(cx, span, self_match_expr); + + // Create the method. + return create_iter_bytes_method(cx, span, ~[ self_match_stmt ]); } |
