diff options
| author | bors <bors@rust-lang.org> | 2013-10-05 14:26:44 -0700 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2013-10-05 14:26:44 -0700 |
| commit | 2733b189ac60cea541fbf80e5839e5027ffc9fbf (patch) | |
| tree | b9fcc58a2f736f292eb32de9553843c99f91f32d /src/libsyntax | |
| parent | 0c388be8d1988a966ef62c545b996b9da0f71e93 (diff) | |
| parent | 0e8ad4d8a2f23206c723137d765027a7acd97837 (diff) | |
| download | rust-2733b189ac60cea541fbf80e5839e5027ffc9fbf.tar.gz rust-2733b189ac60cea541fbf80e5839e5027ffc9fbf.zip | |
auto merge of #9250 : erickt/rust/num, r=erickt
This PR solves one of the pain points with c-style enums. Simplifies writing a fn to convert from an int/uint to an enum. It does this through a `#[deriving(FromPrimitive)]` syntax extension. Before this is committed though, we need to discuss if `ToPrimitive`/`FromPrimitive` has the right design (cc #4819). I've changed all the `.to_int()` and `from_int()` style functions to return `Option<int>` so we can handle partial functions. For this PR though only enums and `extra::num::bigint::*` take advantage of returning None for unrepresentable values. In the long run it'd be better if `i64.to_i8()` returned `None` if the value was too large, but I'll save this for a future PR. Closes #3868.
Diffstat (limited to 'src/libsyntax')
| -rw-r--r-- | src/libsyntax/ext/build.rs | 36 | ||||
| -rw-r--r-- | src/libsyntax/ext/deriving/generic.rs | 1 | ||||
| -rw-r--r-- | src/libsyntax/ext/deriving/mod.rs | 4 | ||||
| -rw-r--r-- | src/libsyntax/ext/deriving/primitive.rs | 127 |
4 files changed, 166 insertions, 2 deletions
diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 5111682f6d0..65a6572fa5e 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,10 @@ 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_fail(&self, span: Span, msg: @str) -> @ast::Expr; fn expr_unreachable(&self, span: Span) -> @ast::Expr; fn pat(&self, span: Span, pat: ast::Pat_) -> @ast::Pat; @@ -564,7 +569,30 @@ impl AstBuilder for @ExtCtxt { } - fn expr_unreachable(&self, span: Span) -> @ast::Expr { + 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_fail(&self, span: Span, msg: @str) -> @ast::Expr { let loc = self.codemap().lookup_char_pos(span.lo); self.expr_call_global( span, @@ -575,12 +603,16 @@ impl AstBuilder for @ExtCtxt { self.ident_of("fail_with"), ], ~[ - self.expr_str(span, @"internal error: entered unreachable code"), + self.expr_str(span, msg), self.expr_str(span, loc.file.name), self.expr_uint(span, loc.line), ]) } + fn expr_unreachable(&self, span: Span) -> @ast::Expr { + self.expr_fail(span, @"internal error: entered unreachable code") + } + fn pat(&self, span: Span, pat: ast::Pat_) -> @ast::Pat { @ast::Pat { id: ast::DUMMY_NODE_ID, node: pat, span: span } diff --git a/src/libsyntax/ext/deriving/generic.rs b/src/libsyntax/ext/deriving/generic.rs index f5e45eec7e0..b3fd4f920d8 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 a428c6704f9..3e65f7bdefb 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 00000000000..38c30def1d1 --- /dev/null +++ b/src/libsyntax/ext/deriving/primitive.rs @@ -0,0 +1,127 @@ +// 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. + +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_i64", + generics: LifetimeBounds::empty(), + explicit_self: None, + args: ~[ + Literal(Path::new(~["i64"])), + ], + ret_ty: Literal(Path::new_(~["std", "option", "Option"], + None, + ~[~Self], + true)), + const_nonmatching: false, + combine_substructure: |c, s, sub| cs_from("i64", c, s, sub), + }, + MethodDef { + name: "from_u64", + generics: LifetimeBounds::empty(), + explicit_self: None, + args: ~[ + Literal(Path::new(~["u64"])), + ], + ret_ty: Literal(Path::new_(~["std", "option", "Option"], + None, + ~[~Self], + true)), + const_nonmatching: false, + combine_substructure: |c, s, sub| cs_from("u64", 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)`") + }; + + match *substr.fields { + StaticStruct(*) => { + cx.span_err(span, "`FromPrimitive` cannot be derived for structs"); + return cx.expr_fail(span, @""); + } + StaticEnum(enum_def, _) => { + if enum_def.variants.is_empty() { + cx.span_err(span, "`FromPrimitive` cannot be derived for enums with no variants"); + return cx.expr_fail(span, @""); + } + + 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_err(span, "`FromPrimitive` cannot be derived for \ + enum variants with arguments"); + return cx.expr_fail(span, @""); + } + + // 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_err(span, "`FromPrimitive` cannot be derived for enums \ + with struct variants"); + return cx.expr_fail(span, @""); + } + } + } + + // 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)") + } +} |
