Skip to content

Commit

Permalink
auto merge of #9153 : alexcrichton/rust/simplify-format, r=huonw
Browse files Browse the repository at this point in the history
This follows from the discussion in #9012.

* All macros are now defined in terms of `format_args!` allowing for removal of a good bit of code in the syntax extension
* The syntax extension is now in a more aptly-named file, `format.rs`
* Documentation was added for the `format!`-related macros.
  • Loading branch information
bors committed Sep 15, 2013
2 parents 36872e4 + 6406138 commit 4ecb0a3
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 142 deletions.
83 changes: 81 additions & 2 deletions src/libstd/fmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ is `?` which is defined for all types by default.
When implementing a format trait for your own time, you will have to implement a
method of the signature:
~~~
~~~{.rust}
fn fmt(value: &T, f: &mut std::fmt::Formatter);
~~~
Expand All @@ -144,6 +144,78 @@ values of these parameters will be listed in the fields of the `Formatter`
struct. In order to help with this, the `Formatter` struct also provides some
helper methods.
### Related macros
There are a number of related macros in the `format!` family. The ones that are
currently implemented are:
~~~{.rust}
format! // described above
write! // first argument is a &mut rt::io::Writer, the destination
writeln! // same as write but appends a newline
print! // the format string is printed to the standard output
println! // same as print but appends a newline
format_args! // described below.
~~~
#### `write!`
This and `writeln` are two macros which are used to emit the format string to a
specified stream. This is used to prevent intermediate allocations of format
strings and instead directly write the output. Under the hood, this function is
actually invoking the `write` function defined in this module. Example usage is:
~~~{.rust}
use std::rt::io;
let mut w = io::mem::MemWriter::new();
write!(&mut w as &mut io::Writer, "Hello {}!", "world");
~~~
#### `print!`
This and `println` emit their output to stdout. Similarly to the `write!` macro,
the goal of these macros is to avoid intermediate allocations when printing
output. Example usage is:
~~~{.rust}
print!("Hello {}!", "world");
println!("I have a newline {}", "character at the end");
~~~
#### `format_args!`
This is a curious macro which is used to safely pass around
an opaque object describing the format string. This object
does not require any heap allocations to create, and it only
references information on the stack. Under the hood, all of
the related macros are implemented in terms of this. First
off, some example usage is:
~~~{.rust}
use std::fmt;
format_args!(fmt::format, "this returns {}", "~str");
format_args!(|args| { fmt::write(my_writer, args) }, "some {}", "args");
format_args!(my_fn, "format {}", "string");
~~~
The first argument of the `format_args!` macro is a function (or closure) which
takes one argument of type `&fmt::Arguments`. This structure can then be
passed to the `write` and `format` functions inside this module in order to
process the format string. The goal of this macro is to even further prevent
intermediate allocations when dealing formatting strings.
For example, a logging library could use the standard formatting syntax, but it
would internally pass around this structure until it has been determined where
output should go to.
It is unsafe to programmatically create an instance of `fmt::Arguments` because
the operations performed when executing a format string require the compile-time
checks provided by the compiler. The `format_args!` macro is the only method of
safely creating these structures, but they can be unsafely created with the
constructor provided.
## Internationalization
The formatting syntax supported by the `format!` extension supports
Expand All @@ -163,7 +235,7 @@ Furthermore, whenever a case is running, the special character `#` can be used
to reference the string value of the argument which was selected upon. As an
example:
~~~
~~~{.rust}
format!("{0, select, other{#}}", "hello") // => ~"hello"
~~~
Expand Down Expand Up @@ -452,6 +524,13 @@ pub fn write(output: &mut io::Writer, args: &Arguments) {
unsafe { write_unsafe(output, args.fmt, args.args) }
}

/// The `writeln` function takes the same arguments as `write`, except that it
/// will also write a newline (`\n`) character at the end of the format string.
pub fn writeln(output: &mut io::Writer, args: &Arguments) {
unsafe { write_unsafe(output, args.fmt, args.args) }
output.write(['\n' as u8]);
}

/// The `write_unsafe` 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.
Expand Down
8 changes: 1 addition & 7 deletions src/libsyntax/ext/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,14 +155,8 @@ pub fn syntax_expander_table() -> SyntaxEnv {
@SE(IdentTT(ext::tt::macro_rules::add_new_extension, None)));
syntax_expanders.insert(intern(&"fmt"),
builtin_normal_tt_no_ctxt(ext::fmt::expand_syntax_ext));
syntax_expanders.insert(intern(&"format"),
builtin_normal_tt_no_ctxt(ext::ifmt::expand_format));
syntax_expanders.insert(intern(&"write"),
builtin_normal_tt_no_ctxt(ext::ifmt::expand_write));
syntax_expanders.insert(intern(&"writeln"),
builtin_normal_tt_no_ctxt(ext::ifmt::expand_writeln));
syntax_expanders.insert(intern(&"format_args"),
builtin_normal_tt_no_ctxt(ext::ifmt::expand_format_args));
builtin_normal_tt_no_ctxt(ext::format::expand_args));
syntax_expanders.insert(
intern(&"auto_encode"),
@SE(ItemDecorator(ext::auto_encode::expand_auto_encode)));
Expand Down
78 changes: 48 additions & 30 deletions src/libsyntax/ext/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,20 +81,10 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv,
// be the root of the call stack. That's the most
// relevant span and it's the actual invocation of
// the macro.
let mut relevant_info = cx.backtrace();
let mut einfo = relevant_info.unwrap();
loop {
match relevant_info {
None => { break }
Some(e) => {
einfo = e;
relevant_info = einfo.call_site.expn_info;
}
}
}
let mac_span = original_span(cx);

