Skip to content

Commit 5e1d6c2

Browse files
committed
libsyntax: add #[deriving(Rand, ToStr)].
The former fills each field of a struct or enum variant with a random value (and picks a random enum variant). The latter makes the .to_str method have the same output as fmt!("%?", ..).
1 parent 1cf2108 commit 5e1d6c2

File tree

3 files changed

+196
-0
lines changed

3 files changed

+196
-0
lines changed

src/libsyntax/ext/deriving/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ pub mod clone;
2323
pub mod iter_bytes;
2424
pub mod encodable;
2525
pub mod decodable;
26+
pub mod rand;
27+
pub mod to_str;
2628

2729
#[path="cmp/eq.rs"]
2830
pub mod eq;
@@ -86,6 +88,10 @@ pub fn expand_meta_deriving(cx: @ext_ctxt,
8688
~"Ord" => expand!(ord::expand_deriving_ord),
8789
~"TotalOrd" => expand!(totalord::expand_deriving_totalord),
8890

91+
~"Rand" => expand!(rand::expand_deriving_rand),
92+
93+
~"ToStr" => expand!(to_str::expand_deriving_to_str),
94+
8995
tname => {
9096
cx.span_err(titem.span, fmt!("unknown \
9197
`deriving` trait: `%s`", tname));

src/libsyntax/ext/deriving/rand.rs

+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use ast;
12+
use ast::{meta_item, item, expr, ident};
13+
use codemap::span;
14+
use ext::base::ext_ctxt;
15+
use ext::build;
16+
use ext::deriving::generic::*;
17+
18+
pub fn expand_deriving_rand(cx: @ext_ctxt,
19+
span: span,
20+
mitem: @meta_item,
21+
in_items: ~[@item])
22+
-> ~[@item] {
23+
let trait_def = TraitDef {
24+
path: Path::new(~[~"core", ~"rand", ~"Rand"]),
25+
additional_bounds: ~[],
26+
generics: LifetimeBounds::empty(),
27+
methods: ~[
28+
MethodDef {
29+
name: ~"rand",
30+
generics: LifetimeBounds {
31+
lifetimes: ~[],
32+
bounds: ~[(~"R",
33+
~[ Path::new(~[~"core", ~"rand", ~"Rng"]) ])]
34+
},
35+
self_ty: None,
36+
args: ~[
37+
Ptr(~Literal(Path::new_local(~"R")),
38+
Borrowed(None, ast::m_imm))
39+
],
40+
ret_ty: Self,
41+
const_nonmatching: false,
42+
combine_substructure: rand_substructure
43+
}
44+
]
45+
};
46+
47+
expand_deriving_generic(cx, span, mitem, in_items, &trait_def)
48+
}
49+
50+
fn rand_substructure(cx: @ext_ctxt, span: span, substr: &Substructure) -> @expr {
51+
let rng = match substr.nonself_args {
52+
[rng] => ~[ rng ],
53+
_ => cx.bug("Incorrect number of arguments to `rand` in `deriving(Rand)`")
54+
};
55+
let rand_ident = ~[
56+
cx.ident_of(~"core"),
57+
cx.ident_of(~"rand"),
58+
cx.ident_of(~"Rand"),
59+
cx.ident_of(~"rand")
60+
];
61+
let rand_call = || {
62+
build::mk_call_global(cx, span,
63+
copy rand_ident, copy rng)
64+
};
65+
66+
return match *substr.fields {
67+
StaticStruct(_, ref summary) => {
68+
rand_thing(cx, span, substr.type_ident, summary, rand_call)
69+
}
70+
StaticEnum(_, ref variants) => {
71+
if variants.is_empty() {
72+
cx.span_fatal(span, "`Rand` cannot be derived for enums with no variants");
73+
}
74+
75+
let variant_count = build::mk_uint(cx, span, variants.len());
76+
77+
// need to specify the uint-ness of the random number
78+
let u32_ty = build::mk_ty_path(cx, span, ~[cx.ident_of(~"uint")]);
79+
let r_ty = build::mk_ty_path(cx, span, ~[cx.ident_of(~"R")]);
80+
let rand_name = build::mk_raw_path_(span, copy rand_ident, None, ~[ u32_ty, r_ty ]);
81+
let rand_name = build::mk_path_raw(cx, span, rand_name);
82+
83+
let rv_call = build::mk_call_(cx, span, rand_name, copy rng);
84+
85+
// rand() % variants.len()
86+
let rand_variant = build::mk_binary(cx, span, ast::rem,
87+
rv_call, variant_count);
88+
89+
let mut arms = do variants.mapi |i, id_sum| {
90+
let i_expr = build::mk_uint(cx, span, i);
91+
let pat = build::mk_pat_lit(cx, span, i_expr);
92+
93+
match *id_sum {
94+
(ident, ref summary) => {
95+
build::mk_arm(cx, span,
96+
~[ pat ],
97+
rand_thing(cx, span, ident, summary, rand_call))
98+
}
99+
}
100+
};
101+
102+
// _ => {} at the end. Should never occur
103+
arms.push(build::mk_unreachable_arm(cx, span));
104+
105+
build::mk_expr(cx, span,
106+
ast::expr_match(rand_variant, arms))
107+
}
108+
_ => cx.bug("Non-static method in `deriving(Rand)`")
109+
};
110+
111+
fn rand_thing(cx: @ext_ctxt, span: span,
112+
ctor_ident: ident,
113+
summary: &Either<uint, ~[ident]>,
114+
rand_call: &fn() -> @expr) -> @expr {
115+
let ctor_ident = ~[ ctor_ident ];
116+
match *summary {
117+
Left(copy count) => {
118+
if count == 0 {
119+
build::mk_path(cx, span, ctor_ident)
120+
} else {
121+
let exprs = vec::from_fn(count, |_| rand_call());
122+
build::mk_call(cx, span, ctor_ident, exprs)
123+
}
124+
}
125+
Right(ref fields) => {
126+
let rand_fields = do fields.map |ident| {
127+
build::Field {
128+
ident: *ident,
129+
ex: rand_call()
130+
}
131+
};
132+
build::mk_struct_e(cx, span, ctor_ident, rand_fields)
133+
}
134+
}
135+
}
136+
}

src/libsyntax/ext/deriving/to_str.rs

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use ast::{meta_item, item, expr};
12+
use codemap::span;
13+
use ext::base::ext_ctxt;
14+
use ext::build;
15+
use ext::deriving::generic::*;
16+
17+
pub fn expand_deriving_to_str(cx: @ext_ctxt,
18+
span: span,
19+
mitem: @meta_item,
20+
in_items: ~[@item])
21+
-> ~[@item] {
22+
let trait_def = TraitDef {
23+
path: Path::new(~[~"core", ~"to_str", ~"ToStr"]),
24+
additional_bounds: ~[],
25+
generics: LifetimeBounds::empty(),
26+
methods: ~[
27+
MethodDef {
28+
name: ~"to_str",
29+
generics: LifetimeBounds::empty(),
30+
self_ty: borrowed_explicit_self(),
31+
args: ~[],
32+
ret_ty: Ptr(~Literal(Path::new_local(~"str")), Owned),
33+
const_nonmatching: false,
34+
combine_substructure: to_str_substructure
35+
}
36+
]
37+
};
38+
39+
expand_deriving_generic(cx, span, mitem, in_items, &trait_def)
40+
}
41+
42+
fn to_str_substructure(cx: @ext_ctxt, span: span, substr: &Substructure) -> @expr {
43+
match substr.self_args {
44+
[self_obj] => {
45+
let self_addr = build::mk_addr_of(cx, span, self_obj);
46+
build::mk_call_global(cx, span,
47+
~[cx.ident_of(~"core"),
48+
cx.ident_of(~"sys"),
49+
cx.ident_of(~"log_str")],
50+
~[self_addr])
51+
}
52+
_ => cx.span_bug(span, ~"Invalid number of arguments in `deriving(ToStr)`")
53+
}
54+
}

0 commit comments

Comments
 (0)