Add new crate num-derive as an new replacement for num-macro

This commit is contained in:
Łukasz Jan Niemier 2016-09-28 13:33:13 +02:00
parent 0c89b893a9
commit b7e64074b2
No known key found for this signature in database
GPG Key ID: C775391C950A6AEE
12 changed files with 330 additions and 75 deletions

View File

@ -5,6 +5,7 @@ set -ex
cargo bench --verbose
cargo test --verbose --manifest-path=macros/Cargo.toml
cargo test --verbose --manifest-path=derive/Cargo.toml
# Build test for the serde feature
cargo build --verbose --features "serde"

27
derive/Cargo.toml Normal file
View File

@ -0,0 +1,27 @@
[package]
authors = ["The Rust Project Developers"]
description = "Numeric syntax extensions"
documentation = "http://rust-num.github.io/num"
homepage = "https://github.com/rust-num/num"
keywords = ["mathematics", "numerics"]
license = "MIT/Apache-2.0"
name = "num-derive"
repository = "https://github.com/rust-num/num"
version = "0.1.33"
[dependencies]
quote = "0.1.3"
syn = "0.7.0"
[dev-dependencies]
compiletest_rs = "0.2.2"
[dev-dependencies.num]
path = ".."
version = "0.1"
[lib]
crate-type = ["rustc-macro"]
name = "num_derive"
rustc-macro = true
test = false

76
derive/src/lib.rs Normal file
View File

@ -0,0 +1,76 @@
// Copyright 2012-2015 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.
#![crate_type = "rustc-macro"]
#![feature(rustc_macro, rustc_macro_lib)]
extern crate syn;
#[macro_use]
extern crate quote;
extern crate rustc_macro;
use rustc_macro::TokenStream;
use syn::Body::Enum;
use syn::VariantData::Unit;
#[rustc_macro_derive(FromPrimitive)]
pub fn from_primitive(input: TokenStream) -> TokenStream {
let source = input.to_string();
let ast = syn::parse_macro_input(&source).unwrap();
let name = &ast.ident;
let variants = match ast.body {
Enum(ref variants) => variants,
_ => {
panic!("`FromPrimitive` can be applied only to the enums, {} is not an enum",
name)
}
};
let mut idx = 0;
let variants: Vec<_> = variants.iter()
.map(|variant| {
let ident = &variant.ident;
match variant.data {
Unit => (),
_ => {
panic!("`FromPrimitive` can be applied only to unitary enums, {}::{} is either struct or tuple", name, ident)
},
}
if let Some(val) = variant.discriminant {
idx = val.value;
}
let tt = quote!(#idx => Some(#name::#ident));
idx += 1;
tt
})
.collect();
let res = quote! {
#ast
impl ::num::traits::FromPrimitive for #name {
fn from_i64(n: i64) -> Option<Self> {
Self::from_u64(n as u64)
}
fn from_u64(n: u64) -> Option<Self> {
match n {
#(variants,)*
_ => None,
}
}
}
};
res.to_string().parse().unwrap()
}

View File

