Reduce the amount of complexity in format!

This renames the syntax-extension file to format from ifmt, and it also reduces
the amount of complexity inside by defining all other macros in terms of
format_args!
This commit is contained in:
Alex Crichton 2013-09-12 19:36:58 -07:00
parent 36872e4180
commit cfe3db810b
6 changed files with 73 additions and 124 deletions

View File

@ -452,6 +452,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.

View File

@ -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)));

View File

@ -715,11 +715,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(
@ -730,11 +730,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(
@ -753,8 +753,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!())
)
)
@ -958,17 +958,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

View File

@ -54,21 +54,16 @@ impl Context {
/// Parses the arguments from the given list of tokens, returning None if
/// there's a parse error so we can continue parsing other fmt! expressions.
fn parse_args(&mut self, sp: Span,
leading_expr: bool,
tts: &[ast::token_tree]) -> (Option<@ast::Expr>,
Option<@ast::Expr>) {
tts: &[ast::token_tree]) -> (@ast::Expr, Option<@ast::Expr>) {
let p = rsparse::new_parser_from_tts(self.ecx.parse_sess(),
self.ecx.cfg(),
tts.to_owned());
// If we want a leading expression, parse it here
let extra = if leading_expr {
let e = Some(p.parse_expr());
if !p.eat(&token::COMMA) {
self.ecx.span_err(sp, "expected token: `,`");
return (e, None);
}
e
} else { None };
// Parse the leading function expression (maybe a block, maybe a path)
let extra = p.parse_expr();
if !p.eat(&token::COMMA) {
self.ecx.span_err(sp, "expected token: `,`");
return (extra, None);
}
if *p.token == token::EOF {
self.ecx.span_err(sp, "requires at least a format string argument");
@ -547,7 +542,7 @@ impl Context {
/// Actually builds the expression which the ifmt! block will be expanded
/// to
fn to_expr(&self, extra: Option<@ast::Expr>, f: Option<&str>) -> @ast::Expr {
fn to_expr(&self, extra: @ast::Expr) -> @ast::Expr {
let mut lets = ~[];
let mut locals = ~[];
let mut names = vec::from_fn(self.name_positions.len(), |_| None);
@ -614,68 +609,33 @@ impl Context {
self.ecx.expr_ident(e.span, lname)));
}
// Now create the fmt::Arguments struct with all our locals we created.
let args = names.move_iter().map(|a| a.unwrap());
let mut args = locals.move_iter().chain(args);
let fmt = self.ecx.expr_ident(self.fmtsp, static_name);
let args = self.ecx.expr_vec_slice(self.fmtsp, args.collect());
let result = self.ecx.expr_call_global(self.fmtsp, ~[
self.ecx.ident_of("std"),
self.ecx.ident_of("fmt"),
self.ecx.ident_of("Arguments"),
self.ecx.ident_of("new"),
], ~[fmt, args]);
let result = match f {
// Invocation of write!()/format!(), call the function and we're
// done.
Some(f) => {
let mut fmt_args = match extra {
Some(e) => ~[e], None => ~[]
};
fmt_args.push(self.ecx.expr_ident(self.fmtsp, static_name));
fmt_args.push(self.ecx.expr_vec_slice(self.fmtsp,
args.collect()));
let result = self.ecx.expr_call_global(self.fmtsp, ~[
self.ecx.ident_of("std"),
self.ecx.ident_of("fmt"),
self.ecx.ident_of(f),
], fmt_args);
// sprintf is unsafe, but we just went through a lot of work to
// validate that our call is save, so inject the unsafe block
// for the user.
self.ecx.expr_block(ast::Block {
view_items: ~[],
stmts: ~[],
expr: Some(result),
id: ast::DUMMY_NODE_ID,
rules: ast::UnsafeBlock(ast::CompilerGenerated),
span: self.fmtsp,
})
}
// Invocation of format_args!()
None => {
let fmt = self.ecx.expr_ident(self.fmtsp, static_name);
let args = self.ecx.expr_vec_slice(self.fmtsp, args.collect());
let result = self.ecx.expr_call_global(self.fmtsp, ~[
self.ecx.ident_of("std"),
self.ecx.ident_of("fmt"),
self.ecx.ident_of("Arguments"),
self.ecx.ident_of("new"),
], ~[fmt, args]);
// We did all the work of making sure that the arguments
// structure is safe, so we can safely have an unsafe block.
let result = self.ecx.expr_block(ast::Block {
view_items: ~[],
stmts: ~[],
expr: Some(result),
id: ast::DUMMY_NODE_ID,
rules: ast::UnsafeBlock(ast::CompilerGenerated),
span: self.fmtsp,
});
let extra = extra.unwrap();
let resname = self.ecx.ident_of("__args");
lets.push(self.ecx.stmt_let(self.fmtsp, false, resname, result));
let res = self.ecx.expr_ident(self.fmtsp, resname);
self.ecx.expr_call(extra.span, extra, ~[
self.ecx.expr_addr_of(extra.span, res)])
}
};
// We did all the work of making sure that the arguments
// structure is safe, so we can safely have an unsafe block.
let result = self.ecx.expr_block(ast::Block {
view_items: ~[],
stmts: ~[],
expr: Some(result),
id: ast::DUMMY_NODE_ID,
rules: ast::UnsafeBlock(ast::CompilerGenerated),
span: self.fmtsp,
});
let resname = self.ecx.ident_of("__args");
lets.push(self.ecx.stmt_let(self.fmtsp, false, resname, result));
let res = self.ecx.expr_ident(self.fmtsp, resname);
let result = self.ecx.expr_call(extra.span, extra, ~[
self.ecx.expr_addr_of(extra.span, res)]);
self.ecx.expr_block(self.ecx.block(self.fmtsp, lets,
Some(result)))
}
@ -740,29 +700,8 @@ impl Context {
}
}
pub fn expand_format(ecx: @ExtCtxt, sp: Span,
tts: &[ast::token_tree]) -> base::MacResult {
expand_ifmt(ecx, sp, tts, false, false, Some("format_unsafe"))
}
pub fn expand_write(ecx: @ExtCtxt, sp: Span,
tts: &[ast::token_tree]) -> base::MacResult {
expand_ifmt(ecx, sp, tts, true, false, Some("write_unsafe"))
}
pub fn expand_writeln(ecx: @ExtCtxt, sp: Span,
tts: &[ast::token_tree]) -> base::MacResult {
expand_ifmt(ecx, sp, tts, true, true, Some("write_unsafe"))
}
pub fn expand_format_args(ecx: @ExtCtxt, sp: Span,
tts: &[ast::token_tree]) -> base::MacResult {
expand_ifmt(ecx, sp, tts, true, false, None)
}
fn expand_ifmt(ecx: @ExtCtxt, sp: Span, tts: &[ast::token_tree],
leading_arg: bool, append_newline: bool,
function: Option<&str>) -> base::MacResult {
pub fn expand_args(ecx: @ExtCtxt, sp: Span,
tts: &[ast::token_tree]) -> base::MacResult {
let mut cx = Context {
ecx: ecx,
args: ~[],
@ -776,14 +715,13 @@ fn expand_ifmt(ecx: @ExtCtxt, sp: Span, tts: &[ast::token_tree],
method_statics: ~[],
fmtsp: sp,
};
let (extra, efmt) = match cx.parse_args(sp, leading_arg, tts) {
let (extra, efmt) = match cx.parse_args(sp, tts) {
(extra, Some(e)) => (extra, e),
(_, None) => { return MRExpr(ecx.expr_uint(sp, 2)); }
};
cx.fmtsp = efmt.span;
let fmt = expr_to_str(ecx, efmt,
"format argument must be a string literal.");
let fmt = if append_newline { fmt + "\n" } else { fmt.to_owned() };
let mut err = false;
do parse::parse_error::cond.trap(|m| {
@ -814,5 +752,5 @@ fn expand_ifmt(ecx: @ExtCtxt, sp: Span, tts: &[ast::token_tree],
}
}
MRExpr(cx.to_expr(extra, function))
MRExpr(cx.to_expr(extra))
}

View File

@ -72,7 +72,7 @@ pub mod ext {
pub mod cfg;
pub mod fmt;
pub mod ifmt;
pub mod format;
pub mod env;
pub mod bytes;
pub mod concat_idents;

View File

@ -11,7 +11,6 @@
fn main() {
// bad arguments to the format! call
format!(); //~ ERROR: requires at least a format string
format!("{}"); //~ ERROR: invalid reference to argument
format!("{1}", 1); //~ ERROR: invalid reference to argument `1`
@ -30,8 +29,6 @@ fn main() {
format!("{foo}", foo=1, foo=2); //~ ERROR: duplicate argument
format!("#"); //~ ERROR: `#` reference used
format!("", foo=1, 2); //~ ERROR: positional arguments cannot follow
format!("" 1); //~ ERROR: expected token: `,`
format!("", 1 1); //~ ERROR: expected token: `,`
format!("{0, select, a{} a{} other{}}", "a"); //~ ERROR: duplicate selector
format!("{0, plural, =1{} =1{} other{}}", 1u); //~ ERROR: duplicate selector
@ -74,4 +71,9 @@ fn main() {
format!("foo } bar"); //~ ERROR: unmatched `}` found
format!("foo }"); //~ ERROR: unmatched `}` found
// FIXME(#5794) the spans on these errors are pretty terrible
//format!();
//format!("" 1);
//format!("", 1 1);
}