Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ergonomic format_args! #33642

Merged
merged 10 commits into from
Jul 13, 2016
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