Skip to content

Commit

Permalink
Allow multi-line f-string with format spec
Browse files Browse the repository at this point in the history
  • Loading branch information
dhruvmanila committed Oct 3, 2023
1 parent 37d21c0 commit 68f3d35
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 4 deletions.
23 changes: 19 additions & 4 deletions crates/ruff_python_parser/src/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,10 @@ impl<'source> Lexer<'source> {
// Tracks the last offset of token value that has been written to `normalized`.
let mut last_offset = self.offset();

// This isn't going to change for the duration of the loop, so we can
// compute it once and then use it as a constant.
let in_format_spec = fstring.is_in_format_spec(self.nesting);

let mut in_named_unicode = false;

loop {
Expand All @@ -584,6 +588,9 @@ impl<'source> Lexer<'source> {
location: self.offset(),
});
}
// If we encounter a newline while we're in a format spec, then we
// stop here and let the lexer emit the newline token.
'\n' | '\r' if in_format_spec => break,
'\n' | '\r' if !fstring.is_triple_quoted() => {
return Err(LexicalError {
error: LexicalErrorType::FStringError(FStringErrorType::UnterminatedString),
Expand Down Expand Up @@ -620,7 +627,7 @@ impl<'source> Lexer<'source> {
}
}
'{' => {
if self.cursor.second() == '{' && !fstring.is_in_format_spec(self.nesting) {
if self.cursor.second() == '{' && !in_format_spec {
self.cursor.bump();
normalized
.push_str(&self.source[TextRange::new(last_offset, self.offset())]);
Expand All @@ -634,9 +641,7 @@ impl<'source> Lexer<'source> {
if in_named_unicode {
in_named_unicode = false;
self.cursor.bump();
} else if self.cursor.second() == '}'
&& !fstring.is_in_format_spec(self.nesting)
{
} else if self.cursor.second() == '}' && !in_format_spec {
self.cursor.bump();
normalized
.push_str(&self.source[TextRange::new(last_offset, self.offset())]);
Expand Down Expand Up @@ -2051,6 +2056,16 @@ def f(arg=%timeit a = b):
assert_debug_snapshot!(lex_source(source));
}

#[test]
fn test_fstring_with_multiline_format_spec() {
let source = r"f'''__{
x:d
}__''' f'__{
x:d
}__'";
assert_debug_snapshot!(lex_source(source));
}

#[test]
fn test_fstring_conversion() {
let source = r#"f"{x!s} {x=!r} {x:.3f!r} {{x!r}}""#;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
---
source: crates/ruff_python_parser/src/lexer.rs
expression: lex_source(source)
---
[
(
FStringStart,
0..4,
),
(
FStringMiddle {
value: "__",
is_raw: false,
},
4..6,
),
(
Lbrace,
6..7,
),
(
NonLogicalNewline,
7..8,
),
(
Name {
name: "x",
},
12..13,
),
(
Colon,
13..14,
),
(
FStringMiddle {
value: "d",
is_raw: false,
},
14..15,
),
(
NonLogicalNewline,
15..16,
),
(
Rbrace,
16..17,
),
(
FStringMiddle {
value: "__",
is_raw: false,
},
17..19,
),
(
FStringEnd,
19..22,
),
(
FStringStart,
23..25,
),
(
FStringMiddle {
value: "__",
is_raw: false,
},
25..27,
),
(
Lbrace,
27..28,
),
(
NonLogicalNewline,
28..29,
),
(
Name {
name: "x",
},
33..34,
),
(
Colon,
34..35,
),
(
FStringMiddle {
value: "d",
is_raw: false,
},
35..36,
),
(
NonLogicalNewline,
36..37,
),
(
Rbrace,
37..38,
),
(
FStringMiddle {
value: "__",
is_raw: false,
},
38..40,
),
(
FStringEnd,
40..41,
),
(
Newline,
41..41,
),
]

0 comments on commit 68f3d35

Please sign in to comment.