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
+ }
0 commit comments