diff options
| author | Erick Tryzelaar <erick.tryzelaar@gmail.com> | 2013-09-16 21:12:18 -0700 |
|---|---|---|
| committer | Erick Tryzelaar <erick.tryzelaar@gmail.com> | 2013-10-02 07:55:41 -0700 |
| commit | 0feaccf5260bc59f7ee4294a2ecc875da669ad30 (patch) | |
| tree | 633a987e41b539c7d52acde7479f37a87f72022d /src/libsyntax/ext | |
| parent | d9d1dfc1955fabb7ee3a55e9c84cdcd5aad67417 (diff) | |
| download | rust-0feaccf5260bc59f7ee4294a2ecc875da669ad30.tar.gz rust-0feaccf5260bc59f7ee4294a2ecc875da669ad30.zip | |
syntax: Add #[deriving(FromPrimitive)] syntax extension
Right now this only works for c-style enums.
Diffstat (limited to 'src/libsyntax/ext')
| -rw-r--r-- | src/libsyntax/ext/build.rs | 27 | ||||
| -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 | 120 |
4 files changed, 152 insertions, 0 deletions
diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 5111682f6d0..8e4f553cb83 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 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..6e012eedfa3 --- /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 <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_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)") + }; +} |
