Skip to content

Commit

Permalink
Decouple string and argument pieces
Browse files Browse the repository at this point in the history
  • Loading branch information
pczarn committed Sep 9, 2014
1 parent 6f34760 commit 696367f
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 45 deletions.
71 changes: 71 additions & 0 deletions src/libcore/fmt/mod.rs
Expand Up @@ -113,6 +113,19 @@ impl<'a> Arguments<'a> {
/// 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> {
Expand All @@ -129,6 +142,14 @@ impl<'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>],
Expand Down Expand Up @@ -263,6 +284,37 @@ uniform_fn_call_workaround! {
///
/// * 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,
Expand All @@ -285,7 +337,26 @@ impl<'a> Formatter<'a> {
// 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()),
Expand Down
2 changes: 1 addition & 1 deletion src/libcore/fmt/rt.rs
Expand Up @@ -14,7 +14,7 @@
//! These definitions are similar to their `ct` equivalents, but differ in that
//! these can be statically allocated and are slightly optimized for the runtime


#[cfg(stage0)]
#[doc(hidden)]
pub enum Piece<'a> {
String(&'a str),
Expand Down
84 changes: 54 additions & 30 deletions src/libsyntax/ext/format.rs
Expand Up @@ -49,11 +49,13 @@ struct Context<'a, 'b:'a> {
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>>,

Expand Down Expand Up @@ -370,28 +372,22 @@ impl<'a, 'b> Context<'a, 'b> {
}
}

/// 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) => {
Expand Down Expand Up @@ -450,10 +446,9 @@ impl<'a, 'b> Context<'a, 'b> {
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))))
}
}
}
Expand All @@ -476,14 +471,38 @@ 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(
self.fmtsp,
true, vec!(
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()
Expand All @@ -494,8 +513,8 @@ impl<'a, 'b> Context<'a, 'b> {
);
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,
Expand Down Expand Up @@ -545,13 +564,14 @@ impl<'a, 'b> Context<'a, 'b> {
}

// 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.
Expand Down Expand Up @@ -718,8 +738,9 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
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,
};
Expand All @@ -739,8 +760,8 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
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 => {}
Expand All @@ -758,7 +779,10 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
}
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() {
Expand Down
22 changes: 8 additions & 14 deletions src/test/pretty/issue-4264.pp
Expand Up @@ -39,26 +39,20 @@
() => {
#[inline]
#[allow(dead_code)]
static __STATIC_FMTSTR:
[::std::fmt::rt::Piece<'static>, ..(1u as uint)] =
([((::std::fmt::rt::String as
fn(&'static str) -> core::fmt::rt::Piece<'static>)(("test"
as
&'static str))
as core::fmt::rt::Piece<'static>)] as
[core::fmt::rt::Piece<'static>, .. 1]);
static __STATIC_FMTSTR: [&'static str, ..(1u as uint)] =
([("test" as &'static str)] as [&'static str, .. 1]);
let __args_vec =
(&([] as [core::fmt::Argument<'_>, .. 0]) as
&[core::fmt::Argument<'_>, .. 0]);
let __args =
(unsafe {
((::std::fmt::Arguments::new as
unsafe fn(&'static [core::fmt::rt::Piece<'static>], &'a [core::fmt::Argument<'a>]) -> core::fmt::Arguments<'a>)((__STATIC_FMTSTR
as
[core::fmt::rt::Piece<'static>, .. 1]),
(__args_vec
as
&[core::fmt::Argument<'_>, .. 0]))
unsafe fn(&'static [&'static str], &'a [core::fmt::Argument<'a>]) -> core::fmt::Arguments<'a>)((__STATIC_FMTSTR
as
[&'static str, .. 1]),
(__args_vec
as
&[core::fmt::Argument<'_>, .. 0]))
as core::fmt::Arguments<'_>)
} as core::fmt::Arguments<'_>);
Expand Down

0 comments on commit 696367f

Please sign in to comment.