Skip to content

Remove hacks from ifmt and introduce fprintf #8637

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 60 additions & 38 deletions src/libstd/fmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,33 @@

# 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
format arguments at runtime into strings and streams.

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
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.

## Usage

The `ifmt!` macro is intended to be familiar to those coming from C's
printf/sprintf functions or Python's `str.format` function. In its current
revision, the `ifmt!` macro returns a `~str` type which is the result of the
The `format!` macro is intended to be familiar to those coming from C's
printf/fprintf functions or Python's `str.format` function. In its current
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
arguments directly while performing minimal allocations.

Some examples of the `ifmt!` extension are:
Some examples of the `format!` extension are:

~~~{.rust}
ifmt!("Hello") // => ~"Hello"
ifmt!("Hello, {:s}!", "world") // => ~"Hello, world!"
ifmt!("The number is {:d}", 1) // => ~"The number is 1"
ifmt!("{}", ~[3, 4]) // => ~"~[3, 4]"
ifmt!("{value}", value=4) // => ~"4"
ifmt!("{} {}", 1, 2) // => ~"1 2"
format!("Hello") // => ~"Hello"
format!("Hello, {:s}!", "world") // => ~"Hello, world!"
format!("The number is {:d}", 1) // => ~"The number is 1"
format!("{}", ~[3, 4]) // => ~"~[3, 4]"
format!("{value}", value=4) // => ~"4"
format!("{} {}", 1, 2) // => ~"1 2"
~~~

From these, you can see that the first argument is a format string. It is
Expand All @@ -62,7 +62,7 @@ format string, although it must always be referred to with the same type.
### Named parameters

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
argument list and have the syntax:

Expand Down Expand Up @@ -146,7 +146,7 @@ helper methods.

## 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
outputs depending on the input. The syntax and methods provided are similar to
other internationalization systems, so again nothing should seem alien.
Expand All @@ -164,7 +164,7 @@ to reference the string value of the argument which was selected upon. As an
example:

~~~
ifmt!("{0, select, other{#}}", "hello") // => ~"hello"
format!("{0, select, other{#}}", "hello") // => ~"hello"
~~~

This example is the equivalent of `{0:s}` essentially.
Expand Down Expand Up @@ -399,7 +399,44 @@ pub trait Pointer { fn fmt(&Self, &mut Formatter); }
#[allow(missing_doc)]
pub trait Float { fn fmt(&Self, &mut Formatter); }

/// The sprintf function takes a precompiled format string and a list of
/// The `write` function takes an output stream, a precompiled format string,
/// and a list of arguments. The arguments will be formatted according to the
/// specified format string into the output stream provided.
///
/// See the documentation for `format` for why this function is unsafe and care
/// should be taken if calling it manually.
///
/// Thankfully the rust compiler provides the macro `fmtf!` which will perform
/// all of this validation at compile-time and provides a safe interface for
/// invoking this function.
///
/// # Arguments
///
/// * output - the buffer to write output to
/// * fmts - the precompiled format string to emit
/// * args - the list of arguments to the format string. These are only the
/// positional arguments (not named)
///
/// Note that this function assumes that there are enough arguments for the
/// format string.
pub unsafe fn write(output: &mut io::Writer,
fmt: &[rt::Piece], args: &[Argument]) {
let mut formatter = Formatter {
flags: 0,
width: None,
precision: None,
buf: output,
align: parse::AlignUnknown,
fill: ' ',
args: args,
curarg: args.iter(),
};
for piece in fmt.iter() {
formatter.run(piece, None);
}
}

