#[cfg(not(stage0))]
#[doc(hidden)] #[inline]
pub unsafe fn new<'a>(pieces: &'static [&'static str],
- fmt: &'static [rt::Argument<'static>],
args: &'a [Argument<'a>]) -> Arguments<'a> {
Arguments {
pieces: mem::transmute(pieces),
- fmt: mem::transmute(fmt),
+ fmt: None,
+ args: args
+ }
+ }
+
+ /// This function is used to specify nonstandard formatting parameters.
+ /// The `pieces` array must be at least as long as `fmt` to construct
+ /// a valid Arguments structure.
+ #[cfg(not(stage0))]
+ #[doc(hidden)] #[inline]
+ pub unsafe fn with_placeholders<'a>(pieces: &'static [&'static str],
+ fmt: &'static [rt::Argument<'static>],
+ args: &'a [Argument<'a>]) -> Arguments<'a> {
+ Arguments {
+ pieces: mem::transmute(pieces),
+ fmt: Some(mem::transmute(fmt)),
args: args
}
}
/// and `format` functions can be safely performed.
#[cfg(not(stage0))]
pub struct Arguments<'a> {
+ // Format string pieces to print.
pieces: &'a [&'a str],
- fmt: &'a [rt::Argument<'a>],
+
+ // Placeholder specs, or `None` if all specs are default (as in "{}{}").
+ fmt: Option<&'a [rt::Argument<'a>]>,
+
+ // Dynamic arguments for interpolation, to be interleaved with string
+ // pieces. (Every argument is preceded by a string piece.)
args: &'a [Argument<'a>],
}
secret_upper_exp, UpperExp;
}
+#[cfg(not(stage0))]
+static DEFAULT_ARGUMENT: rt::Argument<'static> = rt::Argument {
+ position: rt::ArgumentNext,
+ format: rt::FormatSpec {
+ fill: ' ',
+ align: rt::AlignUnknown,
+ flags: 0,
+ precision: rt::CountImplied,
+ width: rt::CountImplied,
+ }
+};
+
/// The `write` 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.
let mut pieces = args.pieces.iter();
- for arg in args.fmt.iter() {
- try!(formatter.buf.write(pieces.next().unwrap().as_bytes()));
- try!(formatter.run(arg));
+ match args.fmt {
+ None => {
+ // We can use default formatting parameters for all arguments.
+ for _ in range(0, args.args.len()) {
+ try!(formatter.buf.write(pieces.next().unwrap().as_bytes()));
+ try!(formatter.run(&DEFAULT_ARGUMENT));
+ }
+ }
+ Some(fmt) => {
+ // Every spec has a corresponding argument that is preceded by
+ // a string piece.
+ for (arg, piece) in fmt.iter().zip(pieces.by_ref()) {
+ try!(formatter.buf.write(piece.as_bytes()));
+ try!(formatter.run(arg));
+ }
+ }
}
+ // There can be only one trailing string piece left.
match pieces.next() {
Some(piece) => {
try!(formatter.buf.write(piece.as_bytes()));
pieces: Vec<Gc<ast::Expr>>,
/// Collection of string literals
str_pieces: Vec<Gc<ast::Expr>>,
+ /// Stays `true` if all formatting parameters are default (as in "{}{}").
+ all_pieces_simple: bool,
+
name_positions: HashMap<String, uint>,
method_statics: Vec<Gc<ast::Item>>,
/// Translate a `parse::Piece` to a static `rt::Argument` or append
/// to the `literal` string.
fn trans_piece(&mut self, piece: &parse::Piece) -> Option<Gc<ast::Expr>> {
- // let mut is_not_default = true;
let sp = self.fmtsp;
match *piece {
parse::String(s) => {
}
};
- // Translate the format
+ let simple_arg = parse::Argument {
+ position: parse::ArgumentNext,
+ format: parse::FormatSpec {
+ fill: arg.format.fill,
+ align: parse::AlignUnknown,
+ flags: 0,
+ precision: parse::CountImplied,
+ width: parse::CountImplied,
+ ty: arg.format.ty
+ }
+ };
+
let fill = match arg.format.fill { Some(c) => c, None => ' ' };
+
+ if *arg != simple_arg || fill != ' ' {
+ self.all_pieces_simple = false;
+ }
+
+ // Translate the format
let fill = self.ecx.expr_lit(sp, ast::LitChar(fill));
let align = match arg.format.align {
parse::AlignLeft => {
}
}
+ fn item_static_array(&self,
+ name: ast::Ident,
+ piece_ty: Gc<ast::Ty>,
+ pieces: Vec<Gc<ast::Expr>>)
+ -> ast::Stmt
+ {
+ let pieces_len = self.ecx.expr_uint(self.fmtsp, pieces.len());
+ let fmt = self.ecx.expr_vec(self.fmtsp, pieces);
+ let ty = ast::TyFixedLengthVec(
+ piece_ty,
+ pieces_len
+ );
+ let ty = self.ecx.ty(self.fmtsp, ty);
+ let st = ast::ItemStatic(ty, ast::MutImmutable, fmt);
+ let item = self.ecx.item(self.fmtsp, name,
+ self.static_attrs(), st);
+ let decl = respan(self.fmtsp, ast::DeclItem(item));
+ respan(self.fmtsp, ast::StmtDecl(box(GC) decl, ast::DUMMY_NODE_ID))
+ }
+
/// Actually builds the expression which the iformat! block will be expanded
/// to
fn to_expr(&self, invocation: Invocation) -> Gc<ast::Expr> {
// Next, build up the static array which will become our precompiled
// format "string"
- let fmt = self.ecx.expr_vec(self.fmtsp, self.str_pieces.clone());
- let piece_ty = self.ecx.ty_rptr(self.fmtsp,
- self.ecx.ty_ident(self.fmtsp,
- self.ecx.ident_of("str")),
- Some(self.ecx.lifetime(self.fmtsp,
- self.ecx.ident_of(
- "'static").name)),
- ast::MutImmutable);
-
- let ty = ast::TyFixedLengthVec(
- piece_ty,
- self.ecx.expr_uint(self.fmtsp, self.str_pieces.len())
- );
- let ty = self.ecx.ty(self.fmtsp, ty);
- let st = ast::ItemStatic(ty, ast::MutImmutable, fmt);
let static_str_name = self.ecx.ident_of("__STATIC_FMTSTR");
- let item = self.ecx.item(self.fmtsp, static_str_name,
- self.static_attrs(), st);
- let decl = respan(self.fmtsp, ast::DeclItem(item));
- lets.push(box(GC) respan(self.fmtsp,
- ast::StmtDecl(box(GC) decl, ast::DUMMY_NODE_ID)));
-
- // Then, build up the static array which will become our precompiled
- // format "string"
- let fmt = self.ecx.expr_vec(self.fmtsp, self.pieces.clone());
- let piece_ty = self.ecx.ty_path(self.ecx.path_all(
+ let static_lifetime = self.ecx.lifetime(self.fmtsp, self.ecx.ident_of("'static").name);
+ let piece_ty = self.ecx.ty_rptr(
self.fmtsp,
- true, vec!(
- self.ecx.ident_of("std"),
- self.ecx.ident_of("fmt"),
- self.ecx.ident_of("rt"),
- self.ecx.ident_of("Argument")),
- vec!(self.ecx.lifetime(self.fmtsp,
- self.ecx.ident_of("'static").name)),
- Vec::new()
- ), None);
- let ty = ast::TyFixedLengthVec(
- piece_ty,
- self.ecx.expr_uint(self.fmtsp, self.pieces.len())
- );
- let ty = self.ecx.ty(self.fmtsp, ty);
- let st = ast::ItemStatic(ty, ast::MutImmutable, fmt);
+ self.ecx.ty_ident(self.fmtsp, self.ecx.ident_of("str")),
+ Some(static_lifetime),
+ ast::MutImmutable);
+ lets.push(box(GC) self.item_static_array(static_str_name,
+ piece_ty,
+ self.str_pieces.clone()));
+
+ // Then, build up the static array which will store our precompiled
+ // nonstandard placeholders, if there are any.
let static_args_name = self.ecx.ident_of("__STATIC_FMTARGS");
- let item = self.ecx.item(self.fmtsp, static_args_name,
- self.static_attrs(), st);
- let decl = respan(self.fmtsp, ast::DeclItem(item));
- lets.push(box(GC) respan(self.fmtsp,
- ast::StmtDecl(box(GC) decl, ast::DUMMY_NODE_ID)));
+ if !self.all_pieces_simple {
+ let piece_ty = self.ecx.ty_path(self.ecx.path_all(
+ self.fmtsp,
+ true, self.rtpath("Argument"),
+ vec![static_lifetime],
+ vec![]
+ ), None);
+ lets.push(box(GC) self.item_static_array(static_args_name,
+ piece_ty,
+ self.pieces.clone()));
+ }
// Right now there is a bug such that for the expression:
// foo(bar(&1))
// Now create the fmt::Arguments struct with all our locals we created.
let pieces = self.ecx.expr_ident(self.fmtsp, static_str_name);
- let fmt = self.ecx.expr_ident(self.fmtsp, static_args_name);
let args_slice = self.ecx.expr_ident(self.fmtsp, slicename);
+
+ let (fn_name, fn_args) = if self.all_pieces_simple {
+ ("new", vec![pieces, args_slice])
+ } else {
+ let fmt = self.ecx.expr_ident(self.fmtsp, static_args_name);
+ ("with_placeholders", vec![pieces, fmt, args_slice])
+ };
+
let result = self.ecx.expr_call_global(self.fmtsp, vec!(
self.ecx.ident_of("std"),
self.ecx.ident_of("fmt"),
self.ecx.ident_of("Arguments"),
- self.ecx.ident_of("new")), vec!(pieces, fmt, args_slice));
+ self.ecx.ident_of(fn_name)), fn_args);
// We did all the work of making sure that the arguments
// structure is safe, so we can safely have an unsafe block.
literal: String::new(),
pieces: Vec::new(),
str_pieces: Vec::new(),
+ all_pieces_simple: true,
method_statics: Vec::new(),
fmtsp: sp,
};