diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index 9f64e1aa492..be75bfec32c 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -116,11 +116,25 @@ impl<'a> Arguments<'a> { #[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 } } @@ -144,8 +158,14 @@ impl<'a> Arguments<'a> { /// 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>], } @@ -276,6 +296,18 @@ uniform_fn_call_workaround! { 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. @@ -299,11 +331,25 @@ pub fn write(output: &mut FormatWriter, args: &Arguments) -> Result { 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())); diff --git a/src/libsyntax/ext/format.rs b/src/libsyntax/ext/format.rs index fc4d8c83c51..0bb32c73ca2 100644 --- a/src/libsyntax/ext/format.rs +++ b/src/libsyntax/ext/format.rs @@ -56,6 +56,9 @@ struct Context<'a, 'b:'a> { pieces: Vec>, /// Collection of string literals str_pieces: Vec>, + /// Stays `true` if all formatting parameters are default (as in "{}{}"). + all_pieces_simple: bool, + name_positions: HashMap, method_statics: Vec>, @@ -383,7 +386,6 @@ impl<'a, 'b> Context<'a, 'b> { /// Translate a `parse::Piece` to a static `rt::Argument` or append /// to the `literal` string. fn trans_piece(&mut self, piece: &parse::Piece) -> Option> { - // let mut is_not_default = true; let sp = self.fmtsp; match *piece { parse::String(s) => { @@ -416,8 +418,25 @@ impl<'a, 'b> Context<'a, 'b> { } }; - // 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 => { @@ -453,6 +472,26 @@ impl<'a, 'b> Context<'a, 'b> { } } + fn item_static_array(&self, + name: ast::Ident, + piece_ty: Gc, + pieces: Vec>) + -> 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 { @@ -471,54 +510,31 @@ impl<'a, 'b> Context<'a, 'b> { // 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)) @@ -565,13 +581,20 @@ impl<'a, 'b> Context<'a, 'b> { // 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. @@ -741,6 +764,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span, literal: String::new(), pieces: Vec::new(), str_pieces: Vec::new(), + all_pieces_simple: true, method_statics: Vec::new(), fmtsp: sp, };