auto merge of #8564 : alexcrichton/rust/ifmt+++, r=graydon

See discussion in #8489, but this selects option 3 by adding a `Default` trait to be implemented by various basic types.

Once this makes it into a snapshot I think it's about time to start overhauling all current use-cases of `fmt!` to move towards `ifmt!`. The goal is to replace `%X` with `{}` in 90% of situations, and this commit should enable that.
This commit is contained in:
bors 2013-08-19 01:42:05 -07:00
commit 3e4f40ec5a
4 changed files with 105 additions and 32 deletions

View File

@ -356,28 +356,46 @@ pub struct Argument<'self> {
priv value: &'self util::Void,
}
/// When a format is not otherwise specified, types are formatted by ascribing
/// to this trait. There is not an explicit way of selecting this trait to be
/// used for formatting, it is only if no other format is specified.
#[allow(missing_doc)]
pub trait Default { fn fmt(&Self, &mut Formatter); }
/// Format trait for the `b` character
#[allow(missing_doc)]
pub trait Bool { fn fmt(&Self, &mut Formatter); }
/// Format trait for the `c` character
#[allow(missing_doc)]
pub trait Char { fn fmt(&Self, &mut Formatter); }
/// Format trait for the `i` and `d` characters
#[allow(missing_doc)]
pub trait Signed { fn fmt(&Self, &mut Formatter); }
/// Format trait for the `u` character
#[allow(missing_doc)]
pub trait Unsigned { fn fmt(&Self, &mut Formatter); }
/// Format trait for the `o` character
#[allow(missing_doc)]
pub trait Octal { fn fmt(&Self, &mut Formatter); }
/// Format trait for the `b` character
#[allow(missing_doc)]
pub trait Binary { fn fmt(&Self, &mut Formatter); }
/// Format trait for the `x` character
#[allow(missing_doc)]
pub trait LowerHex { fn fmt(&Self, &mut Formatter); }
/// Format trait for the `X` character
#[allow(missing_doc)]
pub trait UpperHex { fn fmt(&Self, &mut Formatter); }
/// Format trait for the `s` character
#[allow(missing_doc)]
pub trait String { fn fmt(&Self, &mut Formatter); }
/// Format trait for the `?` character
#[allow(missing_doc)]
pub trait Poly { fn fmt(&Self, &mut Formatter); }
/// Format trait for the `p` character
#[allow(missing_doc)]
pub trait Pointer { fn fmt(&Self, &mut Formatter); }
/// Format trait for the `f` character
#[allow(missing_doc)]
pub trait Float { fn fmt(&Self, &mut Formatter); }
@ -726,9 +744,9 @@ impl Bool for bool {
}
}
impl<'self> String for &'self str {
fn fmt(s: & &'self str, f: &mut Formatter) {
f.pad(*s);
impl<'self, T: str::Str> String for T {
fn fmt(s: &T, f: &mut Formatter) {
f.pad(s.as_slice());
}
}
@ -855,5 +873,37 @@ impl<T> Pointer for *const T {
}
}
// Implementation of Default for various core types
macro_rules! delegate(($ty:ty to $other:ident) => {
impl<'self> Default for $ty {
fn fmt(me: &$ty, f: &mut Formatter) {
$other::fmt(me, f)
}
}
})
delegate!(int to Signed)
delegate!( i8 to Signed)
delegate!(i16 to Signed)
delegate!(i32 to Signed)
delegate!(i64 to Signed)
delegate!(uint to Unsigned)
delegate!( u8 to Unsigned)
delegate!( u16 to Unsigned)
delegate!( u32 to Unsigned)
delegate!( u64 to Unsigned)
delegate!(@str to String)
delegate!(~str to String)
delegate!(&'self str to String)
delegate!(bool to Bool)
delegate!(char to Char)
delegate!(float to Float)
delegate!(f32 to Float)
delegate!(f64 to Float)
impl<T> Default for *const T {
fn fmt(me: &*const T, f: &mut Formatter) { Pointer::fmt(me, f) }
}
// If you expected tests to be here, look instead at the run-pass/ifmt.rs test,
// it's a lot easier than creating all of the rt::Piece structures here.

View File

@ -339,7 +339,11 @@ impl<'self> Parser<'self> {
}
}
// Finally the actual format specifier
spec.ty = self.word();
if self.consume('?') {
spec.ty = "?";
} else {
spec.ty = self.word();
}
return spec;
}

View File