let expanded =
match expandfun(cx, einfo.call_site,
match expandfun(cx, mac_span.call_site,
marked_before, marked_ctxt) {
MRExpr(e) => e,
MRAny(expr_maker,_,_) => expr_maker(),
Expand Down Expand Up @@ -400,11 +390,11 @@ pub fn expand_stmt(extsbox: @mut SyntaxEnv,
-> (Option<Stmt_>, Span) {
// why the copying here and not in expand_expr?
// looks like classic changed-in-only-one-place
let (mac, pth, tts, semi, ctxt) = match *s {
let (pth, tts, semi, ctxt) = match *s {
StmtMac(ref mac, semi) => {
match mac.node {
mac_invoc_tt(ref pth, ref tts, ctxt) => {
((*mac).clone(), pth, (*tts).clone(), semi, ctxt)
(pth, (*tts).clone(), semi, ctxt)
}
}
}
Expand All @@ -431,7 +421,13 @@ pub fn expand_stmt(extsbox: @mut SyntaxEnv,
// mark before expansion:
let marked_tts = mark_tts(tts,fm);
let marked_ctxt = new_mark(fm,ctxt);
let expanded = match expandfun(cx, mac.span, marked_tts, marked_ctxt) {

// See the comment in expand_expr for why we want the original span,
// not the current mac.span.
let mac_span = original_span(cx);

let expanded = match expandfun(cx, mac_span.call_site,
marked_tts, marked_ctxt) {
MRExpr(e) =>
@codemap::Spanned { node: StmtExpr(e, ast::DUMMY_NODE_ID),
span: e.span},
Expand Down Expand Up @@ -715,11 +711,11 @@ pub fn std_macros() -> @str {
}
})
)
macro_rules! error( ($($arg:tt)+) => (log!(1u32, $($arg)+)) )
macro_rules! warn ( ($($arg:tt)+) => (log!(2u32, $($arg)+)) )
macro_rules! info ( ($($arg:tt)+) => (log!(3u32, $($arg)+)) )
macro_rules! debug( ($($arg:tt)+) => (
if cfg!(debug) { log!(4u32, $($arg)+) }
macro_rules! error( ($($arg:tt)*) => (log!(1u32, $($arg)*)) )
macro_rules! warn ( ($($arg:tt)*) => (log!(2u32, $($arg)*)) )
macro_rules! info ( ($($arg:tt)*) => (log!(3u32, $($arg)*)) )
macro_rules! debug( ($($arg:tt)*) => (
if cfg!(debug) { log!(4u32, $($arg)*) }
))

macro_rules! log2(
Expand All @@ -730,11 +726,11 @@ pub fn std_macros() -> @str {
}
})
)
macro_rules! error2( ($($arg:tt)+) => (log2!(1u32, $($arg)+)) )
macro_rules! warn2 ( ($($arg:tt)+) => (log2!(2u32, $($arg)+)) )
macro_rules! info2 ( ($($arg:tt)+) => (log2!(3u32, $($arg)+)) )
macro_rules! debug2( ($($arg:tt)+) => (
if cfg!(debug) { log2!(4u32, $($arg)+) }
macro_rules! error2( ($($arg:tt)*) => (log2!(1u32, $($arg)*)) )
macro_rules! warn2 ( ($($arg:tt)*) => (log2!(2u32, $($arg)*)) )
macro_rules! info2 ( ($($arg:tt)*) => (log2!(3u32, $($arg)*)) )
macro_rules! debug2( ($($arg:tt)*) => (
if cfg!(debug) { log2!(4u32, $($arg)*) }
))

macro_rules! fail(
Expand All @@ -753,8 +749,8 @@ pub fn std_macros() -> @str {
() => (
fail!(\"explicit failure\")
);
($($arg:tt)+) => (
::std::sys::FailWithCause::fail_with(format!($($arg)+), file!(), line!())
($($arg:tt)*) => (
::std::sys::FailWithCause::fail_with(format!($($arg)*), file!(), line!())
)
)

Expand Down Expand Up @@ -958,17 +954,25 @@ pub fn std_macros() -> @str {
)
)

macro_rules! format(($($arg:tt)*) => (
format_args!(::std::fmt::format, $($arg)*)
))
macro_rules! write(($dst:expr, $($arg:tt)*) => (
format_args!(|args| { ::std::fmt::write($dst, args) }, $($arg)*)
))
macro_rules! writeln(($dst:expr, $($arg:tt)*) => (
format_args!(|args| { ::std::fmt::writeln($dst, args) }, $($arg)*)
))
// 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)+)))
($($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)+) => (::std::io::println(format!($($arg)+)))
($($arg:tt)*) => (::std::io::println(format!($($arg)*)))
)

// NOTE: use this after a snapshot lands to abstract the details
Expand Down Expand Up @@ -1262,6 +1266,20 @@ pub fn mtwt_cancel_outer_mark(tts: &[ast::token_tree], ctxt: ast::SyntaxContext)
mark_tts(tts,outer_mark)
}

fn original_span(cx: @ExtCtxt) -> @codemap::ExpnInfo {
let mut relevant_info = cx.backtrace();
let mut einfo = relevant_info.unwrap();
loop {
match relevant_info {
None => { break }
Some(e) => {
einfo = e;
relevant_info = einfo.call_site.expn_info;
}
}
}
return einfo;
}

#[cfg(test)]
mod test {
Expand Down
Loading

0 comments on commit 4ecb0a3

Please sign in to comment.