Skip to content

Commit 6a8b3ae

Browse files
committed
Implement #[deriving(Show)].
1 parent 5d63910 commit 6a8b3ae

File tree

3 files changed

+182
-0
lines changed

3 files changed

+182
-0
lines changed

src/libsyntax/ext/deriving/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pub mod encodable;
2828
pub mod decodable;
2929
pub mod rand;
3030
pub mod to_str;
31+
pub mod show;
3132
pub mod zero;
3233
pub mod default;
3334
pub mod primitive;
@@ -83,6 +84,7 @@ pub fn expand_meta_deriving(cx: &mut ExtCtxt,
8384
"Rand" => expand!(rand::expand_deriving_rand),
8485

8586
"ToStr" => expand!(to_str::expand_deriving_to_str),
87+
"Show" => expand!(show::expand_deriving_show),
8688

8789
"Zero" => expand!(zero::expand_deriving_zero),
8890
"Default" => expand!(default::expand_deriving_default),

src/libsyntax/ext/deriving/show.rs

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use ast;
12+
use ast::{MetaItem, Item, Expr};
13+
use codemap::Span;
14+
use ext::format;
15+
use ext::base::ExtCtxt;
16+
use ext::build::AstBuilder;
17+
use ext::deriving::generic::*;
18+
19+
use parse::token;
20+
21+
use std::hashmap::HashMap;
22+
23+
pub fn expand_deriving_show(cx: &mut ExtCtxt,
24+
span: Span,
25+
mitem: @MetaItem,
26+
in_items: ~[@Item])
27+
-> ~[@Item] {
28+
// &mut ::std::fmt::Formatter
29+
let fmtr = Ptr(~Literal(Path::new(~["std", "fmt", "Formatter"])),
30+
Borrowed(None, ast::MutMutable));
31+
32+
let trait_def = TraitDef {
33+
cx: cx, span: span,
34+
35+
path: Path::new(~["std", "fmt", "Show"]),
36+
additional_bounds: ~[],
37+
generics: LifetimeBounds::empty(),
38+
methods: ~[
39+
MethodDef {
40+
name: "fmt",
41+
generics: LifetimeBounds::empty(),
42+
explicit_self: borrowed_explicit_self(),
43+
args: ~[fmtr],
44+
ret_ty: Literal(Path::new(~["std", "fmt", "Result"])),
45+
inline: false,
46+
const_nonmatching: false,
47+
combine_substructure: show_substructure
48+
}
49+
]
50+
};
51+
trait_def.expand(mitem, in_items)
52+
}
53+
54+
// we construct a format string and then defer to std::fmt, since that
55+
// knows what's up with formatting at so on.
56+
fn show_substructure(cx: &mut ExtCtxt, span: Span,
57+
substr: &Substructure) -> @Expr {
58+
// build `<name>`, `<name>({}, {}, ...)` or `<name> { <field>: {},
59+
// <field>: {}, ... }` based on the "shape".
60+
//
61+
// Easy start: they all start with the name.
62+
let name = match *substr.fields {
63+
Struct(_) => substr.type_ident,
64+
EnumMatching(_, v, _) => v.node.name,
65+
66+
EnumNonMatching(..) | StaticStruct(..) | StaticEnum(..) => {
67+
cx.span_bug(span, "nonsensical .fields in `#[deriving(Show)]`")
68+
}
69+
};
70+
71+
let mut format_string = token::get_ident(name.name).get().to_owned();
72+
// the internal fields we're actually formatting
73+
let mut exprs = ~[];
74+
75+
// Getting harder... making the format string:
76+
match *substr.fields {
77+
// unit struct/nullary variant: no work necessary!
78+
Struct([]) | EnumMatching(_, _, []) => {}
79+
80+
Struct(ref fields) | EnumMatching(_, _, ref fields) => {
81+
if fields[0].name.is_none() {
82+
// tuple struct/"normal" variant
83+
84+
format_string.push_str("(");
85+
86+
for (i, field) in fields.iter().enumerate() {
87+
if i != 0 { format_string.push_str(", "); }
88+
89+
format_string.push_str("{}");
90+
91+
exprs.push(field.self_);
92+
}
93+
94+
format_string.push_str(")");
95+
} else {
96+
// normal struct/struct variant
97+
98+
format_string.push_str(" \\{");
99+
100+
for (i, field) in fields.iter().enumerate() {
101+
if i != 0 { format_string.push_str(","); }
102+
103+
let name = token::get_ident(field.name.unwrap().name);
104+
format_string.push_str(" ");
105+
format_string.push_str(name.get());
106+
format_string.push_str(": {}");
107+
108+
exprs.push(field.self_);
109+
}
110+
111+
format_string.push_str(" \\}");
112+
}
113+
}
114+
_ => unreachable!()
115+
}
116+
117+
// AST construction!
118+
// we're basically calling
119+
//
120+
// format_arg!(|__args| ::std::fmt::write(fmt.buf, __args), "<format_string>", exprs...)
121+
//
122+
// but doing it directly via ext::format.
123+
let formatter = substr.nonself_args[0];
124+
let buf = cx.expr_field_access(span, formatter, cx.ident_of("buf"));
125+
126+
let std_write = ~[cx.ident_of("std"), cx.ident_of("fmt"), cx.ident_of("write")];
127+
let args = cx.ident_of("__args");
128+
let write_call = cx.expr_call_global(span, std_write, ~[buf, cx.expr_ident(span, args)]);
129+
let format_closure = cx.lambda_expr(span, ~[args], write_call);
130+
131+
let s = token::intern_and_get_ident(format_string);
132+
let format_string = cx.expr_str(span, s);
133+
134+
// phew, not our responsibility any more!
135+
format::expand_preparsed_format_args(cx, span,
136+
format_closure,
137+
format_string, exprs, HashMap::new())
138+
}

src/test/run-pass/deriving-show.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#[feature(struct_variant, macro_rules)];
12+
13+
#[deriving(Show)]
14+
struct Unit;
15+
16+
#[deriving(Show)]
17+
struct Tuple(int, uint);
18+
19+
#[deriving(Show)]
20+
struct Struct { x: int, y: uint }
21+
22+
#[deriving(Show)]
23+
enum Enum {
24+
Nullary,
25+
Variant(int, uint),
26+
StructVariant { x: int, y : uint }
27+
}
28+
29+
macro_rules! t {
30+
($x:expr, $expected:expr) => {
31+
assert_eq!(format!("{}", $x), $expected.to_owned())
32+
}
33+
}
34+
35+
pub fn main() {
36+
t!(Unit, "Unit");
37+
t!(Tuple(1, 2), "Tuple(1, 2)");
38+
t!(Struct { x: 1, y: 2 }, "Struct { x: 1, y: 2 }");
39+
t!(Nullary, "Nullary");
40+
t!(Variant(1, 2), "Variant(1, 2)");
41+
t!(StructVariant { x: 1, y: 2 }, "StructVariant { x: 1, y: 2 }");
42+
}

0 commit comments

Comments
 (0)