about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2013-10-12 20:00:58 -0700
committerAlex Crichton <alex@alexcrichton.com>2013-10-15 22:27:10 -0700
commitfc06f7922db0b4d1063f4f29157635117d853426 (patch)
treeab33cac38ef9012c89c84a28054eab7bd364cabc
parenta84c2999c91f6ea43291006288ea6dd8c4852c3b (diff)
downloadrust-fc06f7922db0b4d1063f4f29157635117d853426.tar.gz
rust-fc06f7922db0b4d1063f4f29157635117d853426.zip
Build a few extra features into format! parsing
* Allow named parameters to specify width/precision
* Intepret the format string '0$' as "width is the 0th argument" instead of
  thinking the lone '0' was the sign-aware-zero-padding flag. To get both you'd
  need to put '00$' which makes more sense if you want both to happen.

Closes #9669
-rw-r--r--src/libstd/fmt/mod.rs52
-rw-r--r--src/libstd/fmt/parse.rs87
-rw-r--r--src/libstd/fmt/rt.rs15
-rw-r--r--src/libsyntax/ext/format.rs21
-rw-r--r--src/test/run-pass/ifmt.rs3
5 files changed, 155 insertions, 23 deletions
diff --git a/src/libstd/fmt/mod.rs b/src/libstd/fmt/mod.rs
index a03f21d69c8..4032515f985 100644
--- a/src/libstd/fmt/mod.rs
+++ b/src/libstd/fmt/mod.rs
@@ -647,21 +647,6 @@ impl<'self> Formatter<'self> {
     // the format! syntax extension.
 
     fn run(&mut self, piece: &rt::Piece, cur: Option<&str>) {
-        let setcount = |slot: &mut Option<uint>, cnt: &parse::Count| {
-            match *cnt {
-                parse::CountIs(n) => { *slot = Some(n); }
-                parse::CountImplied => { *slot = None; }
-                parse::CountIsParam(i) => {
-                    let v = self.args[i].value;
-                    unsafe { *slot = Some(*(v as *util::Void as *uint)); }
-                }
-                parse::CountIsNextParam => {
-                    let v = self.curarg.next().unwrap().value;
-                    unsafe { *slot = Some(*(v as *util::Void as *uint)); }
-                }
-            }
-        };
-
         match *piece {
             rt::String(s) => { self.buf.write(s.as_bytes()); }
             rt::CurrentArgument(()) => { self.buf.write(cur.unwrap().as_bytes()); }
@@ -670,8 +655,8 @@ impl<'self> Formatter<'self> {
                 self.fill = arg.format.fill;
                 self.align = arg.format.align;
                 self.flags = arg.format.flags;
-                setcount(&mut self.width, &arg.format.width);
-                setcount(&mut self.precision, &arg.format.precision);
+                self.width = self.getcount(&arg.format.width);
+                self.precision = self.getcount(&arg.format.precision);
 
                 // Extract the correct argument
                 let value = match arg.position {
@@ -688,6 +673,39 @@ impl<'self> Formatter<'self> {
         }
     }
 
+    #[cfg(stage0)]
+    fn getcount(&mut self, cnt: &parse::Count) -> Option<uint> {
+        match *cnt {
+            parse::CountIs(n) => { Some(n) }
+            parse::CountImplied => { None }
+            parse::CountIsParam(i) => {
+                let v = self.args[i].value;
+                unsafe { Some(*(v as *util::Void as *uint)) }
+            }
+            parse::CountIsNextParam => {
+                let v = self.curarg.next().unwrap().value;
+                unsafe { Some(*(v as *util::Void as *uint)) }
+            }
+            parse::CountIsName(*) => unreachable!()
+        }
+    }
+
+    #[cfg(not(stage0))]
+    fn getcount(&mut self, cnt: &rt::Count) -> Option<uint> {
+        match *cnt {
+            rt::CountIs(n) => { Some(n) }
+            rt::CountImplied => { None }
+            rt::CountIsParam(i) => {
+                let v = self.args[i].value;
+                unsafe { Some(*(v as *util::Void as *uint)) }
+            }
+            rt::CountIsNextParam => {
+                let v = self.curarg.next().unwrap().value;
+                unsafe { Some(*(v as *util::Void as *uint)) }
+            }
+        }
+    }
+
     fn execute(&mut self, method: &rt::Method, arg: Argument) {
         match *method {
             // Pluralization is selection upon a numeric value specified as the
diff --git a/src/libstd/fmt/parse.rs b/src/libstd/fmt/parse.rs
index 1b8998b5c6c..11b869c930e 100644
--- a/src/libstd/fmt/parse.rs
+++ b/src/libstd/fmt/parse.rs
@@ -48,6 +48,7 @@ pub struct Argument<'self> {
 
 /// Specification for the formatting of an argument in the format string.
 #[deriving(Eq)]
+#[cfg(stage0)]
 pub struct FormatSpec<'self> {
     /// Optionally specified character to fill alignment with
     fill: Option<char>,
@@ -65,6 +66,26 @@ pub struct FormatSpec<'self> {
     ty: &'self str
 }
 
+/// Specification for the formatting of an argument in the format string.
+#[deriving(Eq)]
+#[cfg(not(stage0))]
+pub struct FormatSpec<'self> {
+    /// Optionally specified character to fill alignment with
+    fill: Option<char>,
+    /// Optionally specified alignment
+    align: Alignment,
+    /// Packed version of various flags provided
+    flags: uint,
+    /// The integer precision to use
+    precision: Count<'self>,
+    /// The string width requested for the resulting format
+    width: Count<'self>,
+    /// The descriptor string representing the name of the format desired for
+    /// this argument, this can be empty or any number of characters, although
+    /// it is required to be one word.
+    ty: &'self str
+}
+
 /// Enum describing where an argument for a format can be located.
 #[deriving(Eq)]
 #[allow(missing_doc)]
@@ -92,9 +113,22 @@ pub enum Flag {
 /// can reference either an argument or a literal integer.
 #[deriving(Eq)]
 #[allow(missing_doc)]
+#[cfg(stage0)]
 pub enum Count {
     CountIs(uint),
     CountIsParam(uint),
+    CountIsName(&'static str), // not actually used, see stage1
+    CountIsNextParam,
+    CountImplied,
+}
+
+#[deriving(Eq)]
+#[allow(missing_doc)]
+#[cfg(not(stage0))]
+pub enum Count<'self> {
+    CountIs(uint),
+    CountIsName(&'self str),
+    CountIsParam(uint),
     CountIsNextParam,
     CountImplied,
 }
@@ -344,10 +378,22 @@ impl<'self> Parser<'self> {
             spec.flags |= 1 << (FlagAlternate as uint);
         }
         // Width and precision
+        let mut havewidth = false;
         if self.consume('0') {
-            spec.flags |= 1 << (FlagSignAwareZeroPad as uint);
+            // small ambiguity with '0$' as a format string. In theory this is a
+            // '0' flag and then an ill-formatted format string with just a '$'
+            // and no count, but this is better if we instead interpret this as
+            // no '0' flag and '0$' as the width instead.
+            if self.consume('$') {
+                spec.width = CountIsParam(0);
+                havewidth = true;
+            } else {
+                spec.flags |= 1 << (FlagSignAwareZeroPad as uint);
+            }
+        }
+        if !havewidth {
+            spec.width = self.count();
         }
-        spec.width = self.count();
         if self.consume('.') {
             if self.consume('*') {
                 spec.precision = CountIsNextParam;
@@ -548,6 +594,7 @@ impl<'self> Parser<'self> {
     /// Parses a Count parameter at the current position. This does not check
     /// for 'CountIsNextParam' because that is only used in precision, not
     /// width.
+    #[cfg(stage0)]
     fn count(&mut self) -> Count {
         match self.integer() {
             Some(i) => {
@@ -560,6 +607,30 @@ impl<'self> Parser<'self> {
             None => { CountImplied }
         }
     }
+    #[cfg(not(stage0))]
+    fn count(&mut self) -> Count<'self> {
+        match self.integer() {
+            Some(i) => {
+                if self.consume('$') {
+                    CountIsParam(i)
+                } else {
+                    CountIs(i)
+                }
+            }
+            None => {
+                let tmp = self.cur.clone();
+                match self.word() {
+                    word if word.len() > 0 && self.consume('$') => {
+                        CountIsName(word)
+                    }
+                    _ => {
+                        self.cur = tmp;
+                        CountImplied
+                    }
+                }
+            }
+        }
+    }
 
     /// Parses a word starting at the current position. A word is considered to
     /// be an alphabetic character followed by any number of alphanumeric
@@ -783,6 +854,18 @@ mod tests {
             },
             method: None,
         })]);
+        same("{:a$.b$s}", ~[Argument(Argument {
+            position: ArgumentNext,
+            format: FormatSpec {
+                fill: None,
+                align: AlignUnknown,
+                flags: 0,
+                precision: CountIsName("b"),
+                width: CountIsName("a"),
+                ty: "s",
+            },
+            method: None,
+        })]);
     }
     #[test]
     fn format_flags() {
diff --git a/src/libstd/fmt/rt.rs b/src/libstd/fmt/rt.rs
index 90763836fc6..063d712dfa9 100644
--- a/src/libstd/fmt/rt.rs
+++ b/src/libstd/fmt/rt.rs
@@ -34,6 +34,7 @@ pub struct Argument<'self> {
     method: Option<&'self Method<'self>>
 }
 
+#[cfg(stage0)]
 pub struct FormatSpec {
     fill: char,
     align: parse::Alignment,
@@ -42,6 +43,20 @@ pub struct FormatSpec {
     width: parse::Count,
 }
 
+#[cfg(not(stage0))]
+pub struct FormatSpec {
+    fill: char,
+    align: parse::Alignment,
+    flags: uint,
+    precision: Count,
+    width: Count,
+}
+
+#[cfg(not(stage0))]
+pub enum Count {
+    CountIs(uint), CountIsParam(uint), CountIsNextParam, CountImplied,
+}
+
 pub enum Position {
     ArgumentNext, ArgumentIs(uint)
 }
diff --git a/src/libsyntax/ext/format.rs b/src/libsyntax/ext/format.rs
index 7518816be1e..31befed6c0c 100644
--- a/src/libsyntax/ext/format.rs
+++ b/src/libsyntax/ext/format.rs
@@ -177,6 +177,9 @@ impl Context {
             parse::CountIsParam(i) => {
                 self.verify_arg_type(Left(i), Unsigned);
             }
+            parse::CountIsName(s) => {
+                self.verify_arg_type(Right(s.to_managed()), Unsigned);
+            }
             parse::CountIsNextParam => {
                 if self.check_positional_ok() {
                     self.verify_arg_type(Left(self.next_arg), Unsigned);
@@ -361,21 +364,31 @@ impl Context {
         let trans_count = |c: parse::Count| {
             match c {
                 parse::CountIs(i) => {
-                    self.ecx.expr_call_global(sp, ctpath("CountIs"),
+                    self.ecx.expr_call_global(sp, rtpath("CountIs"),
                                               ~[self.ecx.expr_uint(sp, i)])
                 }
                 parse::CountIsParam(i) => {
-                    self.ecx.expr_call_global(sp, ctpath("CountIsParam"),
+                    self.ecx.expr_call_global(sp, rtpath("CountIsParam"),
                                               ~[self.ecx.expr_uint(sp, i)])
                 }
                 parse::CountImplied => {
-                    let path = self.ecx.path_global(sp, ctpath("CountImplied"));
+                    let path = self.ecx.path_global(sp, rtpath("CountImplied"));
                     self.ecx.expr_path(path)
                 }
                 parse::CountIsNextParam => {
-                    let path = self.ecx.path_global(sp, ctpath("CountIsNextParam"));
+                    let path = self.ecx.path_global(sp, rtpath("CountIsNextParam"));
                     self.ecx.expr_path(path)
                 }
+                parse::CountIsName(n) => {
+                    let n = n.to_managed();
+                    let i = match self.name_positions.find_copy(&n) {
+                        Some(i) => i,
+                        None => 0, // error already emitted elsewhere
+                    };
+                    let i = i + self.args.len();
+                    self.ecx.expr_call_global(sp, rtpath("CountIsParam"),
+                                              ~[self.ecx.expr_uint(sp, i)])
+                }
             }
         };
         let trans_method = |method: &parse::Method| {
diff --git a/src/test/run-pass/ifmt.rs b/src/test/run-pass/ifmt.rs
index 7611871b0e7..75d11eddb1b 100644
--- a/src/test/run-pass/ifmt.rs
+++ b/src/test/run-pass/ifmt.rs
@@ -119,7 +119,10 @@ pub fn main() {
     t!(format!("{:0>2s}", "a"), "0a");
     t!(format!("{:.*s}", 4, "aaaaaaaaaaaaaaaaaa"), "aaaa");
     t!(format!("{:.1$s}", "aaaaaaaaaaaaaaaaaa", 4), "aaaa");
+    t!(format!("{:.a$s}", "aaaaaaaaaaaaaaaaaa", a=4), "aaaa");
     t!(format!("{:1$s}", "a", 4), "a   ");
+    t!(format!("{1:0$s}", 4, "a"), "a   ");
+    t!(format!("{:a$s}", "a", a=4), "a   ");
     t!(format!("{:-#s}", "a"), "a");
     t!(format!("{:+#s}", "a"), "a");