macros 1.1
diff --git a/rand_macros/Cargo.toml b/rand_macros/Cargo.toml
index 143bfb3..809c827 100644
--- a/rand_macros/Cargo.toml
+++ b/rand_macros/Cargo.toml
@@ -9,12 +9,15 @@
documentation = "http://doc.rust-lang.org/rand"
homepage = "https://github.com/rust-lang/rand"
description = """
-`#[derive]`-like functionality for the `rand::Rand` trait.
+`#[derive(Rand)]` functionality for the `rand::Rand` trait.
"""
[lib]
-name = "rand_macros"
-plugin = true
+proc-macro = true
+
+[dependencies]
+quote = "0.3"
+syn = "0.11"
[dev-dependencies]
rand = { path = "..", version = "0.3" }
diff --git a/rand_macros/README.md b/rand_macros/README.md
index 7e7ea8b..3b01cd9 100644
--- a/rand_macros/README.md
+++ b/rand_macros/README.md
@@ -1,23 +1,51 @@
-`#[derive]`-like functionality for the `rand::Rand` trait.
-## Example
+rand_macros
+====
+
+`#[derive(Rand)]` functionality for the `rand::Rand` trait.
+
+## Usage
+Add this to your `Cargo.toml`:
+
+```toml
+[dependencies]
+rand = "0.3"
+rand_macros = "0.2"
+```
+
+and this to your crate root:
```rust
-#![feature(plugin)]
-
-#![plugin(rand_macros)]
-
extern crate rand;
+#[macro_use]
+extern crate rand_macros;
+```
-#[derive_Rand]
+## Examples
+
+`#[derive(Rand)]` can be used on any `struct` or `enum` where all fields/variants implement `rand::Rand`.
+
+```rust
+#[derive(Debug, Rand)]
struct Foo {
- x: u8,
- y: isize
+ x: u16,
+ y: Option<f64>,
}
-#[derive_Rand]
+#[derive(Debug, Rand)]
enum Bar {
- X(char),
- Y(f64)
+ X{x: u8, y: isize},
+ Y([bool; 4]),
+ Z,
}
```
+Now you can call the `Rng::gen()` function on your custom types.
+
+```rust
+use rand::Rng;
+
+let mut rng = rand::thread_rng();
+
+println!("{:?}", rng.gen::<Foo>());
+println!("{:?}", rng.gen::<Bar>());
+```
diff --git a/rand_macros/src/lib.rs b/rand_macros/src/lib.rs
index 9bf9b0e..be497e9 100644
--- a/rand_macros/src/lib.rs
+++ b/rand_macros/src/lib.rs
@@ -1,182 +1,85 @@
-// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
+extern crate proc_macro;
+#[macro_use]
+extern crate quote;
+extern crate syn;
-#![feature(rustc_private, plugin_registrar)]
+use proc_macro::TokenStream;
-extern crate syntax;
-extern crate syntax_ext;
-extern crate rustc_plugin;
-
-use syntax::ast::{MetaItem, Expr};
-use syntax::ast;
-use syntax::codemap::Span;
-use syntax::ext::base;
-use syntax::ext::base::{ExtCtxt, Annotatable};
-use syntax::ext::build::AstBuilder;
-use syntax_ext::deriving::generic::*;
-use syntax_ext::deriving::generic::ty::*;
-use syntax::parse::token;
-use syntax::ptr::P;
-use rustc_plugin::Registry;
-
-#[plugin_registrar]
-pub fn plugin_registrar(reg: &mut Registry) {
- reg.register_syntax_extension(token::intern("derive_Rand"),
- base::MultiDecorator(Box::new(expand_deriving_rand)));
+#[proc_macro_derive(Rand)]
+pub fn rand_derive(input: TokenStream) -> TokenStream {
+ let s = input.to_string();
+ let ast = syn::parse_derive_input(&s).unwrap();
+ let gen = impl_rand_derive(&ast);
+ gen.parse().unwrap()
}
+fn impl_rand_derive(ast: &syn::MacroInput) -> quote::Tokens {
+ let name = &ast.ident;
+ let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
+ let rand = match ast.body {
+ syn::Body::Struct(syn::VariantData::Struct(ref body)) => {
+ let fields = body
+ .iter()
+ .filter_map(|field| field.ident.as_ref())
+ .map(|ident| quote! { #ident: rng.gen() })
+ .collect::<Vec<_>>();
+ quote! { #name { #(#fields,)* } }
+ },
+ syn::Body::Struct(syn::VariantData::Tuple(ref body)) => {
+ let fields = (0..body.len())
+ .map(|_| quote! { rng.gen() })
+ .collect::<Vec<_>>();
-pub fn expand_deriving_rand(cx: &mut ExtCtxt,
- span: Span,
- mitem: &MetaItem,
- item: &Annotatable,
- push: &mut FnMut(Annotatable)) {
- let trait_def = TraitDef {
- span: span,
- attributes: Vec::new(),
- path: Path::new(vec!("rand", "Rand")),
- additional_bounds: Vec::new(),
- generics: LifetimeBounds::empty(),
- is_unsafe: false,
- methods: vec!(
- MethodDef {
- name: "rand",
- is_unsafe: false,
- generics: LifetimeBounds {
- lifetimes: Vec::new(),
- bounds: vec!(("R",
- vec!( Path::new(vec!("rand", "Rng")) )))
- },
- explicit_self: None,
- args: vec!(
- Ptr(Box::new(Literal(Path::new_local("R"))),
- Borrowed(None, ast::MutMutable))
- ),
- ret_ty: Self_,
- attributes: Vec::new(),
- combine_substructure: combine_substructure(Box::new(|a, b, c| {
- rand_substructure(a, b, c)
- }))
+ quote! { #name (#(#fields),*) }
+ },
+ syn::Body::Struct(syn::VariantData::Unit) => {
+ quote! { #name }
+ },
+ syn::Body::Enum(ref body) => {
+ if body.is_empty() {
+ panic!("`Rand` cannot be derived for enums with no variants");
}
- ),
- associated_types: Vec::new(),
- };
- trait_def.expand(cx, mitem, &item, &mut |i| push(i))
-}
-fn rand_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P<Expr> {
- let rng = if substr.nonself_args.len() == 1 {
- &substr.nonself_args[0]
- } else {
- cx.bug("Incorrect number of arguments to `rand` in `derive(Rand)`")
- };
- let rand_ident = vec!(
- cx.ident_of("rand"),
- cx.ident_of("Rand"),
- cx.ident_of("rand")
- );
- let rand_call = |cx: &mut ExtCtxt, span| {
- cx.expr_call_global(span,
- rand_ident.clone(),
- vec!(rng.clone()))
- };
+ let len = body.len();
+ let mut variants = body
+ .iter()
+ .enumerate()
+ .map(|(index, variant)| {
+ let ident = &variant.ident;
+ let arm = match variant.data {
+ syn::VariantData::Struct(ref body) => {
+ let fields = body
+ .iter()
+ .filter_map(|field| field.ident.as_ref())
+ .map(|ident| quote! { #ident: rng.gen() })
+ .collect::<Vec<_>>();
+ quote! { #name::#ident { #(#fields,)* } }
+ },
+ syn::VariantData::Tuple(ref body) => {
+ let fields = (0..body.len())
+ .map(|_| quote! { rng.gen() })
+ .collect::<Vec<_>>();
- return match *substr.fields {
- StaticStruct(_, ref summary) => {
- let path = cx.path_ident(trait_span, substr.type_ident);
- rand_thing(cx, trait_span, path, summary, rand_call)
+ quote! { #name::#ident (#(#fields),*) }
+ },
+ syn::VariantData::Unit => quote! { #name::#ident }
+ };
+
+ quote! { #index => #arm }
+ })
+ .collect::<Vec<_>>();
+ variants.push(quote! { _ => unreachable!() });
+ quote! { match rng.gen_range(0, #len) { #(#variants,)* } }
}
- StaticEnum(_, ref variants) => {
- if variants.is_empty() {
- cx.span_err(trait_span, "`Rand` cannot be derived for enums with no variants");
- // let compilation continue
- return cx.expr_usize(trait_span, 0);
- }
-
- let variant_count = cx.expr_usize(trait_span, variants.len());
-
- let rand_name = cx.path_all(trait_span,
- true,
- rand_ident.clone(),
- Vec::new(),
- Vec::new(),
- Vec::new());
- let rand_name = cx.expr_path(rand_name);
-
- // ::rand::Rand::rand(rng)
- let rv_call = cx.expr_call(trait_span,
- rand_name,
- vec!(rng.clone()));
-
- // need to specify the usize-ness of the random number
- let usize_ty = cx.ty_ident(trait_span, cx.ident_of("usize"));
- let value_ident = cx.ident_of("__value");
- let let_statement = cx.stmt_let_typed(trait_span,
- false,
- value_ident,
- usize_ty,
- rv_call);
-
- // rand() % variants.len()
- let value_ref = cx.expr_ident(trait_span, value_ident);
- let rand_variant = cx.expr_binary(trait_span,
- ast::BiRem,
- value_ref,
- variant_count);
-
- let mut arms = variants.iter().enumerate().map(|(i, &(ident, v_span, ref summary))| {
- let i_expr = cx.expr_usize(v_span, i);
- let pat = cx.pat_lit(v_span, i_expr);
-
- let path = cx.path(v_span, vec![substr.type_ident, ident]);
- let thing = rand_thing(cx, v_span, path, summary, |cx, sp| rand_call(cx, sp));
- cx.arm(v_span, vec!( pat ), thing)
- }).collect::<Vec<ast::Arm> >();
-
- // _ => {} at the end. Should never occur
- arms.push(cx.arm_unreachable(trait_span));
-
- let match_expr = cx.expr_match(trait_span, rand_variant, arms);
-
- let block = cx.block(trait_span, vec!( let_statement ), Some(match_expr));
- cx.expr_block(block)
- }
- _ => cx.bug("Non-static method in `derive(Rand)`")
};
- fn rand_thing<F>(cx: &mut ExtCtxt,
- trait_span: Span,
- ctor_path: ast::Path,
- summary: &StaticFields,
- mut rand_call: F)
- -> P<Expr> where
- F: FnMut(&mut ExtCtxt, Span) -> P<Expr>,
- {
- let path = cx.expr_path(ctor_path.clone());
- match *summary {
- Unnamed(ref fields) => {
- if fields.is_empty() {
- path
- } else {
- let exprs = fields.iter().map(|span| rand_call(cx, *span)).collect();
- cx.expr_call(trait_span, path, exprs)
- }
- }
- Named(ref fields) => {
- let rand_fields = fields.iter().map(|&(ident, span)| {
- let e = rand_call(cx, span);
- cx.field_imm(span, ident, e)
- }).collect();
- cx.expr_struct(trait_span, ctor_path, rand_fields)
+ quote! {
+ impl #impl_generics ::rand::Rand for #name #ty_generics #where_clause {
+ #[inline]
+ fn rand<R: ::rand::Rng>(rng: &mut R) -> Self {
+ #rand
}
}
}
diff --git a/rand_macros/tests/rand_macros.rs b/rand_macros/tests/rand_macros.rs
index 8cd3c8b..5cc1554 100644
--- a/rand_macros/tests/rand_macros.rs
+++ b/rand_macros/tests/rand_macros.rs
@@ -1,21 +1,28 @@
#![allow(dead_code)]
-#![feature(plugin, custom_derive)]
-#![plugin(rand_macros)]
extern crate rand;
+#[macro_use]
+extern crate rand_macros;
use rand::Rng;
-#[derive_Rand]
-struct Foo {
- x: u8,
- y: isize
+#[derive(Rand)]
+struct Struct {
+ x: u16,
+ y: Option<f64>,
}
-#[derive_Rand]
-enum Bar {
- X(char),
- Y(f64)
+#[derive(Rand)]
+struct Tuple(i16, Option<f64>);
+
+#[derive(Rand)]
+struct Unit;
+
+#[derive(Rand)]
+enum Enum {
+ X { x: u8, y: isize },
+ Y([bool; 4]),
+ Z,
}
#[test]
@@ -24,7 +31,9 @@
// check nothing horrible happens internally:
for _ in 0..100 {
- let _: Foo = rng.gen();
- let _: Bar = rng.gen();
+ let _ = rng.gen::<Struct>();
+ let _ = rng.gen::<Tuple>();
+ let _ = rng.gen::<Unit>();
+ let _ = rng.gen::<Enum>();
}
}