Skip to content

Use Symbol, Span in libfmt_macros #61568

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

Merged
merged 7 commits into from
Jun 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,9 @@ dependencies = [
[[package]]
name = "fmt_macros"
version = "0.0.0"
dependencies = [
"syntax_pos 0.0.0",
]

[[package]]
name = "fnv"
Expand Down
3 changes: 3 additions & 0 deletions src/libfmt_macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ edition = "2018"
name = "fmt_macros"
path = "lib.rs"
crate-type = ["dylib"]

[dependencies]
syntax_pos = { path = "../libsyntax_pos" }
133 changes: 67 additions & 66 deletions src/libfmt_macros/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ use std::str;
use std::string;
use std::iter;

use syntax_pos::{InnerSpan, Symbol};

#[derive(Copy, Clone)]
struct InnerOffset(usize);

impl InnerOffset {
fn to(self, end: InnerOffset) -> InnerSpan {
InnerSpan::new(self.0, end.0)
}
}

/// A piece is a portion of the format string which represents the next part
/// to emit. These are emitted as a stream by the `Parser` class.
#[derive(Copy, Clone, PartialEq)]
Expand All @@ -39,7 +50,7 @@ pub enum Piece<'a> {
#[derive(Copy, Clone, PartialEq)]
pub struct Argument<'a> {
/// Where to find this argument
pub position: Position<'a>,
pub position: Position,
/// How to format the argument
pub format: FormatSpec<'a>,
}
Expand All @@ -54,9 +65,9 @@ pub struct FormatSpec<'a> {
/// Packed version of various flags provided
pub flags: u32,
/// The integer precision to use
pub precision: Count<'a>,
pub precision: Count,
/// The string width requested for the resulting format
pub width: Count<'a>,
pub width: Count,
/// 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.
Expand All @@ -65,16 +76,16 @@ pub struct FormatSpec<'a> {