/// The format function takes a precompiled format string and a list of
/// arguments, to return the resulting formatted string.
///
/// This is currently an unsafe function because the types of all arguments
Expand All @@ -409,7 +446,7 @@ pub trait Float { fn fmt(&Self, &mut Formatter); }
/// for formatting the right type value. Because of this, the function is marked
/// 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
/// invoking this function.
///
Expand All @@ -421,32 +458,17 @@ pub trait Float { fn fmt(&Self, &mut Formatter); }
///
/// Note that this function assumes that there are enough arguments for the
/// format string.
pub unsafe fn sprintf(fmt: &[rt::Piece], args: &[Argument]) -> ~str {
let output = MemWriter::new();
{
let mut formatter = Formatter {
flags: 0,
width: None,
precision: None,
// FIXME(#8248): shouldn't need a transmute
buf: cast::transmute(&output as &io::Writer),
align: parse::AlignUnknown,
fill: ' ',
args: args,
curarg: args.iter(),
};
for piece in fmt.iter() {
formatter.run(piece, None);
}
}
pub unsafe fn format(fmt: &[rt::Piece], args: &[Argument]) -> ~str {
let mut output = MemWriter::new();
write(&mut output as &mut io::Writer, fmt, args);
return str::from_bytes_owned(output.inner());
}

impl<'self> Formatter<'self> {

// 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
// the ifmt! syntax extension.
// the format! syntax extension.

fn run(&mut self, piece: &rt::Piece, cur: Option<&str>) {
let setcount = |slot: &mut Option<uint>, cnt: &parse::Count| {
Expand Down Expand Up @@ -710,7 +732,7 @@ impl<'self> Formatter<'self> {
}

/// 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)]
pub fn argument<'a, T>(f: extern "Rust" fn(&T, &mut Formatter),
t: &'a T) -> Argument<'a> {
Expand Down
8 changes: 6 additions & 2 deletions src/libsyntax/ext/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,12 @@ pub fn syntax_expander_table() -> SyntaxEnv {
ext::tt::macro_rules::add_new_extension));
syntax_expanders.insert(intern(&"fmt"),
builtin_normal_tt(ext::fmt::expand_syntax_ext));
syntax_expanders.insert(intern(&"ifmt"),
builtin_normal_tt(ext::ifmt::expand_syntax_ext));
syntax_expanders.insert(intern(&"format"),
builtin_normal_tt(ext::ifmt::expand_format));
syntax_expanders.insert(intern(&"write"),
builtin_normal_tt(ext::ifmt::expand_write));
syntax_expanders.insert(intern(&"writeln"),
builtin_normal_tt(ext::ifmt::expand_writeln));
syntax_expanders.insert(
intern(&"auto_encode"),
@SE(ItemDecorator(ext::auto_encode::expand_auto_encode)));
Expand Down
62 changes: 43 additions & 19 deletions src/libsyntax/ext/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -758,32 +758,32 @@ pub fn std_macros() -> @str {
)
)

// conditionally define debug!, but keep it type checking even
// in non-debug builds.
macro_rules! __debug (
macro_rules! debug (
($arg:expr) => (
__log(4u32, fmt!( \"%?\", $arg ))
if cfg!(debug) { __log(4u32, fmt!( \"%?\", $arg )) }
);
($( $arg:expr ),+) => (
__log(4u32, fmt!( $($arg),+ ))
if cfg!(debug) { __log(4u32, fmt!( $($arg),+ )) }
)
)

#[cfg(debug)]
#[macro_escape]
mod debug_macro {
macro_rules! debug (($($arg:expr),*) => {
__debug!($($arg),*)
})
}
macro_rules! error2 (
($($arg:tt)*) => ( __log(1u32, format!($($arg)*)))
)

#[cfg(not(debug))]
#[macro_escape]
mod debug_macro {
macro_rules! debug (($($arg:expr),*) => {
if false { __debug!($($arg),*) }
})
}
macro_rules! warn2 (
($($arg:tt)*) => ( __log(2u32, format!($($arg)*)))
)

macro_rules! info2 (
($($arg:tt)*) => ( __log(3u32, format!($($arg)*)))
)

macro_rules! debug2 (
($($arg:tt)*) => (
if cfg!(debug) { __log(4u32, format!($($arg)*)) }
)
)

macro_rules! fail(
() => (
Expand All @@ -797,6 +797,15 @@ pub fn std_macros() -> @str {
)
)

macro_rules! fail2(
() => (
fail!(\"explicit failure\")
);
($($arg:tt)+) => (
::std::sys::FailWithCause::fail_with(format!($($arg)+), file!(), line!())
)
)

macro_rules! assert(
($cond:expr) => {
if !$cond {
Expand Down Expand Up @@ -940,6 +949,7 @@ pub fn std_macros() -> @str {
);
)

// NOTE(acrichto): start removing this after the next snapshot
macro_rules! printf (
($arg:expr) => (
print(fmt!(\"%?\", $arg))
Expand All @@ -949,6 +959,7 @@ pub fn std_macros() -> @str {
)
)

// NOTE(acrichto): start removing this after the next snapshot
macro_rules! printfln (
($arg:expr) => (
println(fmt!(\"%?\", $arg))
Expand All @@ -958,6 +969,19 @@ 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:tt)+) => ( ::std::io::print(format!($($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
// of the TLS interface.
macro_rules! local_data_key (
Expand Down
Loading