From 67512f717e7592d347fc825d2917b786a19b78bd Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 20 Aug 2013 00:40:27 -0700 Subject: [PATCH] Implement a wrapper macro around fprintf -- ifmtf --- src/libsyntax/ext/base.rs | 4 ++- src/libsyntax/ext/ifmt.rs | 68 +++++++++++++++++++++++++++------------ src/test/run-pass/ifmt.rs | 22 ++++++++++++- 3 files changed, 71 insertions(+), 23 deletions(-) diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index dfaffa0c275..560cb26d09a 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -140,7 +140,9 @@ pub fn syntax_expander_table() -> SyntaxEnv { 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)); + builtin_normal_tt(ext::ifmt::expand_sprintf)); + syntax_expanders.insert(intern(&"ifmtf"), + builtin_normal_tt(ext::ifmt::expand_fprintf)); syntax_expanders.insert( intern(&"auto_encode"), @SE(ItemDecorator(ext::auto_encode::expand_auto_encode))); diff --git a/src/libsyntax/ext/ifmt.rs b/src/libsyntax/ext/ifmt.rs index 35be77b95c5..ae098735e5b 100644 --- a/src/libsyntax/ext/ifmt.rs +++ b/src/libsyntax/ext/ifmt.rs @@ -54,20 +54,32 @@ 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, - tts: &[ast::token_tree]) -> Option<@ast::expr> { + leading_expr: bool, + tts: &[ast::token_tree]) -> (Option<@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 (for ifmtf), 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 }; + if *p.token == token::EOF { - self.ecx.span_err(sp, "ifmt! expects at least one argument"); - return None; + self.ecx.span_err(sp, "requires at least a format string argument"); + return (extra, None); } let fmtstr = p.parse_expr(); let mut named = false; while *p.token != token::EOF { if !p.eat(&token::COMMA) { self.ecx.span_err(sp, "expected token: `,`"); - return None; + return (extra, None); } if named || (token::is_ident(p.token) && p.look_ahead(1, |t| *t == token::EQ)) { @@ -81,14 +93,14 @@ impl Context { self.ecx.span_err(*p.span, "expected ident, positional arguments \ cannot follow named arguments"); - return None; + return (extra, None); } _ => { self.ecx.span_err(*p.span, fmt!("expected ident for named \ argument, but found `%s`", p.this_token_to_str())); - return None; + return (extra, None); } }; let name = self.ecx.str_of(ident); @@ -110,7 +122,7 @@ impl Context { self.arg_types.push(None); } } - return Some(fmtstr); + return (extra, Some(fmtstr)); } /// Verifies one piece of a parse string. All errors are not emitted as @@ -530,7 +542,7 @@ impl Context { /// Actually builds the expression which the ifmt! block will be expanded /// to - fn to_expr(&self) -> @ast::expr { + fn to_expr(&self, extra: Option<@ast::expr>, f: &str) -> @ast::expr { let mut lets = ~[]; let mut locals = ~[]; let mut names = vec::from_fn(self.name_positions.len(), |_| None); @@ -596,15 +608,18 @@ impl Context { let args = names.move_iter().map(|a| a.unwrap()); let mut args = locals.move_iter().chain(args); - // Next, build up the actual call to the sprintf function. + 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(self.fmtsp, args.collect())); + + // Next, build up the actual call to the {s,f}printf function. let result = self.ecx.expr_call_global(self.fmtsp, ~[ self.ecx.ident_of("std"), self.ecx.ident_of("fmt"), - self.ecx.ident_of("sprintf"), - ], ~[ - self.ecx.expr_ident(self.fmtsp, static_name), - self.ecx.expr_vec(self.fmtsp, args.collect()), - ]); + 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 @@ -682,8 +697,19 @@ impl Context { } } -pub fn expand_syntax_ext(ecx: @ExtCtxt, sp: span, - tts: &[ast::token_tree]) -> base::MacResult { +pub fn expand_sprintf(ecx: @ExtCtxt, sp: span, + tts: &[ast::token_tree]) -> base::MacResult { + expand_ifmt(ecx, sp, tts, false, "sprintf") +} + +pub fn expand_fprintf(ecx: @ExtCtxt, sp: span, + tts: &[ast::token_tree]) -> base::MacResult { + expand_ifmt(ecx, sp, tts, true, "fprintf") +} + + +fn expand_ifmt(ecx: @ExtCtxt, sp: span, tts: &[ast::token_tree], + leading_arg: bool, function: &str) -> base::MacResult { let mut cx = Context { ecx: ecx, args: ~[], @@ -697,13 +723,13 @@ pub fn expand_syntax_ext(ecx: @ExtCtxt, sp: span, method_statics: ~[], fmtsp: sp, }; - let efmt = match cx.parse_args(sp, tts) { - Some(e) => e, - None => { return MRExpr(ecx.expr_uint(sp, 2)); } + let (extra, efmt) = match cx.parse_args(sp, leading_arg, 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, - "first argument to ifmt! must be a string literal."); + "format argument must be a string literal."); let mut err = false; do parse::parse_error::cond.trap(|m| { @@ -734,5 +760,5 @@ pub fn expand_syntax_ext(ecx: @ExtCtxt, sp: span, } } - MRExpr(cx.to_expr()) + MRExpr(cx.to_expr(extra, function)) } diff --git a/src/test/run-pass/ifmt.rs b/src/test/run-pass/ifmt.rs index cba28463f99..b5341ca80c5 100644 --- a/src/test/run-pass/ifmt.rs +++ b/src/test/run-pass/ifmt.rs @@ -21,8 +21,9 @@ impl fmt::Signed for B { fn fmt(_: &B, f: &mut fmt::Formatter) { f.buf.write("adios".as_bytes()); } } +macro_rules! t(($a:expr, $b:expr) => { assert_eq!($a, $b.to_owned()) }) + 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"); @@ -209,5 +210,24 @@ pub fn main() { t!(ifmt!("{:10.3f}", 1.0f), " 1.000"); t!(ifmt!("{:+10.3f}", 1.0f), " +1.000"); t!(ifmt!("{:+10.3f}", -1.0f), " -1.000"); + + test_ifmtf(); } +fn test_ifmtf() { + use std::rt::io::Decorator; + use std::rt::io::mem::MemWriter; + use std::rt::io; + use std::str; + + let mut buf = MemWriter::new(); + ifmtf!(&mut buf as &mut io::Writer, "{}", 3); + { + let w = &mut buf as &mut io::Writer; + ifmtf!(w, "{foo}", foo=4); + ifmtf!(w, "{:s}", "hello"); + } + + let s = str::from_bytes_owned(buf.inner()); + t!(s, "34hello"); +}