libsyntax: Implement deriving via a syntax extension for the IterBytes trait. r=brson

This commit is contained in:
Patrick Walton 2012-11-20 18:27:13 -08:00
parent b053f0b5e6
commit 57588edf3b
5 changed files with 442 additions and 123 deletions

View file

@ -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",

View file

@ -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 }
}

View file

@ -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 ]);
}

View file

@ -0,0 +1,16 @@
#[deriving_iter_bytes]
enum Foo {
Bar(int, char),
Baz(char, int)
}
#[deriving_iter_bytes]
enum A {
B,
C,
D,
E
}
fn main(){}

View file

@ -0,0 +1,10 @@
#[deriving_iter_bytes]
struct Foo {
x: int,
y: int,
z: int
}
fn main() {}