Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f77e96e

Browse files
committed
Allow proper recursion in f-string format specs
1 parent e532e68 commit f77e96e

File tree

1 file changed

+36
-20
lines changed

1 file changed

+36
-20
lines changed

parser/src/fstring.rs

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@ use self::FStringErrorType::*;
1111
struct FStringParser<'a> {
1212
chars: iter::Peekable<str::Chars<'a>>,
1313
str_location: Location,
14+
recurse_lvl: u8,
1415
}
1516

1617
impl<'a> FStringParser<'a> {
17-
fn new(source: &'a str, str_location: Location) -> Self {
18+
fn new(source: &'a str, str_location: Location, recurse_lvl: u8) -> Self {
1819
Self {
1920
chars: source.chars().peekable(),
2021
str_location,
22+
recurse_lvl,
2123
}
2224
}
2325

@@ -95,25 +97,35 @@ impl<'a> FStringParser<'a> {
9597
let mut spec_constructor = Vec::new();
9698
let mut constant_piece = String::new();
9799
let mut formatted_value_piece = String::new();
100+
let mut spec_delims = Vec::new();
98101
while let Some(&next) = self.chars.peek() {
99102
match next {
100-
'{' if in_nested => return Err(ExpressionNestedTooDeeply),
103+
'{' if in_nested => {
104+
spec_delims.push(next);
105+
formatted_value_piece.push(next);
106+
}
101107
'}' if in_nested => {
102-
in_nested = false;
103-
spec_constructor.push(
104-
self.expr(ExprKind::FormattedValue {
105-
value: Box::new(
106-
FStringParser::new(
107-
&format!("{{{}}}", formatted_value_piece),
108-
Location::default(),
109-
)
110-
.parse()?,
111-
),
112-
conversion: None,
113-
format_spec: None,
114-
}),
115-
);
116-
formatted_value_piece.clear();
108+
if spec_delims.is_empty() {
109+
in_nested = false;
110+
spec_constructor.push(
111+
self.expr(ExprKind::FormattedValue {
112+
value: Box::new(
113+
FStringParser::new(
114+
&format!("{{{}}}", formatted_value_piece),
115+
Location::default(),
116+
&self.recurse_lvl + 1,
117+
)
118+
.parse()?,
119+
),
120+
conversion: None,
121+
format_spec: None,
122+
}),
123+
);
124+
formatted_value_piece.clear();
125+
} else {
126+
spec_delims.pop();
127+
formatted_value_piece.push(next);
128+
}
117129
}
118130
_ if in_nested => {
119131
formatted_value_piece.push(next);
@@ -223,6 +235,10 @@ impl<'a> FStringParser<'a> {
223235
}
224236

225237
fn parse(mut self) -> Result<Expr, FStringErrorType> {
238+
if self.recurse_lvl >= 2 {
239+
return Err(ExpressionNestedTooDeeply);
240+
}
241+
226242
let mut content = String::new();
227243
let mut values = vec![];
228244

@@ -276,7 +292,7 @@ fn parse_fstring_expr(source: &str) -> Result<Expr, ParseError> {
276292
/// Parse an fstring from a string, located at a certain position in the sourcecode.
277293
/// In case of errors, we will get the location and the error returned.
278294
pub fn parse_located_fstring(source: &str, location: Location) -> Result<Expr, FStringError> {
279-
FStringParser::new(source, location)
295+
FStringParser::new(source, location, 0)
280296
.parse()
281297
.map_err(|error| FStringError { error, location })
282298
}
@@ -286,7 +302,7 @@ mod tests {
286302
use super::*;
287303

288304
fn parse_fstring(source: &str) -> Result<Expr, FStringErrorType> {
289-
FStringParser::new(source, Location::default()).parse()
305+
FStringParser::new(source, Location::default(), 0).parse()
290306
}
291307

292308
#[test]
@@ -354,7 +370,7 @@ mod tests {
354370
assert_eq!(parse_fstring("{5!}"), Err(InvalidConversionFlag));
355371
assert_eq!(parse_fstring("{5!x}"), Err(InvalidConversionFlag));
356372

357-
assert_eq!(parse_fstring("{a:{a:{b}}"), Err(ExpressionNestedTooDeeply));
373+
assert_eq!(parse_fstring("{a:{a:{b}}}"), Err(ExpressionNestedTooDeeply));
358374

359375
assert_eq!(parse_fstring("{a:b}}"), Err(UnopenedRbrace));
360376
assert_eq!(parse_fstring("}"), Err(UnopenedRbrace));

0 commit comments

Comments
 (0)