Skip to content

Commit 67512f7

Browse files
committed
Implement a wrapper macro around fprintf -- ifmtf
1 parent 39207a3 commit 67512f7

File tree

3 files changed

+71
-23
lines changed

3 files changed

+71
-23
lines changed

src/libsyntax/ext/base.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,9 @@ pub fn syntax_expander_table() -> SyntaxEnv {
140140
syntax_expanders.insert(intern(&"fmt"),
141141
builtin_normal_tt(ext::fmt::expand_syntax_ext));
142142
syntax_expanders.insert(intern(&"ifmt"),
143-
builtin_normal_tt(ext::ifmt::expand_syntax_ext));
143+
builtin_normal_tt(ext::ifmt::expand_sprintf));
144+
syntax_expanders.insert(intern(&"ifmtf"),
145+
builtin_normal_tt(ext::ifmt::expand_fprintf));
144146
syntax_expanders.insert(
145147
intern(&"auto_encode"),
146148
@SE(ItemDecorator(ext::auto_encode::expand_auto_encode)));

src/libsyntax/ext/ifmt.rs

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -54,20 +54,32 @@ impl Context {
5454
/// Parses the arguments from the given list of tokens, returning None if
5555
/// there's a parse error so we can continue parsing other fmt! expressions.
5656
fn parse_args(&mut self, sp: span,
57-
tts: &[ast::token_tree]) -> Option<@ast::expr> {
57+
leading_expr: bool,
58+
tts: &[ast::token_tree]) -> (Option<@ast::expr>,
59+
Option<@ast::expr>) {
5860
let p = rsparse::new_parser_from_tts(self.ecx.parse_sess(),
5961
self.ecx.cfg(),
6062
tts.to_owned());
63+
// If we want a leading expression (for ifmtf), parse it here
64+
let extra = if leading_expr {
65+
let e = Some(p.parse_expr());
66+
if !p.eat(&token::COMMA) {
67+
self.ecx.span_err(sp, "expected token: `,`");
68+
return (e, None);
69+
}
70+
e
71+
} else { None };
72+
6173
if *p.token == token::EOF {
62-
self.ecx.span_err(sp, "ifmt! expects at least one argument");
63-
return None;
74+
self.ecx.span_err(sp, "requires at least a format string argument");
75+
return (extra, None);
6476
}
6577
let fmtstr = p.parse_expr();
6678
let mut named = false;
6779
while *p.token != token::EOF {
6880
if !p.eat(&token::COMMA) {
6981
self.ecx.span_err(sp, "expected token: `,`");
70-
return None;
82+
return (extra, None);
7183
}
7284
if named || (token::is_ident(p.token) &&
7385
p.look_ahead(1, |t| *t == token::EQ)) {
@@ -81,14 +93,14 @@ impl Context {
8193
self.ecx.span_err(*p.span,
8294
"expected ident, positional arguments \
8395
cannot follow named arguments");
84-
return None;
96+
return (extra, None);
8597
}
8698
_ => {
8799
self.ecx.span_err(*p.span,
88100
fmt!("expected ident for named \
89101
argument, but found `%s`",
90102
p.this_token_to_str()));
91-
return None;
103+
return (extra, None);
92104
}
93105
};
94106
let name = self.ecx.str_of(ident);
@@ -110,7 +122,7 @@ impl Context {
110122
self.arg_types.push(None);
111123
}
112124
}
113-
return Some(fmtstr);
125+
return (extra, Some(fmtstr));
114126
}
115127

116128
/// Verifies one piece of a parse string. All errors are not emitted as
@@ -530,7 +542,7 @@ impl Context {
530542

531543
/// Actually builds the expression which the ifmt! block will be expanded
532544
/// to
533-
fn to_expr(&self) -> @ast::expr {
545+
fn to_expr(&self, extra: Option<@ast::expr>, f: &str) -> @ast::expr {
534546
let mut lets = ~[];
535547
let mut locals = ~[];
536548
let mut names = vec::from_fn(self.name_positions.len(), |_| None);
@@ -596,15 +608,18 @@ impl Context {
596608
let args = names.move_iter().map(|a| a.unwrap());
597609
let mut args = locals.move_iter().chain(args);
598610

599-
// Next, build up the actual call to the sprintf function.
611+
let mut fmt_args = match extra {
612+
Some(e) => ~[e], None => ~[]
613+
};
614+
fmt_args.push(self.ecx.expr_ident(self.fmtsp, static_name));
615+
fmt_args.push(self.ecx.expr_vec(self.fmtsp, args.collect()));
616+
617+
// Next, build up the actual call to the {s,f}printf function.
600618
let result = self.ecx.expr_call_global(self.fmtsp, ~[
601619
self.ecx.ident_of("std"),
602620
self.ecx.ident_of("fmt"),
603-
self.ecx.ident_of("sprintf"),
604-
], ~[
605-
self.ecx.expr_ident(self.fmtsp, static_name),
606-
self.ecx.expr_vec(self.fmtsp, args.collect()),
607-
]);
621+
self.ecx.ident_of(f),
622+
], fmt_args);
608623

609624
// sprintf is unsafe, but we just went through a lot of work to
610625
// validate that our call is save, so inject the unsafe block for the
@@ -682,8 +697,19 @@ impl Context {
682697
}
683698
}
684699

685-
pub fn expand_syntax_ext(ecx: @ExtCtxt, sp: span,
686-
tts: &[ast::token_tree]) -> base::MacResult {
700+
pub fn expand_sprintf(ecx: @ExtCtxt, sp: span,
701+
tts: &[ast::token_tree]) -> base::MacResult {
702+
expand_ifmt(ecx, sp, tts, false, "sprintf")
703+
}
704+
705+
pub fn expand_fprintf(ecx: @ExtCtxt, sp: span,
706+
tts: &[ast::token_tree]) -> base::MacResult {
707+
expand_ifmt(ecx, sp, tts, true, "fprintf")
708+
}
709+
710+
711+
fn expand_ifmt(ecx: @ExtCtxt, sp: span, tts: &[ast::token_tree],
712+
leading_arg: bool, function: &str) -> base::MacResult {
687713
let mut cx = Context {
688714
ecx: ecx,
689715
args: ~[],
@@ -697,13 +723,13 @@ pub fn expand_syntax_ext(ecx: @ExtCtxt, sp: span,
697723
method_statics: ~[],
698724
fmtsp: sp,
699725
};
700-
let efmt = match cx.parse_args(sp, tts) {
701-
Some(e) => e,
702-
None => { return MRExpr(ecx.expr_uint(sp, 2)); }
726+
let (extra, efmt) = match cx.parse_args(sp, leading_arg, tts) {
727+
(extra, Some(e)) => (extra, e),
728+
(_, None) => { return MRExpr(ecx.expr_uint(sp, 2)); }
703729
};
704730
cx.fmtsp = efmt.span;
705731
let fmt = expr_to_str(ecx, efmt,
706-
"first argument to ifmt! must be a string literal.");
732+
"format argument must be a string literal.");
707733

708734
let mut err = false;
709735
do parse::parse_error::cond.trap(|m| {
@@ -734,5 +760,5 @@ pub fn expand_syntax_ext(ecx: @ExtCtxt, sp: span,
734760
}
735761
}
736762

737-
MRExpr(cx.to_expr())
763+
MRExpr(cx.to_expr(extra, function))
738764
}

src/test/run-pass/ifmt.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ impl fmt::Signed for B {
2121
fn fmt(_: &B, f: &mut fmt::Formatter) { f.buf.write("adios".as_bytes()); }
2222
}
2323

24+
macro_rules! t(($a:expr, $b:expr) => { assert_eq!($a, $b.to_owned()) })
25+
2426
pub fn main() {
25-
macro_rules! t(($a:expr, $b:expr) => { assert_eq!($a, $b.to_owned()) })
2627

2728
// Make sure there's a poly formatter that takes anything
2829
t!(ifmt!("{:?}", 1), "1");
@@ -209,5 +210,24 @@ pub fn main() {
209210
t!(ifmt!("{:10.3f}", 1.0f), " 1.000");
210211
t!(ifmt!("{:+10.3f}", 1.0f), " +1.000");
211212
t!(ifmt!("{:+10.3f}", -1.0f), " -1.000");
213+
214+
test_ifmtf();
212215
}
213216

217+
fn test_ifmtf() {
218+
use std::rt::io::Decorator;
219+
use std::rt::io::mem::MemWriter;
220+
use std::rt::io;
221+
use std::str;
222+
223+
let mut buf = MemWriter::new();
224+
ifmtf!(&mut buf as &mut io::Writer, "{}", 3);
225+
{
226+
let w = &mut buf as &mut io::Writer;
227+
ifmtf!(w, "{foo}", foo=4);
228+
ifmtf!(w, "{:s}", "hello");
229+
}
230+
231+
let s = str::from_bytes_owned(buf.inner());
232+
t!(s, "34hello");
233+
}

0 commit comments

Comments
 (0)