Skip to content

Commit fc06f79

Browse files
committed
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
1 parent a84c299 commit fc06f79

File tree

5 files changed

+155
-23
lines changed

5 files changed

+155
-23
lines changed

src/libstd/fmt/mod.rs

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -647,21 +647,6 @@ impl<'self> Formatter<'self> {
647647
// the format! syntax extension.
648648

649649
fn run(&mut self, piece: &rt::Piece, cur: Option<&str>) {
650-
let setcount = |slot: &mut Option<uint>, cnt: &parse::Count| {
651-
match *cnt {
652-
parse::CountIs(n) => { *slot = Some(n); }
653-
parse::CountImplied => { *slot = None; }
654-
parse::CountIsParam(i) => {
655-
let v = self.args[i].value;
656-
unsafe { *slot = Some(*(v as *util::Void as *uint)); }
657-
}
658-
parse::CountIsNextParam => {
659-
let v = self.curarg.next().unwrap().value;
660-
unsafe { *slot = Some(*(v as *util::Void as *uint)); }
661-
}
662-
}
663-
};
664-
665650
match *piece {
666651
rt::String(s) => { self.buf.write(s.as_bytes()); }
667652
rt::CurrentArgument(()) => { self.buf.write(cur.unwrap().as_bytes()); }
@@ -670,8 +655,8 @@ impl<'self> Formatter<'self> {
670655
self.fill = arg.format.fill;
671656
self.align = arg.format.align;
672657
self.flags = arg.format.flags;
673-
setcount(&mut self.width, &arg.format.width);
674-
setcount(&mut self.precision, &arg.format.precision);
658+
self.width = self.getcount(&arg.format.width);
659+
self.precision = self.getcount(&arg.format.precision);
675660

676661
// Extract the correct argument
677662
let value = match arg.position {
@@ -688,6 +673,39 @@ impl<'self> Formatter<'self> {
688673
}
689674
}
690675

676+
#[cfg(stage0)]
677+
fn getcount(&mut self, cnt: &parse::Count) -> Option<uint> {
678+
match *cnt {
679+
parse::CountIs(n) => { Some(n) }
680+
parse::CountImplied => { None }
681+
parse::CountIsParam(i) => {
682+
let v = self.args[i].value;
683+
unsafe { Some(*(v as *util::Void as *uint)) }
684+
}
685+
parse::CountIsNextParam => {
686+
let v = self.curarg.next().unwrap().value;
687+
unsafe { Some(*(v as *util::Void as *uint)) }
688+
}
689+
parse::CountIsName(*) => unreachable!()
690+
}
691+
}
692+
693+
#[cfg(not(stage0))]
694+
fn getcount(&mut self, cnt: &rt::Count) -> Option<uint> {
695+
match *cnt {
696+
rt::CountIs(n) => { Some(n) }
697+
rt::CountImplied => { None }
698+
rt::CountIsParam(i) => {
699+
let v = self.args[i].value;
700+
unsafe { Some(*(v as *util::Void as *uint)) }
701+
}
702+
rt::CountIsNextParam => {
703+
let v = self.curarg.next().unwrap().value;
704+
unsafe { Some(*(v as *util::Void as *uint)) }
705+
}
706+
}
707+
}
708+
691709
fn execute(&mut self, method: &rt::Method, arg: Argument) {
692710
match *method {
693711
// Pluralization is selection upon a numeric value specified as the

src/libstd/fmt/parse.rs

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ pub struct Argument<'self> {
4848

4949
/// Specification for the formatting of an argument in the format string.
5050
#[deriving(Eq)]
51+
#[cfg(stage0)]
5152
pub struct FormatSpec<'self> {
5253
/// Optionally specified character to fill alignment with
5354
fill: Option<char>,
@@ -65,6 +66,26 @@ pub struct FormatSpec<'self> {
6566
ty: &'self str
6667
}
6768

69+
/// Specification for the formatting of an argument in the format string.
70+
#[deriving(Eq)]
71+
#[cfg(not(stage0))]
72+
pub struct FormatSpec<'self> {
73+
/// Optionally specified character to fill alignment with
74+
fill: Option<char>,
75+
/// Optionally specified alignment
76+
align: Alignment,
77+
/// Packed version of various flags provided
78+
flags: uint,
79+
/// The integer precision to use
80+
precision: Count<'self>,
81+
/// The string width requested for the resulting format
82+
width: Count<'self>,
83+
/// The descriptor string representing the name of the format desired for
84+
/// this argument, this can be empty or any number of characters, although
85+
/// it is required to be one word.
86+
ty: &'self str
87+
}
88+
6889
/// Enum describing where an argument for a format can be located.
6990
#[deriving(Eq)]
7091
#[allow(missing_doc)]
@@ -92,9 +113,22 @@ pub enum Flag {
92113
/// can reference either an argument or a literal integer.
93114
#[deriving(Eq)]
94115
#[allow(missing_doc)]
116+
#[cfg(stage0)]
95117
pub enum Count {
96118
CountIs(uint),
97119
CountIsParam(uint),
120+
CountIsName(&'static str), // not actually used, see stage1
121+
CountIsNextParam,
122+
CountImplied,
123+
}
124+
125+
#[deriving(Eq)]
126+
#[allow(missing_doc)]
127+
#[cfg(not(stage0))]
128+
pub enum Count<'self> {
129+
CountIs(uint),
130+
CountIsName(&'self str),
131+
CountIsParam(uint),
98132
CountIsNextParam,
99133
CountImplied,
100134
}
@@ -344,10 +378,22 @@ impl<'self> Parser<'self> {
344378
spec.flags |= 1 << (FlagAlternate as uint);
345379
}
346380
// Width and precision
381+
let mut havewidth = false;
347382
if self.consume('0') {
348-
spec.flags |= 1 << (FlagSignAwareZeroPad as uint);
383+
// small ambiguity with '0$' as a format string. In theory this is a
384+
// '0' flag and then an ill-formatted format string with just a '$'
385+
// and no count, but this is better if we instead interpret this as
386+
// no '0' flag and '0$' as the width instead.
387+
if self.consume('$') {
388+
spec.width = CountIsParam(0);
389+
havewidth = true;
390+
} else {
391+
spec.flags |= 1 << (FlagSignAwareZeroPad as uint);
392+
}
393+
}
394+
if !havewidth {
395+
spec.width = self.count();
349396
}
350-
spec.width = self.count();
351397
if self.consume('.') {
352398
if self.consume('*') {
353399
spec.precision = CountIsNextParam;
@@ -548,6 +594,7 @@ impl<'self> Parser<'self> {
548594
/// Parses a Count parameter at the current position. This does not check
549595
/// for 'CountIsNextParam' because that is only used in precision, not
550596
/// width.
597+
#[cfg(stage0)]
551598
fn count(&mut self) -> Count {
552599
match self.integer() {
553600
Some(i) => {
@@ -560,6 +607,30 @@ impl<'self> Parser<'self> {
560607
None => { CountImplied }
561608
}
562609
}
610+
#[cfg(not(stage0))]
611+
fn count(&mut self) -> Count<'self> {
612+
match self.integer() {
613+
Some(i) => {
614+
if self.consume('$') {
615+
CountIsParam(i)
616+
} else {
617+
CountIs(i)
618+
}
619+
}
620+
None => {
621+
let tmp = self.cur.clone();
622+
match self.word() {
623+
word if word.len() > 0 && self.consume('$') => {
624+
CountIsName(word)
625+
}
626+
_ => {
627+
self.cur = tmp;
628+
CountImplied
629+
}
630+
}
631+
}
632+
}
633+
}
563634

564635
/// Parses a word starting at the current position. A word is considered to
565636
/// be an alphabetic character followed by any number of alphanumeric
@@ -783,6 +854,18 @@ mod tests {
783854
},
784855
method: None,
785856
})]);
857+
same("{:a$.b$s}", ~[Argument(Argument {
858+
position: ArgumentNext,
859+
format: FormatSpec {
860+
fill: None,
861+
align: AlignUnknown,
862+
flags: 0,
863+
precision: CountIsName("b"),
864+
width: CountIsName("a"),
865+
ty: "s",
866+
},
867+
method: None,
868+
})]);
786869
}
787870
#[test]
788871
fn format_flags() {

src/libstd/fmt/rt.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ pub struct Argument<'self> {
3434
method: Option<&'self Method<'self>>
3535
}
3636

37+
#[cfg(stage0)]
3738
pub struct FormatSpec {
3839
fill: char,
3940
align: parse::Alignment,
@@ -42,6 +43,20 @@ pub struct FormatSpec {
4243
width: parse::Count,
4344
}
4445

46+
#[cfg(not(stage0))]
47+
pub struct FormatSpec {
48+
fill: char,
49+
align: parse::Alignment,
50+
flags: uint,
51+
precision: Count,
52+
width: Count,
53+
}
54+
55+
#[cfg(not(stage0))]
56+
pub enum Count {
57+
CountIs(uint), CountIsParam(uint), CountIsNextParam, CountImplied,
58+
}
59+
4560
pub enum Position {
4661
ArgumentNext, ArgumentIs(uint)
4762
}

src/libsyntax/ext/format.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@ impl Context {
177177
parse::CountIsParam(i) => {
178178
self.verify_arg_type(Left(i), Unsigned);
179179
}
180+
parse::CountIsName(s) => {
181+
self.verify_arg_type(Right(s.to_managed()), Unsigned);
182+
}
180183
parse::CountIsNextParam => {
181184
if self.check_positional_ok() {
182185
self.verify_arg_type(Left(self.next_arg), Unsigned);
@@ -361,21 +364,31 @@ impl Context {
361364
let trans_count = |c: parse::Count| {
362365
match c {
363366
parse::CountIs(i) => {
364-
self.ecx.expr_call_global(sp, ctpath("CountIs"),
367+
self.ecx.expr_call_global(sp, rtpath("CountIs"),
365368
~[self.ecx.expr_uint(sp, i)])
366369
}
367370
parse::CountIsParam(i) => {
368-
self.ecx.expr_call_global(sp, ctpath("CountIsParam"),
371+
self.ecx.expr_call_global(sp, rtpath("CountIsParam"),
369372
~[self.ecx.expr_uint(sp, i)])
370373
}
371374
parse::CountImplied => {
372-
let path = self.ecx.path_global(sp, ctpath("CountImplied"));
375+
let path = self.ecx.path_global(sp, rtpath("CountImplied"));
373376
self.ecx.expr_path(path)
374377
}
375378
parse::CountIsNextParam => {
376-
let path = self.ecx.path_global(sp, ctpath("CountIsNextParam"));
379+
let path = self.ecx.path_global(sp, rtpath("CountIsNextParam"));
377380
self.ecx.expr_path(path)
378381
}
382+
parse::CountIsName(n) => {
383+
let n = n.to_managed();
384+
let i = match self.name_positions.find_copy(&n) {
385+
Some(i) => i,
386+
None => 0, // error already emitted elsewhere
387+
};
388+
let i = i + self.args.len();
389+
self.ecx.expr_call_global(sp, rtpath("CountIsParam"),
390+
~[self.ecx.expr_uint(sp, i)])
391+
}
379392
}
380393
};
381394
let trans_method = |method: &parse::Method| {

src/test/run-pass/ifmt.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,10 @@ pub fn main() {
119119
t!(format!("{:0>2s}", "a"), "0a");
120120
t!(format!("{:.*s}", 4, "aaaaaaaaaaaaaaaaaa"), "aaaa");
121121
t!(format!("{:.1$s}", "aaaaaaaaaaaaaaaaaa", 4), "aaaa");
122+
t!(format!("{:.a$s}", "aaaaaaaaaaaaaaaaaa", a=4), "aaaa");
122123
t!(format!("{:1$s}", "a", 4), "a ");
124+
t!(format!("{1:0$s}", 4, "a"), "a ");
125+
t!(format!("{:a$s}", "a", a=4), "a ");
123126
t!(format!("{:-#s}", "a"), "a");
124127
t!(format!("{:+#s}", "a"), "a");
125128

0 commit comments

Comments
 (0)