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
This commit is contained in:
parent
a84c2999c9
commit
fc06f7922d
@ -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
|
||||
|
@ -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() {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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| {
|
||||
|
@ -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");
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user