@ -13,7 +13,7 @@
extern crate num;
#[macro_use]
extern crate num_macros;
extern crate num_derive;
#[derive(Debug, PartialEq, FromPrimitive)] //~ ERROR
struct Color {

View File

@ -13,7 +13,7 @@
extern crate num;
#[macro_use]
extern crate num_macros;
extern crate num_derive;
#[derive(Debug, PartialEq, FromPrimitive)] //~ ERROR
enum Color {

View File

@ -13,7 +13,7 @@
extern crate num;
#[macro_use]
extern crate num_macros;
extern crate num_derive;
#[derive(Debug, PartialEq, FromPrimitive)]
enum Color {}

View File

@ -12,7 +12,7 @@
extern crate num;
#[macro_use]
extern crate num_macros;
extern crate num_derive;
#[derive(Debug, PartialEq, FromPrimitive)]
enum Color {

View File

@ -12,7 +12,7 @@
extern crate num;
#[macro_use]
extern crate num_macros;
extern crate num_derive;
#[derive(Debug, PartialEq, FromPrimitive)]
enum Color {

View File

@ -1,27 +1,17 @@
[package]
authors = ["The Rust Project Developers"]
description = "Numeric syntax extensions"
documentation = "http://rust-num.github.io/num"
homepage = "https://github.com/rust-num/num"
keywords = ["mathematics", "numerics"]
license = "MIT/Apache-2.0"
name = "num-macros"
repository = "https://github.com/rust-num/num"
version = "0.1.33"
[dependencies]
quote = "0.1.3"
syn = "0.6.0"
[dev-dependencies]
compiletest_rs = "0.2.2"
[dev-dependencies.num]
path = ".."
version = "0.1"
authors = ["The Rust Project Developers"]
license = "MIT/Apache-2.0"
homepage = "https://github.com/rust-num/num"
repository = "https://github.com/rust-num/num"
documentation = "http://rust-num.github.io/num"
keywords = ["mathematics", "numerics"]
description = "Numeric syntax extensions"
[lib]
crate-type = ["rustc-macro"]
name = "num_macros"
rustc-macro = true
test = false
plugin = true
[dev-dependencies]
num = { path = "..", version = "0.1" }

View File

@ -8,69 +8,194 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![crate_type = "rustc-macro"]
#![feature(rustc_macro, rustc_macro_lib)]
#![feature(plugin_registrar, rustc_private)]
extern crate syn;
#[macro_use]
extern crate quote;
extern crate rustc_macro;
extern crate syntax;
extern crate syntax_ext;
extern crate rustc_plugin;
use rustc_macro::TokenStream;
use syntax::ast::{MetaItem, Expr, BinOpKind};
use syntax::ast;
use syntax::codemap::Span;
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::InternedString;
use syntax::ptr::P;
use syntax::ext::base::MultiDecorator;
use syntax::parse::token;
use syn::Body::Enum;
use syn::VariantData::Unit;
use rustc_plugin::Registry;
#[rustc_macro_derive(FromPrimitive)]
pub fn from_primitive(input: TokenStream) -> TokenStream {
let source = input.to_string();
macro_rules! pathvec {
($($x:ident)::+) => (
vec![ $( stringify!($x) ),+ ]
)
}
let ast = syn::parse_macro_input(&source).unwrap();
let name = &ast.ident;
macro_rules! path {
($($x:tt)*) => (
::syntax_ext::deriving::generic::ty::Path::new( pathvec!( $($x)* ) )
)
}
let variants = match ast.body {
Enum(ref variants) => variants,
_ => {
panic!("`FromPrimitive` can be applied only to the enums, {} is not an enum",
name)
macro_rules! path_local {
($x:ident) => (
::syntax_ext::deriving::generic::ty::Path::new_local(stringify!($x))
)
}
macro_rules! pathvec_std {
($cx:expr, $first:ident :: $($rest:ident)::+) => ({
let mut v = pathvec!($($rest)::+);
if let Some(s) = $cx.crate_root {
v.insert(0, s);
}
v
})
}
pub fn expand_deriving_from_primitive(cx: &mut ExtCtxt,
span: Span,
mitem: &MetaItem,
item: &Annotatable,
push: &mut FnMut(Annotatable))
{
let inline = cx.meta_word(span, InternedString::new("inline"));
let attrs = vec!(cx.attribute(span, inline));
let trait_def = TraitDef {
is_unsafe: false,
span: span,
attributes: Vec::new(),
path: path!(num::FromPrimitive),
additional_bounds: Vec::new(),
generics: LifetimeBounds::empty(),
methods: vec!(
MethodDef {
name: "from_i64",
is_unsafe: false,
unify_fieldless_variants: false,
generics: LifetimeBounds::empty(),
explicit_self: None,
args: vec!(Literal(path_local!(i64))),
ret_ty: Literal(Path::new_(pathvec_std!(cx, core::option::Option),
None,
vec!(Box::new(Self_)),
true)),
// #[inline] liable to cause code-bloat
attributes: attrs.clone(),
combine_substructure: combine_substructure(Box::new(|c, s, sub| {
cs_from("i64", c, s, sub)
})),
},
MethodDef {
name: "from_u64",
is_unsafe: false,
unify_fieldless_variants: false,
generics: LifetimeBounds::empty(),
explicit_self: None,
args: vec!(Literal(path_local!(u64))),
ret_ty: Literal(Path::new_(pathvec_std!(cx, core::option::Option),
None,
vec!(Box::new(Self_)),
true)),
// #[inline] liable to cause code-bloat
attributes: attrs,
combine_substructure: combine_substructure(Box::new(|c, s, sub| {
cs_from("u64", c, s, sub)
})),
}
),
associated_types: Vec::new(),
supports_unions: false,
};
let mut idx = 0;
let variants: Vec<_> = variants.iter()
.map(|variant| {
let ident = &variant.ident;
match variant.data {
Unit => (),
_ => {
panic!("`FromPrimitive` can be applied only to unitary enums, {}::{} is either struct or tuple", name, ident)
},
}
if let Some(val) = variant.discriminant {
idx = val.value;
}
let tt = quote!(#idx => Some(#name::#ident));
idx += 1;
tt
})
.collect();
trait_def.expand(cx, mitem, &item, push)
}
let res = quote! {
#ast
fn cs_from(name: &str, cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P<Expr> {
if substr.nonself_args.len() != 1 {
cx.span_bug(trait_span, "incorrect number of arguments in `derive(FromPrimitive)`")
}
impl ::num::traits::FromPrimitive for #name {
fn from_i64(n: i64) -> Option<Self> {
Self::from_u64(n as u64)
let n = &substr.nonself_args[0];
match *substr.fields {
StaticStruct(..) => {
cx.span_err(trait_span, "`FromPrimitive` cannot be derived for structs");
return cx.expr_fail(trait_span, InternedString::new(""));
}
StaticEnum(enum_def, _) => {
if enum_def.variants.is_empty() {
cx.span_err(trait_span,
"`FromPrimitive` cannot be derived for enums with no variants");
return cx.expr_fail(trait_span, InternedString::new(""));
}
fn from_u64(n: u64) -> Option<Self> {
match n {
#(variants,)*
_ => None,
let mut arms = Vec::new();
for variant in &enum_def.variants {
match variant.node.data {
ast::VariantData::Unit(..) => {
let span = variant.span;
// expr for `$n == $variant as $name`
let path = cx.path(span, vec![substr.type_ident, variant.node.name]);
let variant = cx.expr_path(path);
let ty = cx.ty_ident(span, cx.ident_of(name));
let cast = cx.expr_cast(span, variant.clone(), ty);
let guard = cx.expr_binary(span, BinOpKind::Eq, n.clone(), cast);
// expr for `Some($variant)`
let body = cx.expr_some(span, variant);
// arm for `_ if $guard => $body`
let arm = ast::Arm {
attrs: vec!(),
pats: vec!(cx.pat_wild(span)),
guard: Some(guard),
body: body,
};
arms.push(arm);
}
ast::VariantData::Tuple(..) => {
cx.span_err(trait_span,
"`FromPrimitive` cannot be derived for \
enum variants with arguments");
return cx.expr_fail(trait_span,
InternedString::new(""));
}
ast::VariantData::Struct(..) => {
cx.span_err(trait_span,
"`FromPrimitive` cannot be derived for enums \
with struct variants");
return cx.expr_fail(trait_span,
InternedString::new(""));
}
}
}
}
};
res.to_string().parse().unwrap()
// arm for `_ => None`
let arm = ast::Arm {
attrs: vec!(),
pats: vec!(cx.pat_wild(trait_span)),
guard: None,
body: cx.expr_none(trait_span),
};
arms.push(arm);
cx.expr_match(trait_span, n.clone(), arms)
}
_ => cx.span_bug(trait_span, "expected StaticEnum in derive(FromPrimitive)")
}
}
#[plugin_registrar]
#[doc(hidden)]
pub fn plugin_registrar(reg: &mut Registry) {
reg.register_syntax_extension(
token::intern("derive_NumFromPrimitive"),
MultiDecorator(Box::new(expand_deriving_from_primitive)));
}

View File

@ -0,0 +1,36 @@
// Copyright 2013-2015 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.
#![feature(custom_derive, plugin)]
#![plugin(num_macros)]
extern crate num;
#[derive(Debug, PartialEq, NumFromPrimitive)]
enum Color {
Red,
Blue,
Green,
}
#[test]
fn test_from_primitive() {
let v: Vec<Option<Color>> = vec![
num::FromPrimitive::from_u64(0),
num::FromPrimitive::from_u64(1),
num::FromPrimitive::from_u64(2),
num::FromPrimitive::from_u64(3),
];
assert_eq!(
v,
vec![Some(Color::Red), Some(Color::Blue), Some(Color::Green), None]
);
}