/// Enum describing where an argument for a format can be located.
#[derive(Copy, Clone, PartialEq)]
pub enum Position<'a> {
pub enum Position {
/// The argument is implied to be located at an index
ArgumentImplicitlyIs(usize),
/// The argument is located at a specific index given in the format
ArgumentIs(usize),
/// The argument has a name.
ArgumentNamed(&'a str),
ArgumentNamed(Symbol),
}

impl Position<'_> {
impl Position {
pub fn index(&self) -> Option<usize> {
match self {
ArgumentIs(i) | ArgumentImplicitlyIs(i) => Some(*i),
Expand Down Expand Up @@ -119,11 +130,11 @@ pub enum Flag {
/// A count is used for the precision and width parameters of an integer, and
/// can reference either an argument or a literal integer.
#[derive(Copy, Clone, PartialEq)]
pub enum Count<'a> {
pub enum Count {
/// The count is specified explicitly.
CountIs(usize),
/// The count is specified by the argument with the given name.
CountIsName(&'a str),
CountIsName(Symbol),
/// The count is specified by the argument at the given index.
CountIsParam(usize),
/// The count is implied and cannot be explicitly specified.
Expand All @@ -134,9 +145,8 @@ pub struct ParseError {
pub description: string::String,
pub note: Option<string::String>,
pub label: string::String,
pub start: SpanIndex,
pub end: SpanIndex,
pub secondary_label: Option<(string::String, SpanIndex, SpanIndex)>,
pub span: InnerSpan,
pub secondary_label: Option<(string::String, InnerSpan)>,
}

/// The parser structure for interpreting the input format string. This is
Expand All @@ -155,44 +165,36 @@ pub struct Parser<'a> {
/// `Some(raw count)` when the string is "raw", used to position spans correctly
style: Option<usize>,
/// Start and end byte offset of every successfully parsed argument
pub arg_places: Vec<(SpanIndex, SpanIndex)>,
pub arg_places: Vec<InnerSpan>,
/// Characters that need to be shifted
skips: Vec<usize>,
/// Span offset of the last opening brace seen, used for error reporting
last_opening_brace_pos: Option<SpanIndex>,
/// Span of the last opening brace seen, used for error reporting
last_opening_brace: Option<InnerSpan>,
/// Wether the source string is comes from `println!` as opposed to `format!` or `print!`
append_newline: bool,
}

#[derive(Clone, Copy, Debug)]
pub struct SpanIndex(pub usize);

impl SpanIndex {
pub fn unwrap(self) -> usize {
self.0
}
}

impl<'a> Iterator for Parser<'a> {
type Item = Piece<'a>;

fn next(&mut self) -> Option<Piece<'a>> {
if let Some(&(pos, c)) = self.cur.peek() {
match c {
'{' => {
let curr_last_brace = self.last_opening_brace_pos;
self.last_opening_brace_pos = Some(self.to_span_index(pos));
let curr_last_brace = self.last_opening_brace;
let byte_pos = self.to_span_index(pos);
self.last_opening_brace = Some(byte_pos.to(InnerOffset(byte_pos.0 + 1)));
self.cur.next();
if self.consume('{') {
self.last_opening_brace_pos = curr_last_brace;
self.last_opening_brace = curr_last_brace;

Some(String(self.string(pos + 1)))
} else {
let arg = self.argument();
if let Some(arg_pos) = self.must_consume('}').map(|end| {
(self.to_span_index(pos), self.to_span_index(end + 1))
}) {
self.arg_places.push(arg_pos);
if let Some(end) = self.must_consume('}') {
let start = self.to_span_index(pos);
let end = self.to_span_index(end + 1);
self.arg_places.push(start.to(end));
}
Some(NextArgument(arg))
}
Expand All @@ -207,8 +209,7 @@ impl<'a> Iterator for Parser<'a> {
"unmatched `}` found",
"unmatched `}`",
"if you intended to print `}`, you can escape it using `}}`",
err_pos,
err_pos,
err_pos.to(err_pos),
);
None
}
Expand Down Expand Up @@ -240,7 +241,7 @@ impl<'a> Parser<'a> {
style,
arg_places: vec![],
skips,
last_opening_brace_pos: None,
last_opening_brace: None,
append_newline,
}
}
Expand All @@ -252,15 +253,13 @@ impl<'a> Parser<'a> {
&mut self,
description: S1,
label: S2,
start: SpanIndex,
end: SpanIndex,
span: InnerSpan,
) {
self.errors.push(ParseError {
description: description.into(),
note: None,
label: label.into(),
start,
end,
span,
secondary_label: None,
});
}
Expand All @@ -273,15 +272,13 @@ impl<'a> Parser<'a> {
description: S1,
label: S2,
note: S3,
start: SpanIndex,
end: SpanIndex,
span: InnerSpan,
) {
self.errors.push(ParseError {
description: description.into(),
note: Some(note.into()),
label: label.into(),
start,
end,
span,
secondary_label: None,
});
}
Expand All @@ -302,22 +299,21 @@ impl<'a> Parser<'a> {
}
}

fn raw(&self) -> usize {
self.style.map(|raw| raw + 1).unwrap_or(0)
}

fn to_span_index(&self, pos: usize) -> SpanIndex {
fn to_span_index(&self, pos: usize) -> InnerOffset {
let mut pos = pos;
// This handles the raw string case, the raw argument is the number of #
// in r###"..."### (we need to add one because of the `r`).
let raw = self.style.map(|raw| raw + 1).unwrap_or(0);
for skip in &self.skips {
if pos > *skip {
pos += 1;
} else if pos == *skip && self.raw() == 0 {
} else if pos == *skip && raw == 0 {
pos += 1;
} else {
break;
}
}
SpanIndex(self.raw() + pos + 1)
InnerOffset(raw + pos + 1)
}

/// Forces consumption of the specified character. If the character is not
Expand All @@ -335,8 +331,8 @@ impl<'a> Parser<'a> {
let label = "expected `}`".to_owned();
let (note, secondary_label) = if c == '}' {
(Some("if you intended to print `{`, you can escape it using `{{`".to_owned()),
self.last_opening_brace_pos.map(|pos| {
("because of this opening brace".to_owned(), pos, pos)
self.last_opening_brace.map(|sp| {
("because of this opening brace".to_owned(), sp)
}))
} else {
(None, None)
Expand All @@ -345,8 +341,7 @@ impl<'a> Parser<'a> {
description,
note,
label,
start: pos,
end: pos,
span: pos.to(pos),
secondary_label,
});
None
Expand All @@ -360,8 +355,8 @@ impl<'a> Parser<'a> {
let label = format!("expected `{:?}`", c);
let (note, secondary_label) = if c == '}' {
(Some("if you intended to print `{`, you can escape it using `{{`".to_owned()),
self.last_opening_brace_pos.map(|pos| {
("because of this opening brace".to_owned(), pos, pos)
self.last_opening_brace.map(|sp| {
("because of this opening brace".to_owned(), sp)
}))
} else {
(None, None)
Expand All @@ -370,12 +365,11 @@ impl<'a> Parser<'a> {
description,
note,
label,
start: pos,
end: pos,
span: pos.to(pos),
secondary_label,
});
} else {
self.err(description, format!("expected `{:?}`", c), pos, pos);
self.err(description, format!("expected `{:?}`", c), pos.to(pos));
}
None
}
Expand Down Expand Up @@ -434,20 +428,24 @@ impl<'a> Parser<'a> {
/// integer index of an argument, a named argument, or a blank string.
/// 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>> {
fn position(&mut self) -> Option<Position> {
if let Some(i) = self.integer() {
Some(ArgumentIs(i))
} else {
match self.cur.peek() {
Some(&(_, c)) if c.is_alphabetic() => Some(ArgumentNamed(self.word())),
Some(&(_, c)) if c.is_alphabetic() => {
Some(ArgumentNamed(Symbol::intern(self.word())))
}
Some(&(pos, c)) if c == '_' => {
let invalid_name = self.string(pos);
self.err_with_note(format!("invalid argument name `{}`", invalid_name),
"invalid argument name",
"argument names cannot start with an underscore",
self.to_span_index(pos),
self.to_span_index(pos + invalid_name.len()));
Some(ArgumentNamed(invalid_name))
self.to_span_index(pos).to(
self.to_span_index(pos + invalid_name.len())
),
);
Some(ArgumentNamed(Symbol::intern(invalid_name)))
},

// This is an `ArgumentNext`.
Expand Down Expand Up @@ -555,7 +553,7 @@ impl<'a> Parser<'a> {
/// Parses a Count parameter at the current position. This does not check
/// for 'CountIsNextParam' because that is only used in precision, not
/// width.
fn count(&mut self) -> Count<'a> {
fn count(&mut self) -> Count {
if let Some(i) = self.integer() {
if self.consume('$') {
CountIsParam(i)
Expand All @@ -569,7 +567,7 @@ impl<'a> Parser<'a> {
self.cur = tmp;
CountImplied
} else if self.consume('$') {
CountIsName(word)
CountIsName(Symbol::intern(word))
} else {
self.cur = tmp;
CountImplied
Expand Down Expand Up @@ -759,6 +757,8 @@ mod tests {
}
#[test]
fn format_counts() {
use syntax_pos::{GLOBALS, Globals, edition};
GLOBALS.set(&Globals::new(edition::DEFAULT_EDITION), || {
same("{:10s}",
&[NextArgument(Argument {
position: ArgumentImplicitlyIs(0),
Expand Down Expand Up @@ -814,11 +814,12 @@ mod tests {
fill: None,
align: AlignUnknown,
flags: 0,
precision: CountIsName("b"),
width: CountIsName("a"),
precision: CountIsName(Symbol::intern("b")),
width: CountIsName(Symbol::intern("a")),
ty: "s",
},
})]);
});
}
#[test]
fn format_flags() {
Expand Down
Loading