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

Skip to content
This repository was archived by the owner on Apr 14, 2022. It is now read-only.

Commit fef3ec9

Browse files
jakebaileyMikhail Arkhipov
authored andcommitted
Handle operators containing equals in fstring parsing (#1869)
* Handle operators containing equals in fstring parsing * Remove some dead code * Remove LINQ, simplify to property * Just append NextChar in the bufferer function, don't pipe down (cherry picked from commit e31f770)
1 parent 56ae89d commit fef3ec9

File tree

5 files changed

+106
-8
lines changed

5 files changed

+106
-8
lines changed

src/Parsing/Impl/FStringParser.cs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ internal class FStringParser {
3232
private static readonly StringSpan DoubleOpen = new StringSpan("{{");
3333
private static readonly StringSpan DoubleClose = new StringSpan("}}");
3434
private static readonly StringSpan NotEqual = new StringSpan("!=");
35-
private static readonly StringSpan EqualEqual = new StringSpan("==");
3635
private static readonly StringSpan BackslashN = new StringSpan("\\N");
3736

3837
internal FStringParser(List<Node> fStringChildren, string fString, bool isRaw,
@@ -136,7 +135,7 @@ private Node ParseFStringExpression() {
136135
_buffer.Clear();
137136
}
138137

139-
Debug.Assert(CurrentChar == '}' || CurrentChar == '!' || CurrentChar == ':' || (_langVersion >= PythonLanguageVersion.V38 && CurrentChar == '='));
138+
Debug.Assert(CurrentChar == '}' || CurrentChar == '!' || CurrentChar == ':' || CurrentChar == '=');
140139

141140
MaybeReadEqualSpecifier();
142141
var conversion = MaybeReadConversionChar();
@@ -232,10 +231,23 @@ private void BufferInnerExpression() {
232231

233232
while (!EndOfFString) {
234233
var ch = CurrentChar;
234+
var appendExtra = false;
235+
235236
if (!quoteChar.HasValue && _nestedParens.Count == 0) {
236237
switch (ch) {
237-
case '!' when !IsNext(NotEqual):
238-
case '=' when !IsNext(EqualEqual) && _langVersion >= PythonLanguageVersion.V38:
238+
case '=':
239+
case '!':
240+
if (!IsEqualsAfterNext) {
241+
return;
242+
}
243+
appendExtra = true;
244+
break;
245+
246+
case '<':
247+
case '>':
248+
appendExtra = IsEqualsAfterNext;
249+
break;
250+
239251
case '}':
240252
case ':':
241253
return;
@@ -253,6 +265,10 @@ private void BufferInnerExpression() {
253265
} else {
254266
HandleInnerExprOutsideString(ref quoteChar, ref stringType);
255267
}
268+
269+
if (appendExtra) {
270+
_buffer.Append(NextChar());
271+
}
256272
}
257273
}
258274

@@ -484,5 +500,7 @@ private bool IsAsciiWhiteSpace {
484500
return false;
485501
}
486502
}
503+
504+
private bool IsEqualsAfterNext => _position + 1 < _fString.Length && _fString[_position + 1] == '=';
487505
}
488506
}

src/Parsing/Test/ParserTests.cs

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ public void FStringErrors() {
116116
new ErrorResult("f-string: expecting '}'", new SourceSpan(19, 4, 19, 5)),
117117
new ErrorResult("unexpected token 'import'", new SourceSpan(21, 4, 21, 10)),
118118
new ErrorResult("f-string: empty expression not allowed", new SourceSpan(23, 4, 23, 5)),
119-
new ErrorResult("unexpected token '='", new SourceSpan(25, 6, 25, 7)),
119+
new ErrorResult("f-string: expecting '}' but found '='", new SourceSpan(25, 6, 25, 7)),
120120
new ErrorResult("expected ':'", new SourceSpan(27, 12, 27, 12)),
121121
new ErrorResult("f-string: lambda must be inside parentheses", new SourceSpan(27, 4, 27, 12)),
122122
new ErrorResult("f-string: expecting '}'", new SourceSpan(29, 6, 29, 7)),
@@ -125,7 +125,9 @@ public void FStringErrors() {
125125
new ErrorResult("f-string: unmatched ')'", new SourceSpan(35, 4, 35, 5)),
126126
new ErrorResult("f-string: unmatched ')'", new SourceSpan(37, 6, 37, 7)),
127127
new ErrorResult("f-string: closing parenthesis '}' does not match opening parenthesis '('", new SourceSpan(39, 6, 39, 7)),
128-
new ErrorResult("f-string: unmatched ']'", new SourceSpan(41, 4, 41, 5))
128+
new ErrorResult("f-string: unmatched ']'", new SourceSpan(41, 4, 41, 5)),
129+
new ErrorResult("unexpected EOF while parsing", new SourceSpan(43, 7, 43, 7)),
130+
new ErrorResult("f-string: expecting '}' but found '='", new SourceSpan(43, 7, 43, 8))
129131
);
130132
}
131133

@@ -408,6 +410,72 @@ public void FStrings() {
408410
)
409411
)
410412
)
413+
),
414+
CheckExprStmt(
415+
CheckFString(
416+
CheckFormattedValue(
417+
CheckConditionalExpression(
418+
CheckNameExpr("x"),
419+
CheckBinaryExpression(CheckNameExpr("x"), PythonOperator.Equal, CheckConstant(1.0)),
420+
CheckConstant(0)
421+
)
422+
)
423+
)
424+
),
425+
CheckExprStmt(
426+
CheckFString(
427+
CheckFormattedValue(
428+
CheckConditionalExpression(
429+
CheckNameExpr("x"),
430+
CheckBinaryExpression(CheckNameExpr("x"), PythonOperator.NotEqual, CheckConstant(1.0)),
431+
CheckConstant(0)
432+
)
433+
)
434+
)
435+
),
436+
CheckExprStmt(
437+
CheckFString(
438+
CheckFormattedValue(
439+
CheckConditionalExpression(
440+
CheckNameExpr("x"),
441+
CheckBinaryExpression(CheckNameExpr("x"), PythonOperator.GreaterThanOrEqual, CheckConstant(1.0)),
442+
CheckConstant(0)
443+
)
444+
)
445+
)
446+
),
447+
CheckExprStmt(
448+
CheckFString(
449+
CheckFormattedValue(
450+
CheckConditionalExpression(
451+
CheckNameExpr("x"),
452+
CheckBinaryExpression(CheckNameExpr("x"), PythonOperator.LessThanOrEqual, CheckConstant(1.0)),
453+
CheckConstant(0)
454+
)
455+
)
456+
)
457+
),
458+
CheckExprStmt(
459+
CheckFString(
460+
CheckFormattedValue(
461+
CheckConditionalExpression(
462+
CheckNameExpr("x"),
463+
CheckBinaryExpression(CheckNameExpr("x"), PythonOperator.GreaterThan, CheckConstant(1.0)),
464+
CheckConstant(0)
465+
)
466+
)
467+
)
468+
),
469+
CheckExprStmt(
470+
CheckFString(
471+
CheckFormattedValue(
472+
CheckConditionalExpression(
473+
CheckNameExpr("x"),
474+
CheckBinaryExpression(CheckNameExpr("x"), PythonOperator.LessThan, CheckConstant(1.0)),
475+
CheckConstant(0)
476+
)
477+
)
478+
)
411479
)
412480
)
413481
);
@@ -478,7 +546,9 @@ public void FStringEqualsErrors() {
478546
ParseErrors("FStringEqualsErrors.py",
479547
PythonLanguageVersion.V38,
480548
new ErrorResult("f-string: expecting '}' but found 'f'", new SourceSpan(1, 9, 1, 10)),
481-
new ErrorResult("f-string: expecting '}' but found 'a'", new SourceSpan(2, 10, 2, 11))
549+
new ErrorResult("f-string: expecting '}' but found 'a'", new SourceSpan(2, 10, 2, 11)),
550+
new ErrorResult("unexpected EOF while parsing", new SourceSpan(3, 9, 3, 9)),
551+
new ErrorResult("f-string: expecting '}' but found 'a'", new SourceSpan(3, 11, 3, 12))
482552
);
483553
}
484554

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
f"{name=foo}"
2-
f"{bad = a }"
2+
f"{bad = a }"
3+
f"{bad *= a }"

src/UnitTests/TestData/Grammar/FStringErrors.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,5 @@
3939
f'{ (} }'
4040
4141
f'{]}'
42+
43+
f'{a /= 1}'

src/UnitTests/TestData/Grammar/FStrings.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,10 @@ def f():
4848
f'{(lambda x: x + 1)(1)}'
4949

5050
f"{[1,2,1][1:]}"
51+
52+
f'{x if x == 1.0 else 0}'
53+
f'{x if x != 1.0 else 0}'
54+
f'{x if x >= 1.0 else 0}'
55+
f'{x if x <= 1.0 else 0}'
56+
f'{x if x > 1.0 else 0}'
57+
f'{x if x < 1.0 else 0}'

0 commit comments

Comments
 (0)