/// Arguments structure. The compiler inserts an `unsafe` block to call this,
/// which is valid because the compiler performs all necessary validation to
/// ensure that the resulting call to format/write would be safe.
+ #[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),
+ args: args
+ }
+ }
+
+ #[cfg(stage0)]
#[doc(hidden)] #[inline]
pub unsafe fn new<'a>(fmt: &'static [rt::Piece<'static>],
args: &'a [Argument<'a>]) -> Arguments<'a> {
/// and pass it to a function or closure, passed as the first argument. The
/// macro validates the format string at compile-time so usage of the `write`
/// and `format` functions can be safely performed.
+#[cfg(not(stage0))]
+pub struct Arguments<'a> {
+ pieces: &'a [&'a str],
+ fmt: &'a [rt::Argument<'a>],
+ args: &'a [Argument<'a>],
+}
+
+#[cfg(stage0)] #[doc(hidden)]
pub struct Arguments<'a> {
fmt: &'a [rt::Piece<'a>],
args: &'a [Argument<'a>],
///
/// * output - the buffer to write output to
/// * args - the precompiled arguments generated by `format_args!`
+#[cfg(not(stage0))]
+pub fn write(output: &mut FormatWriter, args: &Arguments) -> Result {
+ let mut formatter = Formatter {
+ flags: 0,
+ width: None,
+ precision: None,
+ buf: output,
+ align: rt::AlignUnknown,
+ fill: ' ',
+ args: args.args,
+ curarg: args.args.iter(),
+ };
+
+ 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 pieces.next() {
+ Some(piece) => {
+ try!(formatter.buf.write(piece.as_bytes()));
+ }
+ None => {}
+ }
+
+ Ok(())
+}
+
+#[cfg(stage0)] #[doc(hidden)]
pub fn write(output: &mut FormatWriter, args: &Arguments) -> Result {
let mut formatter = Formatter {
flags: 0,
// First up is the collection of functions used to execute a format string
// at runtime. This consumes all of the compile-time statics generated by
// the format! syntax extension.
+ #[cfg(not(stage0))]
+ fn run(&mut self, arg: &rt::Argument) -> Result {
+ // Fill in the format parameters into the formatter
+ self.fill = arg.format.fill;
+ self.align = arg.format.align;
+ self.flags = arg.format.flags;
+ self.width = self.getcount(&arg.format.width);
+ self.precision = self.getcount(&arg.format.precision);
+
+ // Extract the correct argument
+ let value = match arg.position {
+ rt::ArgumentNext => { *self.curarg.next().unwrap() }
+ rt::ArgumentIs(i) => self.args[i],
+ };
+
+ // Then actually do some printing
+ (value.formatter)(value.value, self)
+ }
+ #[cfg(stage0)] #[doc(hidden)]
fn run(&mut self, piece: &rt::Piece) -> Result {
match *piece {
rt::String(s) => self.buf.write(s.as_bytes()),
name_types: HashMap<String, ArgumentType>,
name_ordering: Vec<String>,
- /// The latest consecutive literal strings
- literal: Option<String>,
+ /// The latest consecutive literal strings, or empty if there weren't any.
+ literal: String,
- /// Collection of the compiled `rt::Piece` structures
+ /// Collection of the compiled `rt::Argument` structures
pieces: Vec<Gc<ast::Expr>>,
+ /// Collection of string literals
+ str_pieces: Vec<Gc<ast::Expr>>,
name_positions: HashMap<String, uint>,
method_statics: Vec<Gc<ast::Item>>,
}
}
- /// Translate the accumulated string literals to a static `rt::Piece`
- fn trans_literal_string(&mut self) -> Option<Gc<ast::Expr>> {
+ /// Translate the accumulated string literals to a literal expression
+ fn trans_literal_string(&mut self) -> Gc<ast::Expr> {
let sp = self.fmtsp;
- self.literal.take().map(|s| {
- let s = token::intern_and_get_ident(s.as_slice());
- self.ecx.expr_call_global(sp,
- self.rtpath("String"),
- vec!(
- self.ecx.expr_str(sp, s)
- ))
- })
+ let s = token::intern_and_get_ident(self.literal.as_slice());
+ self.literal.clear();
+ self.ecx.expr_str(sp, s)
}
- /// Translate a `parse::Piece` to a static `rt::Piece`
+ /// 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) => {
- match self.literal {
- Some(ref mut sb) => sb.push_str(s),
- ref mut empty => *empty = Some(String::from_str(s)),
- }
+ self.literal.push_str(s);
None
}
parse::Argument(ref arg) => {
self.ecx.field_imm(sp, self.ecx.ident_of("width"), width)));
let path = self.ecx.path_global(sp, self.rtpath("Argument"));
- let s = self.ecx.expr_struct(sp, path, vec!(
+ Some(self.ecx.expr_struct(sp, path, vec!(
self.ecx.field_imm(sp, self.ecx.ident_of("position"), pos),
- self.ecx.field_imm(sp, self.ecx.ident_of("format"), fmt)));
- Some(self.ecx.expr_call_global(sp, self.rtpath("Argument"), vec!(s)))
+ self.ecx.field_imm(sp, self.ecx.ident_of("format"), fmt))))
}
}
}
// 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(
self.fmtsp,
self.ecx.ident_of("std"),
self.ecx.ident_of("fmt"),
self.ecx.ident_of("rt"),
- self.ecx.ident_of("Piece")),
+ self.ecx.ident_of("Argument")),
vec!(self.ecx.lifetime(self.fmtsp,
self.ecx.ident_of("'static").name)),
Vec::new()
);
let ty = self.ecx.ty(self.fmtsp, ty);
let st = ast::ItemStatic(ty, ast::MutImmutable, fmt);
- let static_name = self.ecx.ident_of("__STATIC_FMTSTR");
- let item = self.ecx.item(self.fmtsp, static_name,
+ 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,
}
// Now create the fmt::Arguments struct with all our locals we created.
- let fmt = self.ecx.expr_ident(self.fmtsp, static_name);
+ 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 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!(fmt, args_slice));
+ self.ecx.ident_of("new")), vec!(pieces, fmt, args_slice));
// We did all the work of making sure that the arguments
// structure is safe, so we can safely have an unsafe block.
name_ordering: name_ordering,
nest_level: 0,
next_arg: 0,
- literal: None,
+ literal: String::new(),
pieces: Vec::new(),
+ str_pieces: Vec::new(),
method_statics: Vec::new(),
fmtsp: sp,
};
cx.verify_piece(&piece);
match cx.trans_piece(&piece) {
Some(piece) => {
- cx.trans_literal_string().map(|piece|
- cx.pieces.push(piece));
+ let s = cx.trans_literal_string();
+ cx.str_pieces.push(s);
cx.pieces.push(piece);
}
None => {}
}
None => {}
}
- cx.trans_literal_string().map(|piece| cx.pieces.push(piece));
+ if !cx.literal.is_empty() {
+ let s = cx.trans_literal_string();
+ cx.str_pieces.push(s);
+ }
// Make sure that all arguments were used and all arguments have types.
for (i, ty) in cx.arg_types.iter().enumerate() {