-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for the new f-string tokens per PEP 701
- Loading branch information
1 parent
856f9c2
commit a42b40c
Showing
23 changed files
with
1,213 additions
and
10 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
use bitflags::bitflags; | ||
|
||
use ruff_text_size::TextSize; | ||
|
||
bitflags! { | ||
#[derive(Debug)] | ||
pub(crate) struct FStringContextFlags: u32 { | ||
/// The current f-string is a triple-quoted f-string i.e., the number of | ||
/// opening and closing quotes is 3. If this flag is not set, the number | ||
/// of opening and closing quotes is 1. | ||
const TRIPLE = 1 << 0; | ||
|
||
/// The current f-string is a double-quoted f-string. If this flag is not | ||
/// set, the current f-string is a single-quoted f-string. | ||
const DOUBLE = 1 << 1; | ||
|
||
/// The current f-string is a raw f-string. If this flag is not set, the | ||
/// current f-string is a non-raw f-string. | ||
const RAW = 1 << 2; | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
pub(crate) struct FStringContext { | ||
flags: FStringContextFlags, | ||
/// The number of open parentheses for the current f-string. This includes all | ||
/// three types of parentheses: round (`(`), square (`[`), and curly (`{`). | ||
open_parentheses_count: u32, | ||
/// The number of format specs for the current f-string. This is because there | ||
/// can be multiple format specs nested. For example, `{a:{b:{c}}}` has 3 format | ||
/// specs. | ||
format_spec_depth: u32, | ||
} | ||
|
||
impl FStringContext { | ||
pub(crate) fn new(flags: FStringContextFlags) -> Self { | ||
Self { | ||
flags, | ||
open_parentheses_count: 0, | ||
format_spec_depth: 0, | ||
} | ||
} | ||
|
||
/// Returns the quote character for the current f-string. | ||
pub(crate) fn quote_char(&self) -> char { | ||
if self.flags.contains(FStringContextFlags::DOUBLE) { | ||
'"' | ||
} else { | ||
'\'' | ||
} | ||
} | ||
|
||
/// Returns the number of quotes for the current f-string. | ||
pub(crate) fn quote_size(&self) -> TextSize { | ||
if self.is_triple_quoted() { | ||
TextSize::from(3) | ||
} else { | ||
TextSize::from(1) | ||
} | ||
} | ||
|
||
/// Returns the triple quotes for the current f-string if it is a triple-quoted | ||
/// f-string, `None` otherwise. | ||
pub(crate) fn triple_quotes(&self) -> Option<&'static str> { | ||
if self.is_triple_quoted() { | ||
if self.flags.contains(FStringContextFlags::DOUBLE) { | ||
Some(r#"""""#) | ||
} else { | ||
Some("'''") | ||
} | ||
} else { | ||
None | ||
} | ||
} | ||
|
||
/// Returns `true` if the current f-string is a raw f-string. | ||
pub(crate) fn is_raw_string(&self) -> bool { | ||
self.flags.contains(FStringContextFlags::RAW) | ||
} | ||
|
||
/// Returns `true` if the current f-string is a triple-quoted f-string. | ||
pub(crate) fn is_triple_quoted(&self) -> bool { | ||
self.flags.contains(FStringContextFlags::TRIPLE) | ||
} | ||
|
||
/// Returns `true` if the current f-string has open parentheses. | ||
pub(crate) fn has_open_parentheses(&mut self) -> bool { | ||
self.open_parentheses_count > 0 | ||
} | ||
|
||
/// Increments the number of parentheses for the current f-string. | ||
pub(crate) fn increment_opening_parentheses(&mut self) { | ||
self.open_parentheses_count += 1; | ||
} | ||
|
||
/// Decrements the number of parentheses for the current f-string. If the | ||
/// lexer is in a format spec, also decrements the number of format specs. | ||
pub(crate) fn decrement_closing_parentheses(&mut self) { | ||
if self.is_in_format_spec() { | ||
self.format_spec_depth = self.format_spec_depth.saturating_sub(1); | ||
} | ||
self.open_parentheses_count = self.open_parentheses_count.saturating_sub(1); | ||
} | ||
|
||
/// Returns `true` if the lexer is in a f-string expression i.e., between | ||
/// two curly braces. | ||
pub(crate) fn is_in_expression(&self) -> bool { | ||
self.open_parentheses_count > self.format_spec_depth | ||
} | ||
|
||
/// Returns `true` if the lexer is in a f-string format spec i.e., after a colon. | ||
pub(crate) fn is_in_format_spec(&self) -> bool { | ||
self.format_spec_depth > 0 && !self.is_in_expression() | ||
} | ||
|
||
/// Returns `true` if the context is in a valid position to start format spec | ||
/// i.e., at the same level of nesting as the opening parentheses token. | ||
/// Increments the number of format specs if it is. | ||
/// | ||
/// This assumes that the current character for the lexer is a colon (`:`). | ||
pub(crate) fn try_start_format_spec(&mut self) -> bool { | ||
if self | ||
.open_parentheses_count | ||
.saturating_sub(self.format_spec_depth) | ||
== 1 | ||
{ | ||
self.format_spec_depth += 1; | ||
true | ||
} else { | ||
false | ||
} | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
...es/ruff_python_parser/src/snapshots/ruff_python_parser__lexer__tests__empty_fstrings.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
--- | ||
source: crates/ruff_python_parser/src/lexer.rs | ||
expression: lex_source(source) | ||
--- | ||
[ | ||
FStringStart, | ||
FStringEnd, | ||
String { | ||
value: "", | ||
kind: String, | ||
triple_quoted: false, | ||
}, | ||
FStringStart, | ||
FStringEnd, | ||
FStringStart, | ||
FStringEnd, | ||
String { | ||
value: "", | ||
kind: String, | ||
triple_quoted: false, | ||
}, | ||
FStringStart, | ||
FStringEnd, | ||
FStringStart, | ||
FStringEnd, | ||
Newline, | ||
] |
40 changes: 40 additions & 0 deletions
40
crates/ruff_python_parser/src/snapshots/ruff_python_parser__lexer__tests__fstring.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
--- | ||
source: crates/ruff_python_parser/src/lexer.rs | ||
expression: lex_source(source) | ||
--- | ||
[ | ||
FStringStart, | ||
FStringMiddle { | ||
value: "normal ", | ||
is_raw: false, | ||
}, | ||
Lbrace, | ||
Name { | ||
name: "foo", | ||
}, | ||
Rbrace, | ||
FStringMiddle { | ||
value: " {another} ", | ||
is_raw: false, | ||
}, | ||
Lbrace, | ||
Name { | ||
name: "bar", | ||
}, | ||
Rbrace, | ||
FStringMiddle { | ||
value: " {", | ||
is_raw: false, | ||
}, | ||
Lbrace, | ||
Name { | ||
name: "three", | ||
}, | ||
Rbrace, | ||
FStringMiddle { | ||
value: "}", | ||
is_raw: false, | ||
}, | ||
FStringEnd, | ||
Newline, | ||
] |
27 changes: 27 additions & 0 deletions
27
.../ruff_python_parser/src/snapshots/ruff_python_parser__lexer__tests__fstring_comments.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
--- | ||
source: crates/ruff_python_parser/src/lexer.rs | ||
expression: lex_source(source) | ||
--- | ||
[ | ||
FStringStart, | ||
FStringMiddle { | ||
value: "\n# not a comment ", | ||
is_raw: false, | ||
}, | ||
Lbrace, | ||
Comment( | ||
"# comment {", | ||
), | ||
NonLogicalNewline, | ||
Name { | ||
name: "x", | ||
}, | ||
NonLogicalNewline, | ||
Rbrace, | ||
FStringMiddle { | ||
value: " # not a comment\n", | ||
is_raw: false, | ||
}, | ||
FStringEnd, | ||
Newline, | ||
] |
50 changes: 50 additions & 0 deletions
50
...uff_python_parser/src/snapshots/ruff_python_parser__lexer__tests__fstring_conversion.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
--- | ||
source: crates/ruff_python_parser/src/lexer.rs | ||
expression: lex_source(source) | ||
--- | ||
[ | ||
FStringStart, | ||
Lbrace, | ||
Name { | ||
name: "x", | ||
}, | ||
Exclamation, | ||
Name { | ||
name: "s", | ||
}, | ||
Rbrace, | ||
FStringMiddle { | ||
value: " ", | ||
is_raw: false, | ||
}, | ||
Lbrace, | ||
Name { | ||
name: "x", | ||
}, | ||
Equal, | ||
Exclamation, | ||
Name { | ||
name: "r", | ||
}, | ||
Rbrace, | ||
FStringMiddle { | ||
value: " ", | ||
is_raw: false, | ||
}, | ||
Lbrace, | ||
Name { | ||
name: "x", | ||
}, | ||
Colon, | ||
FStringMiddle { | ||
value: ".3f!r", | ||
is_raw: false, | ||
}, | ||
Rbrace, | ||
FStringMiddle { | ||
value: " {x!r}", | ||
is_raw: false, | ||
}, | ||
FStringEnd, | ||
Newline, | ||
] |
32 changes: 32 additions & 0 deletions
32
...es/ruff_python_parser/src/snapshots/ruff_python_parser__lexer__tests__fstring_escape.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
--- | ||
source: crates/ruff_python_parser/src/lexer.rs | ||
expression: lex_source(source) | ||
--- | ||
[ | ||
FStringStart, | ||
FStringMiddle { | ||
value: "\\", | ||
is_raw: false, | ||
}, | ||
Lbrace, | ||
Name { | ||
name: "x", | ||
}, | ||
Colon, | ||
FStringMiddle { | ||
value: "\\\"\\", | ||
is_raw: false, | ||
}, | ||
Lbrace, | ||
Name { | ||
name: "x", | ||
}, | ||
Rbrace, | ||
Rbrace, | ||
FStringMiddle { | ||
value: " \\\"\\\"\\\n end", | ||
is_raw: false, | ||
}, | ||
FStringEnd, | ||
Newline, | ||
] |
32 changes: 32 additions & 0 deletions
32
...uff_python_parser/src/snapshots/ruff_python_parser__lexer__tests__fstring_escape_raw.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
--- | ||
source: crates/ruff_python_parser/src/lexer.rs | ||
expression: lex_source(source) | ||
--- | ||
[ | ||
FStringStart, | ||
FStringMiddle { | ||
value: "\\", | ||
is_raw: true, | ||
}, | ||
Lbrace, | ||
Name { | ||
name: "x", | ||
}, | ||
Colon, | ||
FStringMiddle { | ||
value: "\\\"\\", | ||
is_raw: true, | ||
}, | ||
Lbrace, | ||
Name { | ||
name: "x", | ||
}, | ||
Rbrace, | ||
Rbrace, | ||
FStringMiddle { | ||
value: " \\\"\\\"\\\n end", | ||
is_raw: true, | ||
}, | ||
FStringEnd, | ||
Newline, | ||
] |
Oops, something went wrong.