diff --git a/doc/rust.md b/doc/rust.md index 2a1b30bd7fa9..56d116804f53 100644 --- a/doc/rust.md +++ b/doc/rust.md @@ -1775,6 +1775,7 @@ Supported traits for `deriving` are: `obj.to_str()` has similar output as `fmt!("%?", obj)`, but it differs in that each constituent field of the type must also implement `ToStr` and will have `field.to_str()` invoked to build up the result. +* `FromPrimitive`, to create an instance from a numeric primitve. ### Stability One can indicate the stability of an API using the following attributes: diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 5111682f6d06..8e4f553cb83e 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -113,6 +113,7 @@ pub trait AstBuilder { expr: @ast::Expr, ident: ast::Ident, args: ~[@ast::Expr]) -> @ast::Expr; fn expr_block(&self, b: ast::Block) -> @ast::Expr; + fn expr_cast(&self, sp: Span, expr: @ast::Expr, ty: ast::Ty) -> @ast::Expr; fn field_imm(&self, span: Span, name: Ident, e: @ast::Expr) -> ast::Field; fn expr_struct(&self, span: Span, path: ast::Path, fields: ~[ast::Field]) -> @ast::Expr; @@ -132,6 +133,9 @@ pub trait AstBuilder { fn expr_str(&self, sp: Span, s: @str) -> @ast::Expr; fn expr_str_uniq(&self, sp: Span, s: @str) -> @ast::Expr; + fn expr_some(&self, sp: Span, expr: @ast::Expr) -> @ast::Expr; + fn expr_none(&self, sp: Span) -> @ast::Expr; + fn expr_unreachable(&self, span: Span) -> @ast::Expr; fn pat(&self, span: Span, pat: ast::Pat_) -> @ast::Pat; @@ -564,6 +568,29 @@ impl AstBuilder for @ExtCtxt { } + fn expr_cast(&self, sp: Span, expr: @ast::Expr, ty: ast::Ty) -> @ast::Expr { + self.expr(sp, ast::ExprCast(expr, ty)) + } + + + fn expr_some(&self, sp: Span, expr: @ast::Expr) -> @ast::Expr { + let some = ~[ + self.ident_of("std"), + self.ident_of("option"), + self.ident_of("Some"), + ]; + self.expr_call_global(sp, some, ~[expr]) + } + + fn expr_none(&self, sp: Span) -> @ast::Expr { + let none = self.path_global(sp, ~[ + self.ident_of("std"), + self.ident_of("option"), + self.ident_of("None"), + ]); + self.expr_path(none) + } + fn expr_unreachable(&self, span: Span) -> @ast::Expr { let loc = self.codemap().lookup_char_pos(span.lo); self.expr_call_global( diff --git a/src/libsyntax/ext/deriving/generic.rs b/src/libsyntax/ext/deriving/generic.rs index f5e45eec7e03..b3fd4f920d88 100644 --- a/src/libsyntax/ext/deriving/generic.rs +++ b/src/libsyntax/ext/deriving/generic.rs @@ -1151,6 +1151,7 @@ pub fn cs_or(enum_nonmatch_f: EnumNonMatchFunc, enum_nonmatch_f, cx, span, substructure) } + /// cs_binop with binop == and #[inline] pub fn cs_and(enum_nonmatch_f: EnumNonMatchFunc, diff --git a/src/libsyntax/ext/deriving/mod.rs b/src/libsyntax/ext/deriving/mod.rs index a428c6704f96..3e65f7bdefbc 100644 --- a/src/libsyntax/ext/deriving/mod.rs +++ b/src/libsyntax/ext/deriving/mod.rs @@ -32,6 +32,7 @@ pub mod rand; pub mod to_str; pub mod zero; pub mod default; +pub mod primitive; #[path="cmp/eq.rs"] pub mod eq; @@ -97,9 +98,12 @@ pub fn expand_meta_deriving(cx: @ExtCtxt, "Rand" => expand!(rand::expand_deriving_rand), "ToStr" => expand!(to_str::expand_deriving_to_str), + "Zero" => expand!(zero::expand_deriving_zero), "Default" => expand!(default::expand_deriving_default), + "FromPrimitive" => expand!(primitive::expand_deriving_from_primitive), + ref tname => { cx.span_err(titem.span, format!("unknown \ `deriving` trait: `{}`", *tname)); diff --git a/src/libsyntax/ext/deriving/primitive.rs b/src/libsyntax/ext/deriving/primitive.rs new file mode 100644 index 000000000000..6e012eedfa36 --- /dev/null +++ b/src/libsyntax/ext/deriving/primitive.rs @@ -0,0 +1,120 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ast::{MetaItem, item, Expr}; +use ast; +use codemap::Span; +use ext::base::ExtCtxt; +use ext::build::AstBuilder; +use ext::deriving::generic::*; + +pub fn expand_deriving_from_primitive(cx: @ExtCtxt, + span: Span, + mitem: @MetaItem, + in_items: ~[@item]) -> ~[@item] { + let trait_def = TraitDef { + path: Path::new(~["std", "num", "FromPrimitive"]), + additional_bounds: ~[], + generics: LifetimeBounds::empty(), + methods: ~[ + MethodDef { + name: "from_int", + generics: LifetimeBounds::empty(), + explicit_self: None, + args: ~[ + Literal(Path::new(~["int"])), + ], + ret_ty: Literal(Path::new_(~["std", "option", "Option"], + None, + ~[~Self], + true)), + const_nonmatching: false, + combine_substructure: |c, s, sub| cs_from("int", c, s, sub), + }, + MethodDef { + name: "from_uint", + generics: LifetimeBounds::empty(), + explicit_self: None, + args: ~[ + Literal(Path::new(~["uint"])), + ], + ret_ty: Literal(Path::new_(~["std", "option", "Option"], + None, + ~[~Self], + true)), + const_nonmatching: false, + combine_substructure: |c, s, sub| cs_from("uint", c, s, sub), + }, + ] + }; + + trait_def.expand(cx, span, mitem, in_items) +} + +fn cs_from(name: &str, cx: @ExtCtxt, span: Span, substr: &Substructure) -> @Expr { + let n = match substr.nonself_args { + [n] => n, + _ => cx.span_bug(span, "Incorrect number of arguments in `deriving(FromPrimitive)`") + }; + + return match *substr.fields { + StaticEnum(enum_def, _) => { + if enum_def.variants.is_empty() { + cx.span_fatal(span, "`FromPrimitive` cannot be derived for enums with no variants"); + } + + let mut arms = ~[]; + + for variant in enum_def.variants.iter() { + match variant.node.kind { + ast::tuple_variant_kind(ref args) => { + if !args.is_empty() { + cx.span_fatal(span, "`FromPrimitive` cannot be derived for \ + enum variants with arguments"); + } + + // expr for `$n == $variant as $name` + let variant = cx.expr_ident(span, variant.node.name); + let ty = cx.ty_ident(span, cx.ident_of(name)); + let cast = cx.expr_cast(span, variant, ty); + let guard = cx.expr_binary(span, ast::BiEq, n, cast); + + // expr for `Some($variant)` + let body = cx.expr_some(span, variant); + + // arm for `_ if $guard => $body` + let arm = ast::Arm { + pats: ~[cx.pat_wild(span)], + guard: Some(guard), + body: cx.block_expr(body), + }; + + arms.push(arm); + } + ast::struct_variant_kind(_) => { + cx.span_fatal(span, "`FromPrimitive` cannot be derived for enums \ + with struct variants"); + } + } + } + + // arm for `_ => None` + let arm = ast::Arm { + pats: ~[cx.pat_wild(span)], + guard: None, + body: cx.block_expr(cx.expr_none(span)), + }; + arms.push(arm); + + cx.expr_match(span, n, arms) + } + _ => cx.bug("expected StaticEnum in deriving(FromPrimitive)") + }; +} diff --git a/src/test/run-pass/deriving-primitive.rs b/src/test/run-pass/deriving-primitive.rs new file mode 100644 index 000000000000..9f1cab6977c7 --- /dev/null +++ b/src/test/run-pass/deriving-primitive.rs @@ -0,0 +1,37 @@ +// Copyright 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::num::FromPrimitive; +use std::int; + +#[deriving(Eq, FromPrimitive)] +enum A { + Foo = int::max_value, + Bar = 1, + Baz = 3, + Qux, +} + +fn main() { + let x: Option = FromPrimitive::from_int(int::max_value); + assert_eq!(x, Some(Foo)); + + let x: Option = FromPrimitive::from_int(1); + assert_eq!(x, Some(Bar)); + + let x: Option = FromPrimitive::from_int(3); + assert_eq!(x, Some(Baz)); + + let x: Option = FromPrimitive::from_int(4); + assert_eq!(x, Some(Qux)); + + let x: Option = FromPrimitive::from_int(5); + assert_eq!(x, None); +}