mirror of
https://github.com/python/cpython.git
synced 2026-05-06 04:37:33 -04:00
gh-145239: Accept unary plus literal pattern (#148566)
Add '+' alternatives to signed_number and signed_real_number grammar rules, mirroring how unary minus is already handled for pattern matching. Unary plus is a no-op on numbers so the value is returned directly without wrapping in a UnaryOp node.
This commit is contained in:
@@ -858,7 +858,7 @@ A literal pattern corresponds to most
|
||||
: | "None"
|
||||
: | "True"
|
||||
: | "False"
|
||||
signed_number: ["-"] NUMBER
|
||||
signed_number: ["+" | "-"] NUMBER
|
||||
|
||||
The rule ``strings`` and the token ``NUMBER`` are defined in the
|
||||
:doc:`standard Python grammar <./grammar>`. Triple-quoted strings are
|
||||
|
||||
@@ -645,6 +645,10 @@ Other language changes
|
||||
* Allow the *count* argument of :meth:`bytes.replace` to be a keyword.
|
||||
(Contributed by Stan Ulbrych in :gh:`147856`.)
|
||||
|
||||
* Unary plus is now accepted in :keyword:`match` literal patterns, mirroring
|
||||
the existing support for unary minus.
|
||||
(Contributed by Bartosz Sławecki in :gh:`145239`.)
|
||||
|
||||
|
||||
New modules
|
||||
===========
|
||||
|
||||
@@ -554,10 +554,12 @@ complex_number[expr_ty]:
|
||||
|
||||
signed_number[expr_ty]:
|
||||
| NUMBER
|
||||
| '+' number=NUMBER { number }
|
||||
| '-' number=NUMBER { _PyAST_UnaryOp(USub, number, EXTRA) }
|
||||
|
||||
signed_real_number[expr_ty]:
|
||||
| real_number
|
||||
| '+' real=real_number { real }
|
||||
| '-' real=real_number { _PyAST_UnaryOp(USub, real, EXTRA) }
|
||||
|
||||
real_number[expr_ty]:
|
||||
@@ -565,6 +567,7 @@ real_number[expr_ty]:
|
||||
|
||||
imaginary_number[expr_ty]:
|
||||
| imag=NUMBER { _PyPegen_ensure_imaginary(p, imag) }
|
||||
| '+' imag=NUMBER { _PyPegen_ensure_imaginary(p, imag) }
|
||||
|
||||
capture_pattern[pattern_ty]:
|
||||
| target=pattern_capture_target { _PyAST_MatchAs(NULL, target->v.Name.id, EXTRA) }
|
||||
|
||||
@@ -18,6 +18,8 @@ extend-exclude = [
|
||||
"test_lazy_import/__init__.py",
|
||||
"test_lazy_import/data/*.py",
|
||||
"test_lazy_import/data/**/*.py",
|
||||
# Unary plus literal pattern is not yet supported by Ruff (GH-145239)
|
||||
"test_patma.py",
|
||||
]
|
||||
|
||||
[lint]
|
||||
|
||||
@@ -2762,6 +2762,96 @@ class TestPatma(unittest.TestCase):
|
||||
self.assertEqual(y, 1)
|
||||
self.assertIs(z, x)
|
||||
|
||||
def test_patma_256(self):
|
||||
x = 0
|
||||
match x:
|
||||
case +0:
|
||||
y = 0
|
||||
self.assertEqual(x, 0)
|
||||
self.assertEqual(y, 0)
|
||||
|
||||
def test_patma_257(self):
|
||||
x = 0
|
||||
match x:
|
||||
case +0.0:
|
||||
y = 0
|
||||
self.assertEqual(x, 0)
|
||||
self.assertEqual(y, 0)
|
||||
|
||||
def test_patma_258(self):
|
||||
x = 0
|
||||
match x:
|
||||
case +0j:
|
||||
y = 0
|
||||
self.assertEqual(x, 0)
|
||||
self.assertEqual(y, 0)
|
||||
|
||||
def test_patma_259(self):
|
||||
x = 0
|
||||
match x:
|
||||
case +0.0j:
|
||||
y = 0
|
||||
self.assertEqual(x, 0)
|
||||
self.assertEqual(y, 0)
|
||||
|
||||
def test_patma_260(self):
|
||||
x = 1
|
||||
match x:
|
||||
case +1:
|
||||
y = 0
|
||||
self.assertEqual(x, 1)
|
||||
self.assertEqual(y, 0)
|
||||
|
||||
def test_patma_261(self):
|
||||
x = 1.5
|
||||
match x:
|
||||
case +1.5:
|
||||
y = 0
|
||||
self.assertEqual(x, 1.5)
|
||||
self.assertEqual(y, 0)
|
||||
|
||||
def test_patma_262(self):
|
||||
x = 1j
|
||||
match x:
|
||||
case +1j:
|
||||
y = 0
|
||||
self.assertEqual(x, 1j)
|
||||
self.assertEqual(y, 0)
|
||||
|
||||
def test_patma_263(self):
|
||||
x = 1.5j
|
||||
match x:
|
||||
case +1.5j:
|
||||
y = 0
|
||||
self.assertEqual(x, 1.5j)
|
||||
self.assertEqual(y, 0)
|
||||
|
||||
def test_patma_264(self):
|
||||
x = 0.25 + 1.75j
|
||||
match x:
|
||||
case +0.25 + 1.75j:
|
||||
y = 0
|
||||
self.assertEqual(x, 0.25 + 1.75j)
|
||||
self.assertEqual(y, 0)
|
||||
|
||||
def test_patma_265(self):
|
||||
x = 0.25 - 1.75j
|
||||
match x:
|
||||
case 0.25 - +1.75j:
|
||||
y = 0
|
||||
self.assertEqual(x, 0.25 - 1.75j)
|
||||
self.assertEqual(y, 0)
|
||||
|
||||
def test_patma_266(self):
|
||||
x = 0
|
||||
match x:
|
||||
case +1e1000:
|
||||
y = 0
|
||||
case 0:
|
||||
y = 1
|
||||
self.assertEqual(x, 0)
|
||||
self.assertEqual(y, 1)
|
||||
|
||||
def test_patma_runtime_checkable_protocol(self):
|
||||
# Runtime-checkable protocol
|
||||
from typing import Protocol, runtime_checkable
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
Unary plus is now accepted in :keyword:`match` literal patterns, mirroring
|
||||
the existing support for unary minus.
|
||||
Patch by Bartosz Sławecki.
|
||||
Generated
+84
-3
@@ -9066,7 +9066,7 @@ complex_number_rule(Parser *p)
|
||||
return _res;
|
||||
}
|
||||
|
||||
// signed_number: NUMBER | '-' NUMBER
|
||||
// signed_number: NUMBER | '+' NUMBER | '-' NUMBER
|
||||
static expr_ty
|
||||
signed_number_rule(Parser *p)
|
||||
{
|
||||
@@ -9107,6 +9107,33 @@ signed_number_rule(Parser *p)
|
||||
D(fprintf(stderr, "%*c%s signed_number[%d-%d]: %s failed!\n", p->level, ' ',
|
||||
p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NUMBER"));
|
||||
}
|
||||
{ // '+' NUMBER
|
||||
if (p->error_indicator) {
|
||||
p->level--;
|
||||
return NULL;
|
||||
}
|
||||
D(fprintf(stderr, "%*c> signed_number[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'+' NUMBER"));
|
||||
Token * _literal;
|
||||
expr_ty number;
|
||||
if (
|
||||
(_literal = _PyPegen_expect_token(p, 14)) // token='+'
|
||||
&&
|
||||
(number = _PyPegen_number_token(p)) // NUMBER
|
||||
)
|
||||
{
|
||||
D(fprintf(stderr, "%*c+ signed_number[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'+' NUMBER"));
|
||||
_res = number;
|
||||
if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) {
|
||||
p->error_indicator = 1;
|
||||
p->level--;
|
||||
return NULL;
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
p->mark = _mark;
|
||||
D(fprintf(stderr, "%*c%s signed_number[%d-%d]: %s failed!\n", p->level, ' ',
|
||||
p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'+' NUMBER"));
|
||||
}
|
||||
{ // '-' NUMBER
|
||||
if (p->error_indicator) {
|
||||
p->level--;
|
||||
@@ -9149,7 +9176,7 @@ signed_number_rule(Parser *p)
|
||||
return _res;
|
||||
}
|
||||
|
||||
// signed_real_number: real_number | '-' real_number
|
||||
// signed_real_number: real_number | '+' real_number | '-' real_number
|
||||
static expr_ty
|
||||
signed_real_number_rule(Parser *p)
|
||||
{
|
||||
@@ -9190,6 +9217,33 @@ signed_real_number_rule(Parser *p)
|
||||
D(fprintf(stderr, "%*c%s signed_real_number[%d-%d]: %s failed!\n", p->level, ' ',
|
||||
p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "real_number"));
|
||||
}
|
||||
{ // '+' real_number
|
||||
if (p->error_indicator) {
|
||||
p->level--;
|
||||
return NULL;
|
||||
}
|
||||
D(fprintf(stderr, "%*c> signed_real_number[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'+' real_number"));
|
||||
Token * _literal;
|
||||
expr_ty real;
|
||||
if (
|
||||
(_literal = _PyPegen_expect_token(p, 14)) // token='+'
|
||||
&&
|
||||
(real = real_number_rule(p)) // real_number
|
||||
)
|
||||
{
|
||||
D(fprintf(stderr, "%*c+ signed_real_number[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'+' real_number"));
|
||||
_res = real;
|
||||
if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) {
|
||||
p->error_indicator = 1;
|
||||
p->level--;
|
||||
return NULL;
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
p->mark = _mark;
|
||||
D(fprintf(stderr, "%*c%s signed_real_number[%d-%d]: %s failed!\n", p->level, ' ',
|
||||
p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'+' real_number"));
|
||||
}
|
||||
{ // '-' real_number
|
||||
if (p->error_indicator) {
|
||||
p->level--;
|
||||
@@ -9275,7 +9329,7 @@ real_number_rule(Parser *p)
|
||||
return _res;
|
||||
}
|
||||
|
||||
// imaginary_number: NUMBER
|
||||
// imaginary_number: NUMBER | '+' NUMBER
|
||||
static expr_ty
|
||||
imaginary_number_rule(Parser *p)
|
||||
{
|
||||
@@ -9312,6 +9366,33 @@ imaginary_number_rule(Parser *p)
|
||||
D(fprintf(stderr, "%*c%s imaginary_number[%d-%d]: %s failed!\n", p->level, ' ',
|
||||
p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NUMBER"));
|
||||
}
|
||||
{ // '+' NUMBER
|
||||
if (p->error_indicator) {
|
||||
p->level--;
|
||||
return NULL;
|
||||
}
|
||||
D(fprintf(stderr, "%*c> imaginary_number[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'+' NUMBER"));
|
||||
Token * _literal;
|
||||
expr_ty imag;
|
||||
if (
|
||||
(_literal = _PyPegen_expect_token(p, 14)) // token='+'
|
||||
&&
|
||||
(imag = _PyPegen_number_token(p)) // NUMBER
|
||||
)
|
||||
{
|
||||
D(fprintf(stderr, "%*c+ imaginary_number[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'+' NUMBER"));
|
||||
_res = _PyPegen_ensure_imaginary ( p , imag );
|
||||
if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) {
|
||||
p->error_indicator = 1;
|
||||
p->level--;
|
||||
return NULL;
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
p->mark = _mark;
|
||||
D(fprintf(stderr, "%*c%s imaginary_number[%d-%d]: %s failed!\n", p->level, ' ',
|
||||
p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'+' NUMBER"));
|
||||
}
|
||||
_res = NULL;
|
||||
done:
|
||||
p->level--;
|
||||
|
||||
Reference in New Issue
Block a user