(index<- )        ./libsyntax/ext/deriving/rand.rs

    git branch:    * master           5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
    modified:    Fri May  9 13:02:28 2014
   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::{MetaItem, Item, Expr, Ident};
  13  use codemap::Span;
  14  use ext::base::ExtCtxt;
  15  use ext::build::{AstBuilder};
  16  use ext::deriving::generic::*;
  17  
  18  pub fn expand_deriving_rand(cx: &mut ExtCtxt,
  19                              spanSpan,
  20                              mitem: @MetaItem,
  21                              item: @Item,
  22                              push: |@Item|) {
  23      let trait_def = TraitDef {
  24          span: span,
  25          attributes: Vec::new(),
  26          path: Path::new(vec!("rand", "Rand")),
  27          additional_bounds: Vec::new(),
  28          generics: LifetimeBounds::empty(),
  29          methods: vec!(
  30              MethodDef {
  31                  name: "rand",
  32                  generics: LifetimeBounds {
  33                      lifetimes: Vec::new(),
  34                      bounds: vec!(("R",
  35                                    ast::StaticSize,
  36                                    vec!( Path::new(vec!("rand", "Rng")) )))
  37                  },
  38                  explicit_self: None,
  39                  args: vec!(
  40                      Ptr(box Literal(Path::new_local("R")),
  41                          Borrowed(None, ast::MutMutable))
  42                  ),
  43                  ret_ty: Self,
  44                  attributes: Vec::new(),
  45                  const_nonmatching: false,
  46                  combine_substructure: combine_substructure(|a, b, c| {
  47                      rand_substructure(a, b, c)
  48                  })
  49              }
  50          )
  51      };
  52      trait_def.expand(cx, mitem, item, push)
  53  }
  54  
  55  fn rand_substructure(cx: &mut ExtCtxt, trait_spanSpan, substr: &Substructure) -> @Expr {
  56      let rng = match substr.nonself_args {
  57          [rng] => vec!( rng ),
  58          _ => cx.bug("Incorrect number of arguments to `rand` in `deriving(Rand)`")
  59      };
  60      let rand_ident = vec!(
  61          cx.ident_of("rand"),
  62          cx.ident_of("Rand"),
  63          cx.ident_of("rand")
  64      );
  65      let rand_call = |cx&mut ExtCtxt, span| {
  66          cx.expr_call_global(span,
  67                              rand_ident.clone(),
  68                              vec!( *rng.get(0) ))
  69      };
  70  
  71      return match *substr.fields {
  72          StaticStruct(_, ref summary) => {
  73              rand_thing(cx, trait_span, substr.type_ident, summary, rand_call)
  74          }
  75          StaticEnum(_, ref variants) => {
  76              if variants.is_empty() {
  77                  cx.span_err(trait_span, "`Rand` cannot be derived for enums with no variants");
  78                  // let compilation continue
  79                  return cx.expr_uint(trait_span, 0);
  80              }
  81  
  82              let variant_count = cx.expr_uint(trait_span, variants.len());
  83  
  84              let rand_name = cx.path_all(trait_span,
  85                                          true,
  86                                          rand_ident.clone(),
  87                                          Vec::new(),
  88                                          Vec::new());
  89              let rand_name = cx.expr_path(rand_name);
  90  
  91              // ::rand::Rand::rand(rng)
  92              let rv_call = cx.expr_call(trait_span,
  93                                         rand_name,
  94                                         vec!( *rng.get(0) ));
  95  
  96              // need to specify the uint-ness of the random number
  97              let uint_ty = cx.ty_ident(trait_span, cx.ident_of("uint"));
  98              let value_ident = cx.ident_of("__value");
  99              let let_statement = cx.stmt_let_typed(trait_span,
 100                                                    false,
 101                                                    value_ident,
 102                                                    uint_ty,
 103                                                    rv_call);
 104  
 105              // rand() % variants.len()
 106              let value_ref = cx.expr_ident(trait_span, value_ident);
 107              let rand_variant = cx.expr_binary(trait_span,
 108                                                ast::BiRem,
 109                                                value_ref,
 110                                                variant_count);
 111  
 112              let mut arms = variants.iter().enumerate().map(|(i, &(ident, v_span, ref summary))| {
 113                  let i_expr = cx.expr_uint(v_span, i);
 114                  let pat = cx.pat_lit(v_span, i_expr);
 115  
 116                  let thing = rand_thing(cx, v_span, ident, summary, |cx, sp| rand_call(cx, sp));
 117                  cx.arm(v_span, vec!( pat ), thing)
 118              }).collect::<Vec<ast::Arm> >();
 119  
 120              // _ => {} at the end. Should never occur
 121              arms.push(cx.arm_unreachable(trait_span));
 122  
 123              let match_expr = cx.expr_match(trait_span, rand_variant, arms);
 124  
 125              let block = cx.block(trait_span, vec!( let_statement ), Some(match_expr));
 126              cx.expr_block(block)
 127          }
 128          _ => cx.bug("Non-static method in `deriving(Rand)`")
 129      };
 130  
 131      fn rand_thing(cx&mut ExtCtxt,
 132                    trait_spanSpan,
 133                    ctor_identIdent,
 134                    summary&StaticFields,
 135                    rand_call|&mut ExtCtxt, Span-> @Expr)
 136                    -> @Expr {
 137          match *summary {
 138              Unnamed(ref fields) => {
 139                  if fields.is_empty() {
 140                      cx.expr_ident(trait_span, ctor_ident)
 141                  } else {
 142                      let exprs = fields.iter().map(|span| rand_call(cx, *span)).collect();
 143                      cx.expr_call_ident(trait_span, ctor_ident, exprs)
 144                  }
 145              }
 146              Named(ref fields) => {
 147                  let rand_fields = fields.iter().map(|&(ident, span)| {
 148                      let e = rand_call(cx, span);
 149                      cx.field_imm(span, ident, e)
 150                  }).collect();
 151                  cx.expr_struct_ident(trait_span, ctor_ident, rand_fields)
 152              }
 153          }
 154      }
 155  }