Settle on the format/write/print family of names

This commit is contained in:
Alex Crichton 2013-08-23 18:14:11 -07:00
parent 67512f717e
commit eb836dd61e
10 changed files with 293 additions and 253 deletions

View file

@ -12,33 +12,33 @@
# The Formatting Module # The Formatting Module
This module contains the runtime support for the `ifmt!` syntax extension. This This module contains the runtime support for the `format!` syntax extension. This
macro is implemented in the compiler to emit calls to this module in order to macro is implemented in the compiler to emit calls to this module in order to
format arguments at runtime into strings and streams. format arguments at runtime into strings and streams.
The functions contained in this module should not normally be used in everyday The functions contained in this module should not normally be used in everyday
use cases of `ifmt!`. The assumptions made by these functions are unsafe for all use cases of `format!`. The assumptions made by these functions are unsafe for all
inputs, and the compiler performs a large amount of validation on the arguments inputs, and the compiler performs a large amount of validation on the arguments
to `ifmt!` in order to ensure safety at runtime. While it is possible to call to `format!` in order to ensure safety at runtime. While it is possible to call
these functions directly, it is not recommended to do so in the general case. these functions directly, it is not recommended to do so in the general case.
## Usage ## Usage
The `ifmt!` macro is intended to be familiar to those coming from C's The `format!` macro is intended to be familiar to those coming from C's
printf/sprintf functions or Python's `str.format` function. In its current printf/fprintf functions or Python's `str.format` function. In its current
revision, the `ifmt!` macro returns a `~str` type which is the result of the revision, the `format!` macro returns a `~str` type which is the result of the
formatting. In the future it will also be able to pass in a stream to format formatting. In the future it will also be able to pass in a stream to format
arguments directly while performing minimal allocations. arguments directly while performing minimal allocations.
Some examples of the `ifmt!` extension are: Some examples of the `format!` extension are:
~~~{.rust} ~~~{.rust}
ifmt!("Hello") // => ~"Hello" format!("Hello") // => ~"Hello"
ifmt!("Hello, {:s}!", "world") // => ~"Hello, world!" format!("Hello, {:s}!", "world") // => ~"Hello, world!"
ifmt!("The number is {:d}", 1) // => ~"The number is 1" format!("The number is {:d}", 1) // => ~"The number is 1"
ifmt!("{}", ~[3, 4]) // => ~"~[3, 4]" format!("{}", ~[3, 4]) // => ~"~[3, 4]"
ifmt!("{value}", value=4) // => ~"4" format!("{value}", value=4) // => ~"4"
ifmt!("{} {}", 1, 2) // => ~"1 2" format!("{} {}", 1, 2) // => ~"1 2"
~~~ ~~~
From these, you can see that the first argument is a format string. It is From these, you can see that the first argument is a format string. It is
@ -62,7 +62,7 @@ format string, although it must always be referred to with the same type.
### Named parameters ### Named parameters
Rust itself does not have a Python-like equivalent of named parameters to a Rust itself does not have a Python-like equivalent of named parameters to a
function, but the `ifmt!` macro is a syntax extension which allows it to function, but the `format!` macro is a syntax extension which allows it to
leverage named parameters. Named parameters are listed at the end of the leverage named parameters. Named parameters are listed at the end of the
argument list and have the syntax: argument list and have the syntax:
@ -146,7 +146,7 @@ helper methods.
## Internationalization ## Internationalization
The formatting syntax supported by the `ifmt!` extension supports The formatting syntax supported by the `format!` extension supports
internationalization by providing "methods" which execute various different internationalization by providing "methods" which execute various different
outputs depending on the input. The syntax and methods provided are similar to outputs depending on the input. The syntax and methods provided are similar to
other internationalization systems, so again nothing should seem alien. other internationalization systems, so again nothing should seem alien.
@ -164,7 +164,7 @@ to reference the string value of the argument which was selected upon. As an
example: example:
~~~ ~~~
ifmt!("{0, select, other{#}}", "hello") // => ~"hello" format!("{0, select, other{#}}", "hello") // => ~"hello"
~~~ ~~~
This example is the equivalent of `{0:s}` essentially. This example is the equivalent of `{0:s}` essentially.
@ -399,11 +399,11 @@ pub trait Pointer { fn fmt(&Self, &mut Formatter); }
#[allow(missing_doc)] #[allow(missing_doc)]
pub trait Float { fn fmt(&Self, &mut Formatter); } pub trait Float { fn fmt(&Self, &mut Formatter); }
/// The fprintf function takes an output stream, a precompiled format string, /// The `write` function takes an output stream, a precompiled format string,
/// and a list of arguments. The arguments will be formatted according to the /// and a list of arguments. The arguments will be formatted according to the
/// specified format string into the output stream provided. /// specified format string into the output stream provided.
/// ///
/// See the documentation for `sprintf` for why this function is unsafe and care /// See the documentation for `format` for why this function is unsafe and care
/// should be taken if calling it manually. /// should be taken if calling it manually.
/// ///
/// Thankfully the rust compiler provides the macro `fmtf!` which will perform /// Thankfully the rust compiler provides the macro `fmtf!` which will perform
@ -419,7 +419,7 @@ pub trait Float { fn fmt(&Self, &mut Formatter); }
/// ///
/// Note that this function assumes that there are enough arguments for the /// Note that this function assumes that there are enough arguments for the
/// format string. /// format string.
pub unsafe fn fprintf(output: &mut io::Writer, pub unsafe fn write(output: &mut io::Writer,
fmt: &[rt::Piece], args: &[Argument]) { fmt: &[rt::Piece], args: &[Argument]) {
let mut formatter = Formatter { let mut formatter = Formatter {
flags: 0, flags: 0,
@ -436,7 +436,7 @@ pub unsafe fn fprintf(output: &mut io::Writer,
} }
} }
/// The sprintf function takes a precompiled format string and a list of /// The format function takes a precompiled format string and a list of
/// arguments, to return the resulting formatted string. /// arguments, to return the resulting formatted string.
/// ///
/// This is currently an unsafe function because the types of all arguments /// This is currently an unsafe function because the types of all arguments
@ -446,7 +446,7 @@ pub unsafe fn fprintf(output: &mut io::Writer,
/// for formatting the right type value. Because of this, the function is marked /// for formatting the right type value. Because of this, the function is marked
/// as `unsafe` if this is being called manually. /// as `unsafe` if this is being called manually.
/// ///
/// Thankfully the rust compiler provides the macro `ifmt!` which will perform /// Thankfully the rust compiler provides the macro `format!` which will perform
/// all of this validation at compile-time and provides a safe interface for /// all of this validation at compile-time and provides a safe interface for
/// invoking this function. /// invoking this function.
/// ///
@ -458,9 +458,9 @@ pub unsafe fn fprintf(output: &mut io::Writer,
/// ///
/// Note that this function assumes that there are enough arguments for the /// Note that this function assumes that there are enough arguments for the
/// format string. /// format string.
pub unsafe fn sprintf(fmt: &[rt::Piece], args: &[Argument]) -> ~str { pub unsafe fn format(fmt: &[rt::Piece], args: &[Argument]) -> ~str {
let mut output = MemWriter::new(); let mut output = MemWriter::new();
fprintf(&mut output as &mut io::Writer, fmt, args); write(&mut output as &mut io::Writer, fmt, args);
return str::from_bytes_owned(output.inner()); return str::from_bytes_owned(output.inner());
} }
@ -468,7 +468,7 @@ impl<'self> Formatter<'self> {
// First up is the collection of functions used to execute a format string // First up is the collection of functions used to execute a format string
// at runtime. This consumes all of the compile-time statics generated by // at runtime. This consumes all of the compile-time statics generated by
// the ifmt! syntax extension. // the format! syntax extension.
fn run(&mut self, piece: &rt::Piece, cur: Option<&str>) { fn run(&mut self, piece: &rt::Piece, cur: Option<&str>) {
let setcount = |slot: &mut Option<uint>, cnt: &parse::Count| { let setcount = |slot: &mut Option<uint>, cnt: &parse::Count| {
@ -732,7 +732,7 @@ impl<'self> Formatter<'self> {
} }
/// This is a function which calls are emitted to by the compiler itself to /// This is a function which calls are emitted to by the compiler itself to
/// create the Argument structures that are passed into the `sprintf` function. /// create the Argument structures that are passed into the `format` function.
#[doc(hidden)] #[doc(hidden)]
pub fn argument<'a, T>(f: extern "Rust" fn(&T, &mut Formatter), pub fn argument<'a, T>(f: extern "Rust" fn(&T, &mut Formatter),
t: &'a T) -> Argument<'a> { t: &'a T) -> Argument<'a> {

View file

@ -139,10 +139,12 @@ pub fn syntax_expander_table() -> SyntaxEnv {
ext::tt::macro_rules::add_new_extension)); ext::tt::macro_rules::add_new_extension));
syntax_expanders.insert(intern(&"fmt"), syntax_expanders.insert(intern(&"fmt"),
builtin_normal_tt(ext::fmt::expand_syntax_ext)); builtin_normal_tt(ext::fmt::expand_syntax_ext));
syntax_expanders.insert(intern(&"ifmt"), syntax_expanders.insert(intern(&"format"),
builtin_normal_tt(ext::ifmt::expand_sprintf)); builtin_normal_tt(ext::ifmt::expand_format));
syntax_expanders.insert(intern(&"ifmtf"), syntax_expanders.insert(intern(&"write"),
builtin_normal_tt(ext::ifmt::expand_fprintf)); builtin_normal_tt(ext::ifmt::expand_write));
syntax_expanders.insert(intern(&"writeln"),
builtin_normal_tt(ext::ifmt::expand_writeln));
syntax_expanders.insert( syntax_expanders.insert(
intern(&"auto_encode"), intern(&"auto_encode"),
@SE(ItemDecorator(ext::auto_encode::expand_auto_encode))); @SE(ItemDecorator(ext::auto_encode::expand_auto_encode)));

View file

@ -940,6 +940,7 @@ pub fn std_macros() -> @str {
); );
) )
// NOTE(acrichto): start removing this after the next snapshot
macro_rules! printf ( macro_rules! printf (
($arg:expr) => ( ($arg:expr) => (
print(fmt!(\"%?\", $arg)) print(fmt!(\"%?\", $arg))
@ -949,6 +950,7 @@ pub fn std_macros() -> @str {
) )
) )
// NOTE(acrichto): start removing this after the next snapshot
macro_rules! printfln ( macro_rules! printfln (
($arg:expr) => ( ($arg:expr) => (
println(fmt!(\"%?\", $arg)) println(fmt!(\"%?\", $arg))
@ -958,6 +960,21 @@ pub fn std_macros() -> @str {
) )
) )
// FIXME(#6846) once stdio is redesigned, this shouldn't perform an
// allocation but should rather delegate to an invocation of
// write! instead of format!
macro_rules! print (
() => ();
($arg:expr) => ( ::std::io::print(format!(\"{}\", $arg)));
($fmt:expr, $($arg:tt)+) => ( ::std::io::print(format!($fmt, $($arg)+)))
)
// FIXME(#6846) once stdio is redesigned, this shouldn't perform an
// allocation but should rather delegate to an io::Writer
macro_rules! println (
($($arg:tt)*) => ({ print!($($arg)*); ::std::io::println(\"\"); })
)
// NOTE: use this after a snapshot lands to abstract the details // NOTE: use this after a snapshot lands to abstract the details
// of the TLS interface. // of the TLS interface.
macro_rules! local_data_key ( macro_rules! local_data_key (

View file

@ -697,19 +697,24 @@ impl Context {
} }
} }
pub fn expand_sprintf(ecx: @ExtCtxt, sp: span, pub fn expand_format(ecx: @ExtCtxt, sp: span,
tts: &[ast::token_tree]) -> base::MacResult { tts: &[ast::token_tree]) -> base::MacResult {
expand_ifmt(ecx, sp, tts, false, "sprintf") expand_ifmt(ecx, sp, tts, false, false, "format")
} }
pub fn expand_fprintf(ecx: @ExtCtxt, sp: span, pub fn expand_write(ecx: @ExtCtxt, sp: span,
tts: &[ast::token_tree]) -> base::MacResult { tts: &[ast::token_tree]) -> base::MacResult {
expand_ifmt(ecx, sp, tts, true, "fprintf") expand_ifmt(ecx, sp, tts, true, false, "write")
} }
pub fn expand_writeln(ecx: @ExtCtxt, sp: span,
tts: &[ast::token_tree]) -> base::MacResult {
expand_ifmt(ecx, sp, tts, true, true, "write")
}
fn expand_ifmt(ecx: @ExtCtxt, sp: span, tts: &[ast::token_tree], fn expand_ifmt(ecx: @ExtCtxt, sp: span, tts: &[ast::token_tree],
leading_arg: bool, function: &str) -> base::MacResult { leading_arg: bool, append_newline: bool,
function: &str) -> base::MacResult {
let mut cx = Context { let mut cx = Context {
ecx: ecx, ecx: ecx,
args: ~[], args: ~[],
@ -730,6 +735,7 @@ fn expand_ifmt(ecx: @ExtCtxt, sp: span, tts: &[ast::token_tree],
cx.fmtsp = efmt.span; cx.fmtsp = efmt.span;
let fmt = expr_to_str(ecx, efmt, let fmt = expr_to_str(ecx, efmt,
"format argument must be a string literal."); "format argument must be a string literal.");
let fmt = if append_newline { fmt + "\n" } else { fmt.to_owned() };
let mut err = false; let mut err = false;
do parse::parse_error::cond.trap(|m| { do parse::parse_error::cond.trap(|m| {

View file

@ -9,58 +9,58 @@
// except according to those terms. // except according to those terms.
fn main() { fn main() {
// bad arguments to the ifmt! call // bad arguments to the format! call
ifmt!(); //~ ERROR: expects at least one format!(); //~ ERROR: requires at least a format string
ifmt!("{}"); //~ ERROR: invalid reference to argument format!("{}"); //~ ERROR: invalid reference to argument
ifmt!("{1}", 1); //~ ERROR: invalid reference to argument `1` format!("{1}", 1); //~ ERROR: invalid reference to argument `1`
//~^ ERROR: argument never used //~^ ERROR: argument never used
ifmt!("{foo}"); //~ ERROR: no argument named `foo` format!("{foo}"); //~ ERROR: no argument named `foo`
ifmt!("{}", 1, 2); //~ ERROR: argument never used format!("{}", 1, 2); //~ ERROR: argument never used
ifmt!("{1}", 1, 2); //~ ERROR: argument never used format!("{1}", 1, 2); //~ ERROR: argument never used
ifmt!("{}", 1, foo=2); //~ ERROR: named argument never used format!("{}", 1, foo=2); //~ ERROR: named argument never used
ifmt!("{foo}", 1, foo=2); //~ ERROR: argument never used format!("{foo}", 1, foo=2); //~ ERROR: argument never used
ifmt!("", foo=2); //~ ERROR: named argument never used format!("", foo=2); //~ ERROR: named argument never used
ifmt!("{0:d} {0:s}", 1); //~ ERROR: redeclared with type `s` format!("{0:d} {0:s}", 1); //~ ERROR: redeclared with type `s`
ifmt!("{foo:d} {foo:s}", foo=1); //~ ERROR: redeclared with type `s` format!("{foo:d} {foo:s}", foo=1); //~ ERROR: redeclared with type `s`
ifmt!("{foo}", foo=1, foo=2); //~ ERROR: duplicate argument format!("{foo}", foo=1, foo=2); //~ ERROR: duplicate argument
ifmt!("#"); //~ ERROR: `#` reference used format!("#"); //~ ERROR: `#` reference used
ifmt!("", foo=1, 2); //~ ERROR: positional arguments cannot follow format!("", foo=1, 2); //~ ERROR: positional arguments cannot follow
ifmt!("" 1); //~ ERROR: expected token: `,` format!("" 1); //~ ERROR: expected token: `,`
ifmt!("", 1 1); //~ ERROR: expected token: `,` format!("", 1 1); //~ ERROR: expected token: `,`
ifmt!("{0, select, a{} a{} other{}}", "a"); //~ ERROR: duplicate selector format!("{0, select, a{} a{} other{}}", "a"); //~ ERROR: duplicate selector
ifmt!("{0, plural, =1{} =1{} other{}}", 1u); //~ ERROR: duplicate selector format!("{0, plural, =1{} =1{} other{}}", 1u); //~ ERROR: duplicate selector
ifmt!("{0, plural, one{} one{} other{}}", 1u); //~ ERROR: duplicate selector format!("{0, plural, one{} one{} other{}}", 1u); //~ ERROR: duplicate selector
// bad syntax of the format string // bad syntax of the format string
ifmt!("{"); //~ ERROR: unterminated format string format!("{"); //~ ERROR: unterminated format string
ifmt!("\\ "); //~ ERROR: invalid escape format!("\\ "); //~ ERROR: invalid escape
ifmt!("\\"); //~ ERROR: expected an escape format!("\\"); //~ ERROR: expected an escape
ifmt!("{0, }", 1); //~ ERROR: expected method format!("{0, }", 1); //~ ERROR: expected method
ifmt!("{0, foo}", 1); //~ ERROR: unknown method format!("{0, foo}", 1); //~ ERROR: unknown method
ifmt!("{0, select}", "a"); //~ ERROR: must be followed by format!("{0, select}", "a"); //~ ERROR: must be followed by
ifmt!("{0, plural}", 1); //~ ERROR: must be followed by format!("{0, plural}", 1); //~ ERROR: must be followed by
ifmt!("{0, select, a{{}", 1); //~ ERROR: must be terminated format!("{0, select, a{{}", 1); //~ ERROR: must be terminated
ifmt!("{0, select, {} other{}}", "a"); //~ ERROR: empty selector format!("{0, select, {} other{}}", "a"); //~ ERROR: empty selector
ifmt!("{0, select, other{} other{}}", "a"); //~ ERROR: multiple `other` format!("{0, select, other{} other{}}", "a"); //~ ERROR: multiple `other`
ifmt!("{0, plural, offset: other{}}", "a"); //~ ERROR: must be an integer format!("{0, plural, offset: other{}}", "a"); //~ ERROR: must be an integer
ifmt!("{0, plural, offset 1 other{}}", "a"); //~ ERROR: be followed by `:` format!("{0, plural, offset 1 other{}}", "a"); //~ ERROR: be followed by `:`
ifmt!("{0, plural, =a{} other{}}", "a"); //~ ERROR: followed by an integer format!("{0, plural, =a{} other{}}", "a"); //~ ERROR: followed by an integer
ifmt!("{0, plural, a{} other{}}", "a"); //~ ERROR: unexpected plural format!("{0, plural, a{} other{}}", "a"); //~ ERROR: unexpected plural
ifmt!("{0, select, a{}}", "a"); //~ ERROR: must provide an `other` format!("{0, select, a{}}", "a"); //~ ERROR: must provide an `other`
ifmt!("{0, plural, =1{}}", "a"); //~ ERROR: must provide an `other` format!("{0, plural, =1{}}", "a"); //~ ERROR: must provide an `other`
ifmt!("{0, plural, other{{0:s}}}", "a"); //~ ERROR: previously used as format!("{0, plural, other{{0:s}}}", "a"); //~ ERROR: previously used as
ifmt!("{:s} {0, plural, other{}}", "a"); //~ ERROR: argument used to format!("{:s} {0, plural, other{}}", "a"); //~ ERROR: argument used to
ifmt!("{0, select, other{}} \ format!("{0, select, other{}} \
{0, plural, other{}}", "a"); {0, plural, other{}}", "a");
//~^ ERROR: declared with multiple formats //~^ ERROR: declared with multiple formats
@ -68,7 +68,7 @@ fn main() {
// format strings because otherwise the "internal pointer of which argument // format strings because otherwise the "internal pointer of which argument
// is next" would be invalidated if different cases had different numbers of // is next" would be invalidated if different cases had different numbers of
// arguments. // arguments.
ifmt!("{0, select, other{{}}}", "a"); //~ ERROR: cannot use implicit format!("{0, select, other{{}}}", "a"); //~ ERROR: cannot use implicit
ifmt!("{0, plural, other{{}}}", 1); //~ ERROR: cannot use implicit format!("{0, plural, other{{}}}", 1); //~ ERROR: cannot use implicit
ifmt!("{0, plural, other{{1:.*d}}}", 1, 2); //~ ERROR: cannot use implicit format!("{0, plural, other{{1:.*d}}}", 1, 2); //~ ERROR: cannot use implicit
} }

View file

@ -9,6 +9,6 @@
// except according to those terms. // except according to those terms.
fn main() { fn main() {
ifmt!("{0, plural, other{}}", "a"); format!("{0, plural, other{}}", "a");
//~^ ERROR: expected uint but found //~^ ERROR: expected uint but found
} }

View file

@ -9,6 +9,6 @@
// except according to those terms. // except according to those terms.
fn main() { fn main() {
ifmt!("{0, select, other{}}", 2); format!("{0, select, other{}}", 2);
//~^ ERROR: expected &str but found integral //~^ ERROR: expected &str but found integral
} }

View file

@ -9,6 +9,6 @@
// except according to those terms. // except according to those terms.
fn main() { fn main() {
ifmt!("{:d}", "3"); format!("{:d}", "3");
//~^ ERROR: failed to find an implementation of trait std::fmt::Signed //~^ ERROR: failed to find an implementation of trait std::fmt::Signed
} }

View file

@ -9,6 +9,6 @@
// except according to those terms. // except according to those terms.
fn main() { fn main() {
ifmt!("{:notimplemented}", "3"); format!("{:notimplemented}", "3");
//~^ ERROR: unknown format trait `notimplemented` //~^ ERROR: unknown format trait `notimplemented`
} }

View file

@ -26,208 +26,223 @@ macro_rules! t(($a:expr, $b:expr) => { assert_eq!($a, $b.to_owned()) })
pub fn main() { pub fn main() {
// Make sure there's a poly formatter that takes anything // Make sure there's a poly formatter that takes anything
t!(ifmt!("{:?}", 1), "1"); t!(format!("{:?}", 1), "1");
t!(ifmt!("{:?}", A), "{}"); t!(format!("{:?}", A), "{}");
t!(ifmt!("{:?}", ()), "()"); t!(format!("{:?}", ()), "()");
t!(ifmt!("{:?}", @(~1, "foo")), "@(~1, \"foo\")"); t!(format!("{:?}", @(~1, "foo")), "@(~1, \"foo\")");
// Various edge cases without formats // Various edge cases without formats
t!(ifmt!(""), ""); t!(format!(""), "");
t!(ifmt!("hello"), "hello"); t!(format!("hello"), "hello");
t!(ifmt!("hello \\{"), "hello {"); t!(format!("hello \\{"), "hello {");
// default formatters should work // default formatters should work
t!(ifmt!("{}", 1i), "1"); t!(format!("{}", 1i), "1");
t!(ifmt!("{}", 1i8), "1"); t!(format!("{}", 1i8), "1");
t!(ifmt!("{}", 1i16), "1"); t!(format!("{}", 1i16), "1");
t!(ifmt!("{}", 1i32), "1"); t!(format!("{}", 1i32), "1");
t!(ifmt!("{}", 1i64), "1"); t!(format!("{}", 1i64), "1");
t!(ifmt!("{}", 1u), "1"); t!(format!("{}", 1u), "1");
t!(ifmt!("{}", 1u8), "1"); t!(format!("{}", 1u8), "1");
t!(ifmt!("{}", 1u16), "1"); t!(format!("{}", 1u16), "1");
t!(ifmt!("{}", 1u32), "1"); t!(format!("{}", 1u32), "1");
t!(ifmt!("{}", 1u64), "1"); t!(format!("{}", 1u64), "1");
t!(ifmt!("{}", 1.0f), "1"); t!(format!("{}", 1.0f), "1");
t!(ifmt!("{}", 1.0f32), "1"); t!(format!("{}", 1.0f32), "1");
t!(ifmt!("{}", 1.0f64), "1"); t!(format!("{}", 1.0f64), "1");
t!(ifmt!("{}", "a"), "a"); t!(format!("{}", "a"), "a");
t!(ifmt!("{}", ~"a"), "a"); t!(format!("{}", ~"a"), "a");
t!(ifmt!("{}", @"a"), "a"); t!(format!("{}", @"a"), "a");
t!(ifmt!("{}", false), "false"); t!(format!("{}", false), "false");
t!(ifmt!("{}", 'a'), "a"); t!(format!("{}", 'a'), "a");
// At least exercise all the formats // At least exercise all the formats
t!(ifmt!("{:b}", true), "true"); t!(format!("{:b}", true), "true");
t!(ifmt!("{:c}", '☃'), ""); t!(format!("{:c}", '☃'), "");
t!(ifmt!("{:d}", 10), "10"); t!(format!("{:d}", 10), "10");
t!(ifmt!("{:i}", 10), "10"); t!(format!("{:i}", 10), "10");
t!(ifmt!("{:u}", 10u), "10"); t!(format!("{:u}", 10u), "10");
t!(ifmt!("{:o}", 10u), "12"); t!(format!("{:o}", 10u), "12");
t!(ifmt!("{:x}", 10u), "a"); t!(format!("{:x}", 10u), "a");
t!(ifmt!("{:X}", 10u), "A"); t!(format!("{:X}", 10u), "A");
t!(ifmt!("{:s}", "foo"), "foo"); t!(format!("{:s}", "foo"), "foo");
t!(ifmt!("{:s}", ~"foo"), "foo"); t!(format!("{:s}", ~"foo"), "foo");
t!(ifmt!("{:s}", @"foo"), "foo"); t!(format!("{:s}", @"foo"), "foo");
t!(ifmt!("{:p}", 0x1234 as *int), "0x1234"); t!(format!("{:p}", 0x1234 as *int), "0x1234");
t!(ifmt!("{:p}", 0x1234 as *mut int), "0x1234"); t!(format!("{:p}", 0x1234 as *mut int), "0x1234");
t!(ifmt!("{:d}", A), "aloha"); t!(format!("{:d}", A), "aloha");
t!(ifmt!("{:d}", B), "adios"); t!(format!("{:d}", B), "adios");
t!(ifmt!("foo {:s} ☃☃☃☃☃☃", "bar"), "foo bar ☃☃☃☃☃☃"); t!(format!("foo {:s} ☃☃☃☃☃☃", "bar"), "foo bar ☃☃☃☃☃☃");
t!(ifmt!("{1} {0}", 0, 1), "1 0"); t!(format!("{1} {0}", 0, 1), "1 0");
t!(ifmt!("{foo} {bar}", foo=0, bar=1), "0 1"); t!(format!("{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!(format!("{foo} {1} {bar} {0}", 0, 1, foo=2, bar=3), "2 1 3 0");
t!(ifmt!("{} {0:s}", "a"), "a a"); t!(format!("{} {0:s}", "a"), "a a");
t!(ifmt!("{} {0}", "a"), "a a"); t!(format!("{} {0}", "a"), "a a");
// Methods should probably work // Methods should probably work
t!(ifmt!("{0, plural, =1{a#} =2{b#} zero{c#} other{d#}}", 0u), "c0"); t!(format!("{0, plural, =1{a#} =2{b#} zero{c#} other{d#}}", 0u), "c0");
t!(ifmt!("{0, plural, =1{a#} =2{b#} zero{c#} other{d#}}", 1u), "a1"); t!(format!("{0, plural, =1{a#} =2{b#} zero{c#} other{d#}}", 1u), "a1");
t!(ifmt!("{0, plural, =1{a#} =2{b#} zero{c#} other{d#}}", 2u), "b2"); t!(format!("{0, plural, =1{a#} =2{b#} zero{c#} other{d#}}", 2u), "b2");
t!(ifmt!("{0, plural, =1{a#} =2{b#} zero{c#} other{d#}}", 3u), "d3"); t!(format!("{0, plural, =1{a#} =2{b#} zero{c#} other{d#}}", 3u), "d3");
t!(ifmt!("{0, select, a{a#} b{b#} c{c#} other{d#}}", "a"), "aa"); t!(format!("{0, select, a{a#} b{b#} c{c#} other{d#}}", "a"), "aa");
t!(ifmt!("{0, select, a{a#} b{b#} c{c#} other{d#}}", "b"), "bb"); t!(format!("{0, select, a{a#} b{b#} c{c#} other{d#}}", "b"), "bb");
t!(ifmt!("{0, select, a{a#} b{b#} c{c#} other{d#}}", "c"), "cc"); t!(format!("{0, select, a{a#} b{b#} c{c#} other{d#}}", "c"), "cc");
t!(ifmt!("{0, select, a{a#} b{b#} c{c#} other{d#}}", "d"), "dd"); t!(format!("{0, select, a{a#} b{b#} c{c#} other{d#}}", "d"), "dd");
t!(ifmt!("{1, select, a{#{0:s}} other{#{1}}}", "b", "a"), "ab"); t!(format!("{1, select, a{#{0:s}} other{#{1}}}", "b", "a"), "ab");
t!(ifmt!("{1, select, a{#{0}} other{#{1}}}", "c", "b"), "bb"); t!(format!("{1, select, a{#{0}} other{#{1}}}", "c", "b"), "bb");
// Formatting strings and their arguments // Formatting strings and their arguments
t!(ifmt!("{:s}", "a"), "a"); t!(format!("{:s}", "a"), "a");
t!(ifmt!("{:4s}", "a"), "a "); t!(format!("{:4s}", "a"), "a ");
t!(ifmt!("{:>4s}", "a"), " a"); t!(format!("{:>4s}", "a"), " a");
t!(ifmt!("{:<4s}", "a"), "a "); t!(format!("{:<4s}", "a"), "a ");
t!(ifmt!("{:.4s}", "a"), "a"); t!(format!("{:.4s}", "a"), "a");
t!(ifmt!("{:4.4s}", "a"), "a "); t!(format!("{:4.4s}", "a"), "a ");
t!(ifmt!("{:4.4s}", "aaaaaaaaaaaaaaaaaa"), "aaaa"); t!(format!("{:4.4s}", "aaaaaaaaaaaaaaaaaa"), "aaaa");
t!(ifmt!("{:<4.4s}", "aaaaaaaaaaaaaaaaaa"), "aaaa"); t!(format!("{:<4.4s}", "aaaaaaaaaaaaaaaaaa"), "aaaa");
t!(ifmt!("{:>4.4s}", "aaaaaaaaaaaaaaaaaa"), "aaaa"); t!(format!("{:>4.4s}", "aaaaaaaaaaaaaaaaaa"), "aaaa");
t!(ifmt!("{:>10.4s}", "aaaaaaaaaaaaaaaaaa"), "aaaa"); t!(format!("{:>10.4s}", "aaaaaaaaaaaaaaaaaa"), "aaaa");
t!(ifmt!("{:2.4s}", "aaaaa"), "aaaa"); t!(format!("{:2.4s}", "aaaaa"), "aaaa");
t!(ifmt!("{:2.4s}", "aaaa"), "aaaa"); t!(format!("{:2.4s}", "aaaa"), "aaaa");
t!(ifmt!("{:2.4s}", "aaa"), "aaa"); t!(format!("{:2.4s}", "aaa"), "aaa");
t!(ifmt!("{:2.4s}", "aa"), "aa"); t!(format!("{:2.4s}", "aa"), "aa");
t!(ifmt!("{:2.4s}", "a"), "a "); t!(format!("{:2.4s}", "a"), "a ");
t!(ifmt!("{:0>2s}", "a"), "0a"); t!(format!("{:0>2s}", "a"), "0a");
t!(ifmt!("{:.*s}", 4, "aaaaaaaaaaaaaaaaaa"), "aaaa"); t!(format!("{:.*s}", 4, "aaaaaaaaaaaaaaaaaa"), "aaaa");
t!(ifmt!("{:.1$s}", "aaaaaaaaaaaaaaaaaa", 4), "aaaa"); t!(format!("{:.1$s}", "aaaaaaaaaaaaaaaaaa", 4), "aaaa");
t!(ifmt!("{:1$s}", "a", 4), "a "); t!(format!("{:1$s}", "a", 4), "a ");
t!(ifmt!("{:-#s}", "a"), "a"); t!(format!("{:-#s}", "a"), "a");
t!(ifmt!("{:+#s}", "a"), "a"); t!(format!("{:+#s}", "a"), "a");
// Formatting integers should select the right implementation based off the // Formatting integers should select the right implementation based off the
// type of the argument. Also, hex/octal/binary should be defined for // type of the argument. Also, hex/octal/binary should be defined for
// integers, but they shouldn't emit the negative sign. // integers, but they shouldn't emit the negative sign.
t!(ifmt!("{:d}", -1i), "-1"); t!(format!("{:d}", -1i), "-1");
t!(ifmt!("{:d}", -1i8), "-1"); t!(format!("{:d}", -1i8), "-1");
t!(ifmt!("{:d}", -1i16), "-1"); t!(format!("{:d}", -1i16), "-1");
t!(ifmt!("{:d}", -1i32), "-1"); t!(format!("{:d}", -1i32), "-1");
t!(ifmt!("{:d}", -1i64), "-1"); t!(format!("{:d}", -1i64), "-1");
t!(ifmt!("{:t}", 1i), "1"); t!(format!("{:t}", 1i), "1");
t!(ifmt!("{:t}", 1i8), "1"); t!(format!("{:t}", 1i8), "1");
t!(ifmt!("{:t}", 1i16), "1"); t!(format!("{:t}", 1i16), "1");
t!(ifmt!("{:t}", 1i32), "1"); t!(format!("{:t}", 1i32), "1");
t!(ifmt!("{:t}", 1i64), "1"); t!(format!("{:t}", 1i64), "1");
t!(ifmt!("{:x}", 1i), "1"); t!(format!("{:x}", 1i), "1");
t!(ifmt!("{:x}", 1i8), "1"); t!(format!("{:x}", 1i8), "1");
t!(ifmt!("{:x}", 1i16), "1"); t!(format!("{:x}", 1i16), "1");
t!(ifmt!("{:x}", 1i32), "1"); t!(format!("{:x}", 1i32), "1");
t!(ifmt!("{:x}", 1i64), "1"); t!(format!("{:x}", 1i64), "1");
t!(ifmt!("{:X}", 1i), "1"); t!(format!("{:X}", 1i), "1");
t!(ifmt!("{:X}", 1i8), "1"); t!(format!("{:X}", 1i8), "1");
t!(ifmt!("{:X}", 1i16), "1"); t!(format!("{:X}", 1i16), "1");
t!(ifmt!("{:X}", 1i32), "1"); t!(format!("{:X}", 1i32), "1");
t!(ifmt!("{:X}", 1i64), "1"); t!(format!("{:X}", 1i64), "1");
t!(ifmt!("{:o}", 1i), "1"); t!(format!("{:o}", 1i), "1");
t!(ifmt!("{:o}", 1i8), "1"); t!(format!("{:o}", 1i8), "1");
t!(ifmt!("{:o}", 1i16), "1"); t!(format!("{:o}", 1i16), "1");
t!(ifmt!("{:o}", 1i32), "1"); t!(format!("{:o}", 1i32), "1");
t!(ifmt!("{:o}", 1i64), "1"); t!(format!("{:o}", 1i64), "1");
t!(ifmt!("{:u}", 1u), "1"); t!(format!("{:u}", 1u), "1");
t!(ifmt!("{:u}", 1u8), "1"); t!(format!("{:u}", 1u8), "1");
t!(ifmt!("{:u}", 1u16), "1"); t!(format!("{:u}", 1u16), "1");
t!(ifmt!("{:u}", 1u32), "1"); t!(format!("{:u}", 1u32), "1");
t!(ifmt!("{:u}", 1u64), "1"); t!(format!("{:u}", 1u64), "1");
t!(ifmt!("{:t}", 1u), "1"); t!(format!("{:t}", 1u), "1");
t!(ifmt!("{:t}", 1u8), "1"); t!(format!("{:t}", 1u8), "1");
t!(ifmt!("{:t}", 1u16), "1"); t!(format!("{:t}", 1u16), "1");
t!(ifmt!("{:t}", 1u32), "1"); t!(format!("{:t}", 1u32), "1");
t!(ifmt!("{:t}", 1u64), "1"); t!(format!("{:t}", 1u64), "1");
t!(ifmt!("{:x}", 1u), "1"); t!(format!("{:x}", 1u), "1");
t!(ifmt!("{:x}", 1u8), "1"); t!(format!("{:x}", 1u8), "1");
t!(ifmt!("{:x}", 1u16), "1"); t!(format!("{:x}", 1u16), "1");
t!(ifmt!("{:x}", 1u32), "1"); t!(format!("{:x}", 1u32), "1");
t!(ifmt!("{:x}", 1u64), "1"); t!(format!("{:x}", 1u64), "1");
t!(ifmt!("{:X}", 1u), "1"); t!(format!("{:X}", 1u), "1");
t!(ifmt!("{:X}", 1u8), "1"); t!(format!("{:X}", 1u8), "1");
t!(ifmt!("{:X}", 1u16), "1"); t!(format!("{:X}", 1u16), "1");
t!(ifmt!("{:X}", 1u32), "1"); t!(format!("{:X}", 1u32), "1");
t!(ifmt!("{:X}", 1u64), "1"); t!(format!("{:X}", 1u64), "1");
t!(ifmt!("{:o}", 1u), "1"); t!(format!("{:o}", 1u), "1");
t!(ifmt!("{:o}", 1u8), "1"); t!(format!("{:o}", 1u8), "1");
t!(ifmt!("{:o}", 1u16), "1"); t!(format!("{:o}", 1u16), "1");
t!(ifmt!("{:o}", 1u32), "1"); t!(format!("{:o}", 1u32), "1");
t!(ifmt!("{:o}", 1u64), "1"); t!(format!("{:o}", 1u64), "1");
// Test the flags for formatting integers // Test the flags for formatting integers
t!(ifmt!("{:3d}", 1), " 1"); t!(format!("{:3d}", 1), " 1");
t!(ifmt!("{:>3d}", 1), " 1"); t!(format!("{:>3d}", 1), " 1");
t!(ifmt!("{:>+3d}", 1), " +1"); t!(format!("{:>+3d}", 1), " +1");
t!(ifmt!("{:<3d}", 1), "1 "); t!(format!("{:<3d}", 1), "1 ");
t!(ifmt!("{:#d}", 1), "1"); t!(format!("{:#d}", 1), "1");
t!(ifmt!("{:#x}", 10), "0xa"); t!(format!("{:#x}", 10), "0xa");
t!(ifmt!("{:#X}", 10), "0xA"); t!(format!("{:#X}", 10), "0xA");
t!(ifmt!("{:#5x}", 10), " 0xa"); t!(format!("{:#5x}", 10), " 0xa");
t!(ifmt!("{:#o}", 10), "0o12"); t!(format!("{:#o}", 10), "0o12");
t!(ifmt!("{:08x}", 10), "0000000a"); t!(format!("{:08x}", 10), "0000000a");
t!(ifmt!("{:8x}", 10), " a"); t!(format!("{:8x}", 10), " a");
t!(ifmt!("{:<8x}", 10), "a "); t!(format!("{:<8x}", 10), "a ");
t!(ifmt!("{:>8x}", 10), " a"); t!(format!("{:>8x}", 10), " a");
t!(ifmt!("{:#08x}", 10), "0x00000a"); t!(format!("{:#08x}", 10), "0x00000a");
t!(ifmt!("{:08d}", -10), "-0000010"); t!(format!("{:08d}", -10), "-0000010");
t!(ifmt!("{:x}", -1u8), "ff"); t!(format!("{:x}", -1u8), "ff");
t!(ifmt!("{:X}", -1u8), "FF"); t!(format!("{:X}", -1u8), "FF");
t!(ifmt!("{:t}", -1u8), "11111111"); t!(format!("{:t}", -1u8), "11111111");
t!(ifmt!("{:o}", -1u8), "377"); t!(format!("{:o}", -1u8), "377");
t!(ifmt!("{:#x}", -1u8), "0xff"); t!(format!("{:#x}", -1u8), "0xff");
t!(ifmt!("{:#X}", -1u8), "0xFF"); t!(format!("{:#X}", -1u8), "0xFF");
t!(ifmt!("{:#t}", -1u8), "0b11111111"); t!(format!("{:#t}", -1u8), "0b11111111");
t!(ifmt!("{:#o}", -1u8), "0o377"); t!(format!("{:#o}", -1u8), "0o377");
// Signed combinations // Signed combinations
t!(ifmt!("{:+5d}", 1), " +1"); t!(format!("{:+5d}", 1), " +1");
t!(ifmt!("{:+5d}", -1), " -1"); t!(format!("{:+5d}", -1), " -1");
t!(ifmt!("{:05d}", 1), "00001"); t!(format!("{:05d}", 1), "00001");
t!(ifmt!("{:05d}", -1), "-0001"); t!(format!("{:05d}", -1), "-0001");
t!(ifmt!("{:+05d}", 1), "+0001"); t!(format!("{:+05d}", 1), "+0001");
t!(ifmt!("{:+05d}", -1), "-0001"); t!(format!("{:+05d}", -1), "-0001");
// Some float stuff // Some float stuff
t!(ifmt!("{:f}", 1.0f), "1"); t!(format!("{:f}", 1.0f), "1");
t!(ifmt!("{:f}", 1.0f32), "1"); t!(format!("{:f}", 1.0f32), "1");
t!(ifmt!("{:f}", 1.0f64), "1"); t!(format!("{:f}", 1.0f64), "1");
t!(ifmt!("{:.3f}", 1.0f), "1.000"); t!(format!("{:.3f}", 1.0f), "1.000");
t!(ifmt!("{:10.3f}", 1.0f), " 1.000"); t!(format!("{:10.3f}", 1.0f), " 1.000");
t!(ifmt!("{:+10.3f}", 1.0f), " +1.000"); t!(format!("{:+10.3f}", 1.0f), " +1.000");
t!(ifmt!("{:+10.3f}", -1.0f), " -1.000"); t!(format!("{:+10.3f}", -1.0f), " -1.000");
test_ifmtf(); test_write();
test_print();
} }
fn test_ifmtf() { // Basic test to make sure that we can invoke the `write!` macro with an
// io::Writer instance.
fn test_write() {
use std::rt::io::Decorator; use std::rt::io::Decorator;
use std::rt::io::mem::MemWriter; use std::rt::io::mem::MemWriter;
use std::rt::io; use std::rt::io;
use std::str; use std::str;
let mut buf = MemWriter::new(); let mut buf = MemWriter::new();
ifmtf!(&mut buf as &mut io::Writer, "{}", 3); write!(&mut buf as &mut io::Writer, "{}", 3);
{ {
let w = &mut buf as &mut io::Writer; let w = &mut buf as &mut io::Writer;
ifmtf!(w, "{foo}", foo=4); write!(w, "{foo}", foo=4);
ifmtf!(w, "{:s}", "hello"); write!(w, "{:s}", "hello");
writeln!(w, "{}", "line");
writeln!(w, "{foo}", foo="bar");
} }
let s = str::from_bytes_owned(buf.inner()); let s = str::from_bytes_owned(buf.inner());
t!(s, "34hello"); t!(s, "34helloline\nbar\n");
}
// Just make sure that the macros are defined, there's not really a lot that we
// can do with them just yet (to test the output)
fn test_print() {
print!(1);
print!("{:?}", ~[0u8]);
println!("hello");
println!("this is a {}", "test");
println!("{foo}", foo="bar");
} }