Skip to content

Commit

Permalink
Auto merge of #33642 - xen0n:ergonomic-format-macro, r=alexcrichton
Browse files Browse the repository at this point in the history
Ergonomic format_args!

Fixes #9456 (at last).

Not a ground-up rewrite of the existing machinery, but more like an added intermediary layer between macro arguments and format placeholders. This is now implementing Rust RFC 1618!
  • Loading branch information
bors authored Jul 13, 2016
2 parents 0b7fb80 + 51e54e5 commit db71987
Show file tree
Hide file tree
Showing 5 changed files with 321 additions and 199 deletions.
62 changes: 42 additions & 20 deletions src/libfmt_macros/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,6 @@ pub struct FormatSpec<'a> {
/// Enum describing where an argument for a format can be located.
#[derive(Copy, Clone, PartialEq)]
pub enum Position<'a> {
/// The argument will be in the next position. This is the default.
ArgumentNext,
/// The argument is located at a specific index.
ArgumentIs(usize),
/// The argument has a name.
Expand Down Expand Up @@ -127,8 +125,6 @@ pub enum Count<'a> {
CountIsName(&'a str),
/// The count is specified by the argument at the given index.
CountIsParam(usize),
/// The count is specified by the next parameter.
CountIsNextParam,
/// The count is implied and cannot be explicitly specified.
CountImplied,
}
Expand All @@ -144,6 +140,8 @@ pub struct Parser<'a> {
cur: iter::Peekable<str::CharIndices<'a>>,
/// Error messages accumulated during parsing
pub errors: Vec<string::String>,
/// Current position of implicit positional argument pointer
curarg: usize,
}

impl<'a> Iterator for Parser<'a> {
Expand Down Expand Up @@ -186,6 +184,7 @@ impl<'a> Parser<'a> {
input: s,
cur: s.char_indices().peekable(),
errors: vec![],
curarg: 0,
}
}

Expand Down Expand Up @@ -259,21 +258,40 @@ impl<'a> Parser<'a> {
/// Parses an Argument structure, or what's contained within braces inside
/// the format string
fn argument(&mut self) -> Argument<'a> {
let pos = self.position();
let format = self.format();

// Resolve position after parsing format spec.
let pos = match pos {
Some(position) => position,
None => {
let i = self.curarg;
self.curarg += 1;
ArgumentIs(i)
}
};

Argument {
position: self.position(),
format: self.format(),
position: pos,
format: format,
}
}

/// Parses a positional argument for a format. This could either be an
/// integer index of an argument, a named argument, or a blank string.
fn position(&mut self) -> Position<'a> {
/// Returns `Some(parsed_position)` if the position is not implicitly
/// consuming a macro argument, `None` if it's the case.
fn position(&mut self) -> Option<Position<'a>> {
if let Some(i) = self.integer() {
ArgumentIs(i)
Some(ArgumentIs(i))
} else {
match self.cur.peek() {
Some(&(_, c)) if c.is_alphabetic() => ArgumentNamed(self.word()),
_ => ArgumentNext,
Some(&(_, c)) if c.is_alphabetic() => Some(ArgumentNamed(self.word())),

// This is an `ArgumentNext`.
// Record the fact and do the resolution after parsing the
// format spec, to make things like `{:.*}` work.
_ => None,
}
}
}
Expand Down Expand Up @@ -340,7 +358,11 @@ impl<'a> Parser<'a> {
}
if self.consume('.') {
if self.consume('*') {
spec.precision = CountIsNextParam;
// Resolve `CountIsNextParam`.
// We can do this immediately as `position` is resolved later.
let i = self.curarg;
self.curarg += 1;
spec.precision = CountIsParam(i);
} else {
spec.precision = self.count();
}
Expand Down Expand Up @@ -487,7 +509,7 @@ mod tests {
fn format_nothing() {
same("{}",
&[NextArgument(Argument {
position: ArgumentNext,
position: ArgumentIs(0),
format: fmtdflt(),
})]);
}
Expand Down Expand Up @@ -565,7 +587,7 @@ mod tests {
fn format_counts() {
same("{:10s}",
&[NextArgument(Argument {
position: ArgumentNext,
position: ArgumentIs(0),
format: FormatSpec {
fill: None,
align: AlignUnknown,
Expand All @@ -577,7 +599,7 @@ mod tests {
})]);
same("{:10$.10s}",
&[NextArgument(Argument {
position: ArgumentNext,
position: ArgumentIs(0),
format: FormatSpec {
fill: None,
align: AlignUnknown,
Expand All @@ -589,19 +611,19 @@ mod tests {
})]);
same("{:.*s}",
&[NextArgument(Argument {
position: ArgumentNext,
position: ArgumentIs(1),
format: FormatSpec {
fill: None,
align: AlignUnknown,
flags: 0,
precision: CountIsNextParam,
precision: CountIsParam(0),
width: CountImplied,
ty: "s",
},
})]);
same("{:.10$s}",
&[NextArgument(Argument {
position: ArgumentNext,
position: ArgumentIs(0),
format: FormatSpec {
fill: None,
align: AlignUnknown,
Expand All @@ -613,7 +635,7 @@ mod tests {
})]);
same("{:a$.b$s}",
&[NextArgument(Argument {
position: ArgumentNext,
position: ArgumentIs(0),
format: FormatSpec {
fill: None,
align: AlignUnknown,
Expand All @@ -628,7 +650,7 @@ mod tests {
fn format_flags() {
same("{:-}",
&[NextArgument(Argument {
position: ArgumentNext,
position: ArgumentIs(0),
format: FormatSpec {
fill: None,
align: AlignUnknown,
Expand All @@ -640,7 +662,7 @@ mod tests {
})]);
same("{:+#}",
&[NextArgument(Argument {
position: ArgumentNext,
position: ArgumentIs(0),
format: FormatSpec {
fill: None,
align: AlignUnknown,
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -881,7 +881,7 @@ fn check_on_unimplemented<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
}
},
// `{:1}` and `{}` are not to be used
Position::ArgumentIs(_) | Position::ArgumentNext => {
Position::ArgumentIs(_) => {
span_err!(ccx.tcx.sess, attr.span, E0231,
"only named substitution \
parameters are allowed");
Expand Down
Loading

0 comments on commit db71987

Please sign in to comment.