Implement a wrapper macro around fprintf -- ifmtf
This commit is contained in:
parent
39207a358b
commit
67512f717e
3 changed files with 71 additions and 23 deletions
|
@ -140,7 +140,9 @@ pub fn syntax_expander_table() -> SyntaxEnv {
|
||||||
syntax_expanders.insert(intern(&"fmt"),
|
syntax_expanders.insert(intern(&"fmt"),
|
||||||
builtin_normal_tt(ext::fmt::expand_syntax_ext));
|
builtin_normal_tt(ext::fmt::expand_syntax_ext));
|
||||||
syntax_expanders.insert(intern(&"ifmt"),
|
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(
|
syntax_expanders.insert(
|
||||||
intern(&"auto_encode"),
|
intern(&"auto_encode"),
|
||||||
@SE(ItemDecorator(ext::auto_encode::expand_auto_encode)));
|
@SE(ItemDecorator(ext::auto_encode::expand_auto_encode)));
|
||||||
|
|
|
@ -54,20 +54,32 @@ impl Context {
|
||||||
/// Parses the arguments from the given list of tokens, returning None if
|
/// 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.
|
/// there's a parse error so we can continue parsing other fmt! expressions.
|
||||||
fn parse_args(&mut self, sp: span,
|
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(),
|
let p = rsparse::new_parser_from_tts(self.ecx.parse_sess(),
|
||||||
self.ecx.cfg(),
|
self.ecx.cfg(),
|
||||||
tts.to_owned());
|
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 {
|
if *p.token == token::EOF {
|
||||||
self.ecx.span_err(sp, "ifmt! expects at least one argument");
|
self.ecx.span_err(sp, "requires at least a format string argument");
|
||||||
return None;
|
return (extra, None);
|
||||||
}
|
}
|
||||||
let fmtstr = p.parse_expr();
|
let fmtstr = p.parse_expr();
|
||||||
let mut named = false;
|
let mut named = false;
|
||||||
while *p.token != token::EOF {
|
while *p.token != token::EOF {
|
||||||
if !p.eat(&token::COMMA) {
|
if !p.eat(&token::COMMA) {
|
||||||
self.ecx.span_err(sp, "expected token: `,`");
|
self.ecx.span_err(sp, "expected token: `,`");
|
||||||
return None;
|
return (extra, None);
|
||||||
}
|
}
|
||||||
if named || (token::is_ident(p.token) &&
|
if named || (token::is_ident(p.token) &&
|
||||||
p.look_ahead(1, |t| *t == token::EQ)) {
|
p.look_ahead(1, |t| *t == token::EQ)) {
|
||||||
|
@ -81,14 +93,14 @@ impl Context {
|
||||||
self.ecx.span_err(*p.span,
|
self.ecx.span_err(*p.span,
|
||||||
"expected ident, positional arguments \
|
"expected ident, positional arguments \
|
||||||
cannot follow named arguments");
|
cannot follow named arguments");
|
||||||
return None;
|
return (extra, None);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.ecx.span_err(*p.span,
|
self.ecx.span_err(*p.span,
|
||||||
fmt!("expected ident for named \
|
fmt!("expected ident for named \
|
||||||
argument, but found `%s`",
|
argument, but found `%s`",
|
||||||
p.this_token_to_str()));
|
p.this_token_to_str()));
|
||||||
return None;
|
return (extra, None);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let name = self.ecx.str_of(ident);
|
let name = self.ecx.str_of(ident);
|
||||||
|
@ -110,7 +122,7 @@ impl Context {
|
||||||
self.arg_types.push(None);
|
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
|
/// 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
|
/// Actually builds the expression which the ifmt! block will be expanded
|
||||||
/// to
|
/// to
|
||||||
fn to_expr(&self) -> @ast::expr {
|
fn to_expr(&self, extra: Option<@ast::expr>, f: &str) -> @ast::expr {
|
||||||
let mut lets = ~[];
|
let mut lets = ~[];
|
||||||
let mut locals = ~[];
|
let mut locals = ~[];
|
||||||
let mut names = vec::from_fn(self.name_positions.len(), |_| None);
|
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 args = names.move_iter().map(|a| a.unwrap());
|
||||||
let mut args = locals.move_iter().chain(args);
|
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, ~[
|
let result = self.ecx.expr_call_global(self.fmtsp, ~[
|
||||||
self.ecx.ident_of("std"),
|
self.ecx.ident_of("std"),
|
||||||
self.ecx.ident_of("fmt"),
|
self.ecx.ident_of("fmt"),
|
||||||
self.ecx.ident_of("sprintf"),
|
self.ecx.ident_of(f),
|
||||||
], ~[
|
], fmt_args);
|
||||||
self.ecx.expr_ident(self.fmtsp, static_name),
|
|
||||||
self.ecx.expr_vec(self.fmtsp, args.collect()),
|
|
||||||
]);
|
|
||||||
|
|
||||||
// sprintf is unsafe, but we just went through a lot of work to
|
// 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
|
// 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,
|
pub fn expand_sprintf(ecx: @ExtCtxt, sp: span,
|
||||||
tts: &[ast::token_tree]) -> base::MacResult {
|
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 {
|
let mut cx = Context {
|
||||||
ecx: ecx,
|
ecx: ecx,
|
||||||
args: ~[],
|
args: ~[],
|
||||||
|
@ -697,13 +723,13 @@ pub fn expand_syntax_ext(ecx: @ExtCtxt, sp: span,
|
||||||
method_statics: ~[],
|
method_statics: ~[],
|
||||||
fmtsp: sp,
|
fmtsp: sp,
|
||||||
};
|
};
|
||||||
let efmt = match cx.parse_args(sp, tts) {
|
let (extra, efmt) = match cx.parse_args(sp, leading_arg, tts) {
|
||||||
Some(e) => e,
|
(extra, Some(e)) => (extra, e),
|
||||||
None => { return MRExpr(ecx.expr_uint(sp, 2)); }
|
(_, None) => { return MRExpr(ecx.expr_uint(sp, 2)); }
|
||||||
};
|
};
|
||||||
cx.fmtsp = efmt.span;
|
cx.fmtsp = efmt.span;
|
||||||
let fmt = expr_to_str(ecx, efmt,
|
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;
|
let mut err = false;
|
||||||
do parse::parse_error::cond.trap(|m| {
|
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))
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,9 @@ impl fmt::Signed for B {
|
||||||
fn fmt(_: &B, f: &mut fmt::Formatter) { f.buf.write("adios".as_bytes()); }
|
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() {
|
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
|
// Make sure there's a poly formatter that takes anything
|
||||||
t!(ifmt!("{:?}", 1), "1");
|
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");
|
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");
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue