]> git.lizzy.rs Git - rust.git/commitdiff
Decouple string and argument pieces
authorPiotr Czarnecki <pioczarn@gmail.com>
Thu, 21 Aug 2014 13:34:00 +0000 (14:34 +0100)
committerPiotr Czarnecki <pioczarn@gmail.com>
Tue, 9 Sep 2014 19:34:41 +0000 (20:34 +0100)
src/libcore/fmt/mod.rs
src/libcore/fmt/rt.rs
src/libsyntax/ext/format.rs
src/test/pretty/issue-4264.pp

index 7c4494358b1bf8055d281e981f9cd04cfa24f217..9f64e1aa4928ef7ff3137f380e07046606af53bd 100644 (file)
@@ -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> {
@@ -129,6 +142,14 @@ pub unsafe fn new<'a>(fmt: &'static [rt::Piece<'static>],
 /// 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>],
@@ -263,6 +284,37 @@ pub fn $name<T: $trait_>(x: &T, fmt: &mut Formatter) -> Result {
 ///
 ///   * 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,
@@ -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()),
index 1f5449130ecf34b94b0787154cc7a9acf6a44299..59fbde88d8be3ad9e1979e250334e0ba8c44b4c0 100644 (file)
@@ -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),
index b3bb01e1d0409881735cc831a04f895e74edbdfe..fc4d8c83c514e24f7b073c4a188e576e65b20928 100644 (file)
@@ -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>>,
 
@@ -370,28 +372,22 @@ fn trans_count(&self, c: parse::Count) -> Gc<ast::Expr> {
         }
     }
 
-    /// 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) => {
@@ -450,10 +446,9 @@ fn trans_piece(&mut self, piece: &parse::Piece) -> Option<Gc<ast::Expr>> {
                     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))))
             }
         }
     }
@@ -476,6 +471,30 @@ 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(
                 self.fmtsp,
@@ -483,7 +502,7 @@ fn to_expr(&self, invocation: Invocation) -> Gc<ast::Expr> {
                     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()
@@ -494,8 +513,8 @@ fn to_expr(&self, invocation: Invocation) -> Gc<ast::Expr> {
         );
         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,
@@ -545,13 +564,14 @@ fn to_expr(&self, invocation: Invocation) -> Gc<ast::Expr> {
         }
 
         // 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.
@@ -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,
     };
@@ -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 => {}
@@ -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() {
index 86e394e5408781e0120a3bbb32378fa46840157d..376a3a38fda1ed3d566a8dc2550ff1b6008f37e5 100644 (file)
@@ -39,26 +39,20 @@ pub fn bar() {
          () => {
              #[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<'_>);