mirror of
https://github.com/astral-sh/ruff.git
synced 2025-07-21 20:15:11 +00:00

(Supersedes #9152, authored by @LaBatata101) ## Summary This PR replaces the current parser generated from LALRPOP to a hand-written recursive descent parser. It also updates the grammar for [PEP 646](https://peps.python.org/pep-0646/) so that the parser outputs the correct AST. For example, in `data[*x]`, the index expression is now a tuple with a single starred expression instead of just a starred expression. Beyond the performance improvements, the parser is also error resilient and can provide better error messages. The behavior as seen by any downstream tools isn't changed. That is, the linter and formatter can still assume that the parser will _stop_ at the first syntax error. This will be updated in the following months. For more details about the change here, refer to the PR corresponding to the individual commits and the release blog post. ## Test Plan Write _lots_ and _lots_ of tests for both valid and invalid syntax and verify the output. ## Acknowledgements - @MichaReiser for reviewing 100+ parser PRs and continuously providing guidance throughout the project - @LaBatata101 for initiating the transition to a hand-written parser in #9152 - @addisoncrump for implementing the fuzzer which helped [catch](https://github.com/astral-sh/ruff/pull/10903) [a](https://github.com/astral-sh/ruff/pull/10910) [lot](https://github.com/astral-sh/ruff/pull/10966) [of](https://github.com/astral-sh/ruff/pull/10896) [bugs](https://github.com/astral-sh/ruff/pull/10877) --------- Co-authored-by: Victor Hugo Gomes <labatata101@linuxmail.org> Co-authored-by: Micha Reiser <micha@reiser.io>
337 lines
5.6 KiB
Python
337 lines
5.6 KiB
Python
# Cases sampled from Lib/test/test_patma.py
|
|
|
|
# case test_patma_098
|
|
match x:
|
|
case -0j:
|
|
y = 0
|
|
# case test_patma_142
|
|
match x:
|
|
case bytes(z):
|
|
y = 0
|
|
# case test_patma_073
|
|
match x:
|
|
case 0 if 0:
|
|
y = 0
|
|
case 0 if 1:
|
|
y = 1
|
|
# case test_patma_006
|
|
match 3:
|
|
case 0 | 1 | 2 | 3:
|
|
x = True
|
|
# case test_patma_049
|
|
match x:
|
|
case [0, 1] | [1, 0]:
|
|
y = 0
|
|
# case black_check_sequence_then_mapping
|
|
match x:
|
|
case [*_]:
|
|
return "seq"
|
|
case {}:
|
|
return "map"
|
|
# case test_patma_035
|
|
match x:
|
|
case {0: [1, 2, {}]}:
|
|
y = 0
|
|
case {0: [1, 2, {}] | True} | {1: [[]]} | {0: [1, 2, {}]} | [] | "X" | {}:
|
|
y = 1
|
|
case []:
|
|
y = 2
|
|
# case test_patma_107
|
|
match x:
|
|
case 0.25 + 1.75j:
|
|
y = 0
|
|
# case test_patma_097
|
|
match x:
|
|
case -0j:
|
|
y = 0
|
|
# case test_patma_007
|
|
match 4:
|
|
case 0 | 1 | 2 | 3:
|
|
x = True
|
|
# case test_patma_154
|
|
match x:
|
|
case 0 if x:
|
|
y = 0
|
|
# case test_patma_134
|
|
match x:
|
|
case {1: 0}:
|
|
y = 0
|
|
case {0: 0}:
|
|
y = 1
|
|
case {**z}:
|
|
y = 2
|
|
# case test_patma_185
|
|
match Seq():
|
|
case [*_]:
|
|
y = 0
|
|
# case test_patma_063
|
|
match x:
|
|
case 1:
|
|
y = 0
|
|
case 1:
|
|
y = 1
|
|
# case test_patma_248
|
|
match x:
|
|
case {"foo": bar}:
|
|
y = bar
|
|
# case test_patma_019
|
|
match (0, 1, 2):
|
|
case [0, 1, *x, 2]:
|
|
y = 0
|
|
# case test_patma_052
|
|
match x:
|
|
case [0]:
|
|
y = 0
|
|
case [1, 0] if (x := x[:0]):
|
|
y = 1
|
|
case [1, 0]:
|
|
y = 2
|
|
# case test_patma_191
|
|
match w:
|
|
case [x, y, *_]:
|
|
z = 0
|
|
# case test_patma_110
|
|
match x:
|
|
case -0.25 - 1.75j:
|
|
y = 0
|
|
# case test_patma_151
|
|
match (x,):
|
|
case [y]:
|
|
z = 0
|
|
# case test_patma_114
|
|
match x:
|
|
case A.B.C.D:
|
|
y = 0
|
|
# case test_patma_232
|
|
match x:
|
|
case None:
|
|
y = 0
|
|
# case test_patma_058
|
|
match x:
|
|
case 0:
|
|
y = 0
|
|
# case test_patma_233
|
|
match x:
|
|
case False:
|
|
y = 0
|
|
# case test_patma_078
|
|
match x:
|
|
case []:
|
|
y = 0
|
|
case [""]:
|
|
y = 1
|
|
case "":
|
|
y = 2
|
|
# case test_patma_156
|
|
match x:
|
|
case z:
|
|
y = 0
|
|
# case test_patma_189
|
|
match w:
|
|
case [x, y, *rest]:
|
|
z = 0
|
|
# case test_patma_042
|
|
match x:
|
|
case (0 as z) | (1 as z) | (2 as z) if z == x % 2:
|
|
y = 0
|
|
# case test_patma_034
|
|
match x:
|
|
case {0: [1, 2, {}]}:
|
|
y = 0
|
|
case {0: [1, 2, {}] | False} | {1: [[]]} | {0: [1, 2, {}]} | [] | "X" | {}:
|
|
y = 1
|
|
case []:
|
|
y = 2
|
|
# case test_patma_123
|
|
match (0, 1, 2):
|
|
case 0, *x:
|
|
y = 0
|
|
# case test_patma_126
|
|
match (0, 1, 2):
|
|
case *x, 2,:
|
|
y = 0
|
|
# case test_patma_151
|
|
match x,:
|
|
case y,:
|
|
z = 0
|
|
# case test_patma_152
|
|
match w, x:
|
|
case y, z:
|
|
v = 0
|
|
# case test_patma_153
|
|
match w := x,:
|
|
case y as v,:
|
|
z = 0
|
|
|
|
match x:
|
|
# F-strings aren't allowed as patterns but it's a soft syntax error in Python.
|
|
case f"{y}":
|
|
pass
|
|
match {"test": 1}:
|
|
case {
|
|
**rest,
|
|
}:
|
|
print(rest)
|
|
match {"label": "test"}:
|
|
case {
|
|
"label": str() | None as label,
|
|
}:
|
|
print(label)
|
|
match x:
|
|
case [0, 1,]:
|
|
y = 0
|
|
match x:
|
|
case (0, 1,):
|
|
y = 0
|
|
match x:
|
|
case (0,):
|
|
y = 0
|
|
match x,:
|
|
case z:
|
|
pass
|
|
match x, y:
|
|
case z:
|
|
pass
|
|
match x, y,:
|
|
case z:
|
|
pass
|
|
|
|
# PatternMatchSingleton
|
|
match x:
|
|
case None:
|
|
...
|
|
case True:
|
|
...
|
|
case False:
|
|
...
|
|
|
|
# PatternMatchValue
|
|
match x:
|
|
case a.b:
|
|
...
|
|
case a.b.c:
|
|
...
|
|
case '':
|
|
...
|
|
case b'':
|
|
...
|
|
case 1:
|
|
...
|
|
case 1.0:
|
|
...
|
|
case 1.0J:
|
|
...
|
|
case 1 + 1j:
|
|
...
|
|
case -1:
|
|
...
|
|
case -1.:
|
|
...
|
|
case -0b01:
|
|
...
|
|
case (1):
|
|
...
|
|
|
|
# PatternMatchOr
|
|
match x:
|
|
case 1 | 2:
|
|
...
|
|
case '' | 1.1 | -1 | 1 + 1j | a.b:
|
|
...
|
|
|
|
# PatternMatchAs
|
|
match x:
|
|
case a:
|
|
...
|
|
case a as b:
|
|
...
|
|
case 1 | 2 as two:
|
|
...
|
|
case 1 + 3j as sum:
|
|
...
|
|
case a.b as ab:
|
|
...
|
|
case _:
|
|
...
|
|
case _ as x:
|
|
...
|
|
|
|
# PatternMatchSequence
|
|
match x:
|
|
case 1, 2, 3:
|
|
...
|
|
case (1, 2, 3,):
|
|
...
|
|
case (1 + 2j, a, None, a.b):
|
|
...
|
|
case (1 as X, b) as S:
|
|
...
|
|
case [1, 2, 3 + 1j]:
|
|
...
|
|
case ([1,2], 3):
|
|
...
|
|
case [1]:
|
|
...
|
|
|
|
# PatternMatchStar
|
|
match x:
|
|
case *a,:
|
|
...
|
|
case *_,:
|
|
...
|
|
case [1, 2, *rest]:
|
|
...
|
|
case (*_, 1, 2):
|
|
...
|
|
|
|
# PatternMatchClass
|
|
match x:
|
|
case Point():
|
|
...
|
|
case a.b.Point():
|
|
...
|
|
case Point2D(x=0):
|
|
...
|
|
case Point2D(x=0, y=0,):
|
|
...
|
|
case Point2D(0, 1):
|
|
...
|
|
case Point2D([0, 1], y=1):
|
|
...
|
|
case Point2D(x=[0, 1], y=1):
|
|
...
|
|
|
|
# PatternMatchMapping
|
|
match x := b:
|
|
case {1: _}:
|
|
...
|
|
case {'': a, None: (1, 2), **rest}:
|
|
...
|
|
|
|
# Pattern guard
|
|
match y:
|
|
case a if b := c: ...
|
|
case e if 1 < 2: ...
|
|
|
|
# `match` as an identifier
|
|
match *a + b, c # ((match * a) + b), c
|
|
match *(a + b), c # (match * (a + b)), c
|
|
match (*a + b, c) # match ((*(a + b)), c)
|
|
match -a * b + c # (match - (a * b)) + c
|
|
match -(a * b) + c # (match - (a * b)) + c
|
|
match (-a) * b + c # (match (-(a * b))) + c
|
|
match ().a # (match()).a
|
|
match (()).a # (match(())).a
|
|
match ((),).a # (match(())).a
|
|
match [a].b # (match[a]).b
|
|
match [a,].b # (match[(a,)]).b (not (match[a]).b)
|
|
match [(a,)].b # (match[(a,)]).b
|
|
match()[a:
|
|
b] # (match())[a: b]
|
|
if match := 1: pass
|
|
match match:
|
|
case 1: pass
|
|
case 2:
|
|
pass
|
|
match = lambda query: query == event
|
|
print(match(12))
|