@ -623,19 +623,16 @@ impl Context {
fn format_arg(&self, sp: span, arg: Either<uint, @str>,
ident: ast::ident) -> @ast::expr {
let mut ty = match arg {
let ty = match arg {
Left(i) => self.arg_types[i].unwrap(),
Right(s) => *self.name_types.get(&s)
};
// Default types to '?' if nothing else is specified.
if ty == Unknown {
ty = Known(@"?");
}
let argptr = self.ecx.expr_addr_of(sp, self.ecx.expr_ident(sp, ident));
match ty {
let fmt_trait = match ty {
Unknown => "Default",
Known(tyname) => {
let fmt_trait = match tyname.as_slice() {
match tyname.as_slice() {
"?" => "Poly",
"b" => "Bool",
"c" => "Char",
@ -653,35 +650,35 @@ impl Context {
`%s`", tyname));
"Dummy"
}
};
let format_fn = self.ecx.path_global(sp, ~[
self.ecx.ident_of("std"),
self.ecx.ident_of("fmt"),
self.ecx.ident_of(fmt_trait),
self.ecx.ident_of("fmt"),
]);
self.ecx.expr_call_global(sp, ~[
self.ecx.ident_of("std"),
self.ecx.ident_of("fmt"),
self.ecx.ident_of("argument"),
], ~[self.ecx.expr_path(format_fn), argptr])
}
}
String => {
self.ecx.expr_call_global(sp, ~[
return self.ecx.expr_call_global(sp, ~[
self.ecx.ident_of("std"),
self.ecx.ident_of("fmt"),
self.ecx.ident_of("argumentstr"),
], ~[argptr])
}
Unsigned => {
self.ecx.expr_call_global(sp, ~[
return self.ecx.expr_call_global(sp, ~[
self.ecx.ident_of("std"),
self.ecx.ident_of("fmt"),
self.ecx.ident_of("argumentuint"),
], ~[argptr])
}
Unknown => { fail!() }
}
};
let format_fn = self.ecx.path_global(sp, ~[
self.ecx.ident_of("std"),
self.ecx.ident_of("fmt"),
self.ecx.ident_of(fmt_trait),
self.ecx.ident_of("fmt"),
]);
self.ecx.expr_call_global(sp, ~[
self.ecx.ident_of("std"),
self.ecx.ident_of("fmt"),
self.ecx.ident_of("argument"),
], ~[self.ecx.expr_path(format_fn), argptr])
}
}

View File

@ -25,16 +25,36 @@ pub fn main() {
macro_rules! t(($a:expr, $b:expr) => { assert_eq!($a, $b.to_owned()) })
// Make sure there's a poly formatter that takes anything
t!(ifmt!("{}", 1), "1");
t!(ifmt!("{}", A), "{}");
t!(ifmt!("{}", ()), "()");
t!(ifmt!("{}", @(~1, "foo")), "@(~1, \"foo\")");
t!(ifmt!("{:?}", 1), "1");
t!(ifmt!("{:?}", A), "{}");
t!(ifmt!("{:?}", ()), "()");
t!(ifmt!("{:?}", @(~1, "foo")), "@(~1, \"foo\")");
// Various edge cases without formats
t!(ifmt!(""), "");
t!(ifmt!("hello"), "hello");
t!(ifmt!("hello \\{"), "hello {");
// default formatters should work
t!(ifmt!("{}", 1i), "1");
t!(ifmt!("{}", 1i8), "1");
t!(ifmt!("{}", 1i16), "1");
t!(ifmt!("{}", 1i32), "1");
t!(ifmt!("{}", 1i64), "1");
t!(ifmt!("{}", 1u), "1");
t!(ifmt!("{}", 1u8), "1");
t!(ifmt!("{}", 1u16), "1");
t!(ifmt!("{}", 1u32), "1");
t!(ifmt!("{}", 1u64), "1");
t!(ifmt!("{}", 1.0f), "1");
t!(ifmt!("{}", 1.0f32), "1");
t!(ifmt!("{}", 1.0f64), "1");
t!(ifmt!("{}", "a"), "a");
t!(ifmt!("{}", ~"a"), "a");
t!(ifmt!("{}", @"a"), "a");
t!(ifmt!("{}", false), "false");
t!(ifmt!("{}", 'a'), "a");
// At least exercise all the formats
t!(ifmt!("{:b}", true), "true");
t!(ifmt!("{:c}", '☃'), "");
@ -45,6 +65,8 @@ pub fn main() {
t!(ifmt!("{:x}", 10u), "a");
t!(ifmt!("{:X}", 10u), "A");
t!(ifmt!("{:s}", "foo"), "foo");
t!(ifmt!("{:s}", ~"foo"), "foo");
t!(ifmt!("{:s}", @"foo"), "foo");
t!(ifmt!("{:p}", 0x1234 as *int), "0x1234");
t!(ifmt!("{:p}", 0x1234 as *mut int), "0x1234");
t!(ifmt!("{:d}", A), "aloha");
@ -54,7 +76,7 @@ pub fn main() {
t!(ifmt!("{foo} {bar}", foo=0, bar=1), "0 1");
t!(ifmt!("{foo} {1} {bar} {0}", 0, 1, foo=2, bar=3), "2 1 3 0");
t!(ifmt!("{} {0:s}", "a"), "a a");
t!(ifmt!("{} {0}", "a"), "\"a\" \"a\"");
t!(ifmt!("{} {0}", "a"), "a a");
// Methods should probably work
t!(ifmt!("{0, plural, =1{a#} =2{b#} zero{c#} other{d#}}", 0u), "c0");