Implement #[deriving(Show)].

This commit is contained in:
Huon Wilson 2014-02-06 23:02:28 +11:00
parent 5d63910f90
commit 6a8b3ae22f
3 changed files with 182 additions and 0 deletions

View File

@ -28,6 +28,7 @@ pub mod encodable;
pub mod decodable;
pub mod rand;
pub mod to_str;
pub mod show;
pub mod zero;
pub mod default;
pub mod primitive;
@ -83,6 +84,7 @@ pub fn expand_meta_deriving(cx: &mut ExtCtxt,
"Rand" => expand!(rand::expand_deriving_rand),
"ToStr" => expand!(to_str::expand_deriving_to_str),
"Show" => expand!(show::expand_deriving_show),
"Zero" => expand!(zero::expand_deriving_zero),
"Default" => expand!(default::expand_deriving_default),

View File

@ -0,0 +1,138 @@
// Copyright 2014 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;
use ast::{MetaItem, Item, Expr};
use codemap::Span;
use ext::format;
use ext::base::ExtCtxt;
use ext::build::AstBuilder;
use ext::deriving::generic::*;
use parse::token;
use std::hashmap::HashMap;
pub fn expand_deriving_show(cx: &mut ExtCtxt,
span: Span,
mitem: @MetaItem,
in_items: ~[@Item])
-> ~[@Item] {
// &mut ::std::fmt::Formatter
let fmtr = Ptr(~Literal(Path::new(~["std", "fmt", "Formatter"])),
Borrowed(None, ast::MutMutable));
let trait_def = TraitDef {
cx: cx, span: span,
path: Path::new(~["std", "fmt", "Show"]),
additional_bounds: ~[],
generics: LifetimeBounds::empty(),
methods: ~[
MethodDef {
name: "fmt",
generics: LifetimeBounds::empty(),
explicit_self: borrowed_explicit_self(),
args: ~[fmtr],
ret_ty: Literal(Path::new(~["std", "fmt", "Result"])),
inline: false,
const_nonmatching: false,
combine_substructure: show_substructure
}
]
};
trait_def.expand(mitem, in_items)
}
// we construct a format string and then defer to std::fmt, since that
// knows what's up with formatting at so on.
fn show_substructure(cx: &mut ExtCtxt, span: Span,
substr: &Substructure) -> @Expr {
// build `<name>`, `<name>({}, {}, ...)` or `<name> { <field>: {},
// <field>: {}, ... }` based on the "shape".
//
// Easy start: they all start with the name.
let name = match *substr.fields {
Struct(_) => substr.type_ident,
EnumMatching(_, v, _) => v.node.name,
EnumNonMatching(..) | StaticStruct(..) | StaticEnum(..) => {
cx.span_bug(span, "nonsensical .fields in `#[deriving(Show)]`")
}
};
let mut format_string = token::get_ident(name.name).get().to_owned();
// the internal fields we're actually formatting
let mut exprs = ~[];
// Getting harder... making the format string:
match *substr.fields {
// unit struct/nullary variant: no work necessary!
Struct([]) | EnumMatching(_, _, []) => {}
Struct(ref fields) | EnumMatching(_, _, ref fields) => {
if fields[0].name.is_none() {
// tuple struct/"normal" variant
format_string.push_str("(");
for (i, field) in fields.iter().enumerate() {
if i != 0 { format_string.push_str(", "); }
format_string.push_str("{}");
exprs.push(field.self_);
}
format_string.push_str(")");
} else {
// normal struct/struct variant
format_string.push_str(" \\{");
for (i, field) in fields.iter().enumerate() {
if i != 0 { format_string.push_str(","); }
let name = token::get_ident(field.name.unwrap().name);
format_string.push_str(" ");
format_string.push_str(name.get());
format_string.push_str(": {}");
exprs.push(field.self_);
}
format_string.push_str(" \\}");
}
}
_ => unreachable!()
}
// AST construction!
// we're basically calling
//
// format_arg!(|__args| ::std::fmt::write(fmt.buf, __args), "<format_string>", exprs...)
//
// but doing it directly via ext::format.
let formatter = substr.nonself_args[0];
let buf = cx.expr_field_access(span, formatter, cx.ident_of("buf"));
let std_write = ~[cx.ident_of("std"), cx.ident_of("fmt"), cx.ident_of("write")];
let args = cx.ident_of("__args");
let write_call = cx.expr_call_global(span, std_write, ~[buf, cx.expr_ident(span, args)]);
let format_closure = cx.lambda_expr(span, ~[args], write_call);
let s = token::intern_and_get_ident(format_string);
let format_string = cx.expr_str(span, s);
// phew, not our responsibility any more!
format::expand_preparsed_format_args(cx, span,
format_closure,
format_string, exprs, HashMap::new())
}

View File

@ -0,0 +1,42 @@
// Copyright 2014 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(struct_variant, macro_rules)];
#[deriving(Show)]
struct Unit;
#[deriving(Show)]
struct Tuple(int, uint);
#[deriving(Show)]
struct Struct { x: int, y: uint }
#[deriving(Show)]
enum Enum {
Nullary,
Variant(int, uint),
StructVariant { x: int, y : uint }
}
macro_rules! t {
($x:expr, $expected:expr) => {
assert_eq!(format!("{}", $x), $expected.to_owned())
}
}
pub fn main() {
t!(Unit, "Unit");
t!(Tuple(1, 2), "Tuple(1, 2)");
t!(Struct { x: 1, y: 2 }, "Struct { x: 1, y: 2 }");
t!(Nullary, "Nullary");
t!(Variant(1, 2), "Variant(1, 2)");
t!(StructVariant { x: 1, y: 2 }, "StructVariant { x: 1, y: 2 }");
}