Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ylib2to3 -- Fork lib2to3 with some blib2to3 backports; add structured pattern matching (match) and parenthesized-context-managers statement support #1067

Merged
merged 103 commits into from
Jun 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
103 commits
Select commit Hold shift + click to select a range
a0b51d2
Add ylib2to3
char101 Dec 18, 2021
881000e
Yapf modifications to support match statement
char101 Dec 18, 2021
586c299
Disable double indent when splitting parameters after keyword
char101 Dec 18, 2021
d17eb60
Update readme
char101 Dec 18, 2021
49609c1
sync with blib2to3
char101 Jan 14, 2022
bf0c816
Fix comment indent inside match
char101 Feb 26, 2023
36a32e6
Sync with blib2to3
char101 Feb 26, 2023
a2b9464
Misc changes
char101 Feb 26, 2023
3f5df6c
Fix imports
char101 Feb 26, 2023
c749435
Fix typo
char101 Feb 26, 2023
b6c2342
yapf ylib2to3
Spitfire1900 Apr 19, 2023
df8eaff
Remove YAPF fork message
Spitfire1900 Apr 19, 2023
f88d351
Merge branch 'main' into 3.10
Spitfire1900 Apr 25, 2023
bfe6887
fix imports
Spitfire1900 Apr 25, 2023
e38ced1
add vscode settings to gitignore
Spitfire1900 Apr 25, 2023
e8e01ee
remove pickles
Spitfire1900 Apr 25, 2023
5e81cf4
Merge remote-tracking branch 'google/main' into 3.10
Spitfire1900 Apr 28, 2023
9595044
run yapf over the workspace
Spitfire1900 Apr 28, 2023
ced0d69
remove E501 errors
Spitfire1900 May 2, 2023
10e2c45
no more flake8 errors in yapf prime
Spitfire1900 May 2, 2023
ccab2d7
no E266 in ylib2to3
Spitfire1900 May 2, 2023
2979c5e
Revert "Disable double indent when splitting parameters after keyword"
Spitfire1900 May 2, 2023
ba1aa28
E265
Spitfire1900 May 2, 2023
faf3f92
E127
Spitfire1900 May 2, 2023
d302940
F841
Spitfire1900 May 2, 2023
e1dfeae
E722
Spitfire1900 May 2, 2023
8f6c59d
E721
Spitfire1900 May 2, 2023
fc10524
E741
Spitfire1900 May 2, 2023
d589534
E502
Spitfire1900 May 2, 2023
2dd4abb
Remove unused copy
Spitfire1900 May 2, 2023
9e1b23d
pygram - remove unused imports
Spitfire1900 May 2, 2023
75bef18
strstart and endprog
Spitfire1900 May 2, 2023
561a124
Need to add pickle txt files
Spitfire1900 May 2, 2023
af26198
F401
Spitfire1900 May 2, 2023
6cee88b
Add testMatchStatementRemoveSpacesInCaseBracket
Spitfire1900 May 2, 2023
8d7740c
Revert "Add testMatchStatementRemoveSpacesInCaseBracket"
Spitfire1900 May 2, 2023
03adcc7
testPatternMatching
Spitfire1900 May 2, 2023
c64b152
run yapf on setup.py
Spitfire1900 May 2, 2023
153df0c
format_token was still installing lib2to3
Spitfire1900 May 2, 2023
ccd5205
testpatternMatching should expect spaces between case and bracket
Spitfire1900 May 2, 2023
0331784
add credit to black
Spitfire1900 May 2, 2023
926233b
Merge branch 'main' into 3.10
Spitfire1900 May 2, 2023
4c10c07
Merge remote-tracking branch 'google/main' into 3.10
Spitfire1900 May 3, 2023
d622bf6
Merge remote-tracking branch 'google/main' into 3.10
Spitfire1900 May 5, 2023
c4b26b9
move ylib2to3 to third_party
Spitfire1900 May 5, 2023
f1cf3ca
fix package_data
Spitfire1900 May 5, 2023
89c46b6
add LICENSE
Spitfire1900 May 5, 2023
8edbbbb
different keys for each third_party package
Spitfire1900 May 5, 2023
155e560
remove trailing comma
Spitfire1900 May 5, 2023
0e86a24
place *.pickle files in tmp dir
Spitfire1900 May 5, 2023
71deeb2
try to fix PyZipFile case
Spitfire1900 May 5, 2023
96ec816
add compiled pickles to package output
Spitfire1900 May 5, 2023
16bbedf
add comments
Spitfire1900 May 5, 2023
1a6b718
use post develop and install commands in setup.py
Spitfire1900 May 5, 2023
4813200
Decided that an unclean uninstall with package_data support was the b…
Spitfire1900 May 8, 2023
45d8724
finish revert of tempfile logic
Spitfire1900 May 8, 2023
1e1c9aa
Clean up some typing
Spitfire1900 May 8, 2023
279a640
refactor to yapf.third_party
Spitfire1900 May 9, 2023
6ef2507
rename testPatternMatching to testStructuredPatternMatching
Spitfire1900 May 11, 2023
68e9e2d
add test for testParenthesizedContextManagers
Spitfire1900 May 11, 2023
da30fae
E501
Spitfire1900 May 11, 2023
6338c5b
move to _ylib2to3
Spitfire1900 May 11, 2023
7a9214b
update readme
Spitfire1900 May 11, 2023
4ccee1b
add some additional notes to readme, still requires a list of backpor…
Spitfire1900 May 11, 2023
e346da8
undo changes to load_packaged_grammar and call load_grammar instead
Spitfire1900 May 12, 2023
6403ffc
add some pydoc to _generate_pickle_name()
Spitfire1900 May 15, 2023
ec5e4e7
add summary.
Spitfire1900 May 15, 2023
c7368e2
PoC impl. -- write pickle files to user-specific cache dir
Spitfire1900 May 15, 2023
ace0391
pydoc cleanup
Spitfire1900 May 15, 2023
ea23c1b
ci was passing because tomli was installed by pytest
Spitfire1900 May 15, 2023
c8363ab
for now install dependencies manually
Spitfire1900 May 15, 2023
d46f501
use PKG_INFO.ini
Spitfire1900 May 15, 2023
7fa40a5
get package data from including config ini file
Spitfire1900 May 15, 2023
5df77a8
Revert "for now install dependencies manually"
Spitfire1900 May 15, 2023
b56ccb3
install workdir before running pytest
Spitfire1900 May 15, 2023
30d4eac
github ci debugging
Spitfire1900 May 15, 2023
909e149
do not do fancy things with modules
Spitfire1900 May 15, 2023
787d3b1
fixup
Spitfire1900 May 15, 2023
c40eb35
use https://pypi.org/project/importlib-metadata
Spitfire1900 May 15, 2023
04ec7c0
do not use __cache_dir__ as only one function cares about it
Spitfire1900 May 16, 2023
2fd1b44
use metadata for yapf.__version__
Spitfire1900 May 16, 2023
ff7b2da
install YAPF as it's own step in GitHub CI
Spitfire1900 May 16, 2023
f409564
remove PKG_INFO.ini, using importlib_metadata
Spitfire1900 May 16, 2023
1600deb
correct _ylib2to3 package pydoc
Spitfire1900 May 16, 2023
6803b52
_ylib2to3, not ylib2to3 in get_data() call
Spitfire1900 May 16, 2023
e4ed3ec
accidentally removed importlib-metadata from install_requires
Spitfire1900 May 16, 2023
b53b22d
apply isort config from #1093
Spitfire1900 May 16, 2023
b05cf2e
Only exclude .vscode/settings.json in the git root directory (#1)
Spitfire1900 May 18, 2023
203c6da
Merge google/yapf#1067, treat yapf.third_party as a third_party in is…
Spitfire1900 May 19, 2023
f9a4d25
Merge remote-tracking branch 'google/main' into 3.10
Spitfire1900 May 19, 2023
5f3e560
move known_third_party ahead of known_yapftests
Spitfire1900 May 19, 2023
e55ab26
yapf is being installed twice
Spitfire1900 May 19, 2023
44703f4
no verbose output
Spitfire1900 May 19, 2023
b3efc3a
Merge remote-tracking branch 'google/main' into 3.10
Spitfire1900 May 26, 2023
5ea5b92
Move yapf.third_party to third_party, install at yapf_third_party though
Spitfire1900 May 26, 2023
2cf7cb7
Merge remote-tracking branch 'google/main' into 3.10
Spitfire1900 May 27, 2023
3b1883b
fixup ci.yml
Spitfire1900 May 27, 2023
bcb78ad
dedent yaml
Spitfire1900 May 27, 2023
1c8616e
install to system instead of user
Spitfire1900 May 27, 2023
1b85529
if pip is going to be upgraded it needs to be upgraded first
Spitfire1900 May 27, 2023
a522ce3
better GH workflow naming
Spitfire1900 May 27, 2023
8066d4e
Revert "better GH workflow naming"
Spitfire1900 May 27, 2023
59760c3
better GH workflow naming
Spitfire1900 May 27, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 14 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions

name: YAPF
name: Test with pytest

on:
pull_request:
Expand All @@ -20,11 +20,17 @@ jobs:
uses: actions/setup-python@57ded4d7d5e986d7296eab16560982c6dd7c923b # v4.6.0
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
- name: Test with pytest
run: |
pip install pytest
pip install pytest-cov
- name: Upgrade pip
run: >-
python -m pip install
--upgrade
--disable-pip-version-check
pip
- name: Perform package installs
run: >-
pip install
.
pytest
pytest-cov
- name: Test with pytest
run: pytest
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
*~
# Merge files created by git.
*.orig
# Byte compiled python modules.
# Compiled python.
*.pyc
*.pickle
# vim swap files
.*.sw?
.sw?
Expand Down
1 change: 1 addition & 0 deletions .isort.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[settings]
force_single_line=true
known_third_party=yapf_third_party
known_yapftests=yapftests

sections=FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER,YAPFTESTS
25 changes: 19 additions & 6 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@
from setuptools import find_packages
from setuptools import setup

import yapf


class RunTests(Command):
user_options = []
Expand All @@ -44,7 +42,7 @@ def run(self):
with codecs.open('README.rst', 'r', 'utf-8') as fd:
setup(
name='yapf',
version=yapf.__version__,
version='0.33.0',
description='A formatter for Python code.',
url='https://github.com/google/yapf',
long_description=fd.read(),
Expand All @@ -55,7 +53,9 @@ def run(self):
options={'bdist_wheel': {
'python_tag': 'py3'
}},
packages=find_packages('.'),
packages=find_packages(where='.', include=['yapf*', 'yapftests*']) +
find_packages(where='third_party'),
package_dir={'yapf_third_party': 'third_party/yapf_third_party'},
project_urls={
'Source': 'https://github.com/google/yapf',
},
Expand All @@ -78,12 +78,25 @@ def run(self):
entry_points={
'console_scripts': [
'yapf = yapf:run_main',
'yapf-diff = third_party.yapf_diff.yapf_diff:main',
'yapf-diff = yapf_third_party.yapf_diff.yapf_diff:main',
],
},
cmdclass={
'test': RunTests,
},
package_data={
'yapf_third_party': [
'yapf_diff/LICENSE',
'_ylib2to3/Grammar.txt',
'_ylib2to3/PatternGrammar.txt',
'_ylib2to3/LICENSE',
]
},
include_package_data=True,
python_requires='>=3.7',
install_requires=['tomli>=2.0.1'],
install_requires=[
'importlib-metadata>=6.6.0',
'platformdirs>=3.5.1',
'tomli>=2.0.1',
],
)
252 changes: 252 additions & 0 deletions third_party/yapf_third_party/_ylib2to3/Grammar.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
# Grammar for 2to3. This grammar supports Python 2.x and 3.x.

# NOTE WELL: You should also follow all the steps listed at
# https://devguide.python.org/grammar/

# Start symbols for the grammar:
# file_input is a module or sequence of commands read from an input file;
# single_input is a single interactive statement;
# eval_input is the input for the eval() and input() functions.
# NB: compound_stmt in single_input is followed by extra NEWLINE!
file_input: (NEWLINE | stmt)* ENDMARKER
single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE
eval_input: testlist NEWLINE* ENDMARKER

decorator: '@' namedexpr_test NEWLINE
decorators: decorator+
decorated: decorators (classdef | funcdef | async_funcdef)
async_funcdef: ASYNC funcdef
funcdef: 'def' NAME parameters ['->' test] ':' suite
parameters: '(' [typedargslist] ')'

# The following definition for typedarglist is equivalent to this set of rules:
#
# arguments = argument (',' argument)*
# argument = tfpdef ['=' test]
# kwargs = '**' tname [',']
# args = '*' [tname_star]
# kwonly_kwargs = (',' argument)* [',' [kwargs]]
# args_kwonly_kwargs = args kwonly_kwargs | kwargs
# poskeyword_args_kwonly_kwargs = arguments [',' [args_kwonly_kwargs]]
# typedargslist_no_posonly = poskeyword_args_kwonly_kwargs | args_kwonly_kwargs
# typedarglist = arguments ',' '/' [',' [typedargslist_no_posonly]])|(typedargslist_no_posonly)"
#
# It needs to be fully expanded to allow our LL(1) parser to work on it.

typedargslist: tfpdef ['=' test] (',' tfpdef ['=' test])* ',' '/' [
',' [((tfpdef ['=' test] ',')* ('*' [tname_star] (',' tname ['=' test])*
[',' ['**' tname [',']]] | '**' tname [','])
| tfpdef ['=' test] (',' tfpdef ['=' test])* [','])]
] | ((tfpdef ['=' test] ',')* ('*' [tname_star] (',' tname ['=' test])*
[',' ['**' tname [',']]] | '**' tname [','])
| tfpdef ['=' test] (',' tfpdef ['=' test])* [','])

tname: NAME [':' test]
tname_star: NAME [':' (test|star_expr)]
tfpdef: tname | '(' tfplist ')'
tfplist: tfpdef (',' tfpdef)* [',']

# The following definition for varargslist is equivalent to this set of rules:
#
# arguments = argument (',' argument )*
# argument = vfpdef ['=' test]
# kwargs = '**' vname [',']
# args = '*' [vname]
# kwonly_kwargs = (',' argument )* [',' [kwargs]]
# args_kwonly_kwargs = args kwonly_kwargs | kwargs
# poskeyword_args_kwonly_kwargs = arguments [',' [args_kwonly_kwargs]]
# vararglist_no_posonly = poskeyword_args_kwonly_kwargs | args_kwonly_kwargs
# varargslist = arguments ',' '/' [','[(vararglist_no_posonly)]] | (vararglist_no_posonly)
#
# It needs to be fully expanded to allow our LL(1) parser to work on it.

varargslist: vfpdef ['=' test ](',' vfpdef ['=' test])* ',' '/' [',' [
((vfpdef ['=' test] ',')* ('*' [vname] (',' vname ['=' test])*
[',' ['**' vname [',']]] | '**' vname [','])
| vfpdef ['=' test] (',' vfpdef ['=' test])* [','])
]] | ((vfpdef ['=' test] ',')*
('*' [vname] (',' vname ['=' test])* [',' ['**' vname [',']]]| '**' vname [','])
| vfpdef ['=' test] (',' vfpdef ['=' test])* [','])

vname: NAME
vfpdef: vname | '(' vfplist ')'
vfplist: vfpdef (',' vfpdef)* [',']

stmt: simple_stmt | compound_stmt
simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
small_stmt: (expr_stmt | print_stmt | del_stmt | pass_stmt | flow_stmt |
import_stmt | global_stmt | exec_stmt | assert_stmt)
expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) |
('=' (yield_expr|testlist_star_expr))*)
annassign: ':' test ['=' (yield_expr|testlist_star_expr)]
testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [',']
augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' |
'<<=' | '>>=' | '**=' | '//=')
# For normal and annotated assignments, additional restrictions enforced by the interpreter
print_stmt: 'print' ( [ test (',' test)* [','] ] |
'>>' test [ (',' test)+ [','] ] )
del_stmt: 'del' exprlist
pass_stmt: 'pass'
flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt
break_stmt: 'break'
continue_stmt: 'continue'
return_stmt: 'return' [testlist_star_expr]
yield_stmt: yield_expr
raise_stmt: 'raise' [test ['from' test | ',' test [',' test]]]
import_stmt: import_name | import_from
import_name: 'import' dotted_as_names
import_from: ('from' ('.'* dotted_name | '.'+)
'import' ('*' | '(' import_as_names ')' | import_as_names))
import_as_name: NAME ['as' NAME]
dotted_as_name: dotted_name ['as' NAME]
import_as_names: import_as_name (',' import_as_name)* [',']
dotted_as_names: dotted_as_name (',' dotted_as_name)*
dotted_name: NAME ('.' NAME)*
global_stmt: ('global' | 'nonlocal') NAME (',' NAME)*
exec_stmt: 'exec' expr ['in' test [',' test]]
assert_stmt: 'assert' test [',' test]

compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated | async_stmt | match_stmt
async_stmt: ASYNC (funcdef | with_stmt | for_stmt)
if_stmt: 'if' namedexpr_test ':' suite ('elif' namedexpr_test ':' suite)* ['else' ':' suite]
while_stmt: 'while' namedexpr_test ':' suite ['else' ':' suite]
for_stmt: 'for' exprlist 'in' testlist_star_expr ':' suite ['else' ':' suite]
try_stmt: ('try' ':' suite
((except_clause ':' suite)+
['else' ':' suite]
['finally' ':' suite] |
'finally' ':' suite))
with_stmt: 'with' asexpr_test (',' asexpr_test)* ':' suite

# NB compile.c makes sure that the default except clause is last
except_clause: 'except' ['*'] [test [(',' | 'as') test]]
suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT

# Backward compatibility cruft to support:
# [ x for x in lambda: True, lambda: False if x() ]
# even while also allowing:
# lambda x: 5 if x else 2
# (But not a mix of the two)
testlist_safe: old_test [(',' old_test)+ [',']]
old_test: or_test | old_lambdef
old_lambdef: 'lambda' [varargslist] ':' old_test

namedexpr_test: asexpr_test [':=' asexpr_test]

# This is actually not a real rule, though since the parser is very
# limited in terms of the strategy about match/case rules, we are inserting
# a virtual case (<expr> as <expr>) as a valid expression. Unless a better
# approach is thought, the only side effect of this seem to be just allowing
# more stuff to be parser (which would fail on the ast).
asexpr_test: test ['as' test]

test: or_test ['if' or_test 'else' test] | lambdef
or_test: and_test ('or' and_test)*
and_test: not_test ('and' not_test)*
not_test: 'not' not_test | comparison
comparison: expr (comp_op expr)*
comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not'
star_expr: '*' expr
expr: xor_expr ('|' xor_expr)*
xor_expr: and_expr ('^' and_expr)*
and_expr: shift_expr ('&' shift_expr)*
shift_expr: arith_expr (('<<'|'>>') arith_expr)*
arith_expr: term (('+'|'-') term)*
term: factor (('*'|'@'|'/'|'%'|'//') factor)*
factor: ('+'|'-'|'~') factor | power
power: [AWAIT] atom trailer* ['**' factor]
atom: ('(' [yield_expr|testlist_gexp] ')' |
'[' [listmaker] ']' |
'{' [dictsetmaker] '}' |
'`' testlist1 '`' |
NAME | NUMBER | STRING+ | '.' '.' '.')
listmaker: (namedexpr_test|star_expr) ( old_comp_for | (',' (namedexpr_test|star_expr))* [','] )
testlist_gexp: (namedexpr_test|star_expr) ( old_comp_for | (',' (namedexpr_test|star_expr))* [','] )
lambdef: 'lambda' [varargslist] ':' test
trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
subscriptlist: (subscript|star_expr) (',' (subscript|star_expr))* [',']
subscript: test [':=' test] | [test] ':' [test] [sliceop]
sliceop: ':' [test]
exprlist: (expr|star_expr) (',' (expr|star_expr))* [',']
testlist: test (',' test)* [',']
dictsetmaker: ( ((test ':' asexpr_test | '**' expr)
(comp_for | (',' (test ':' asexpr_test | '**' expr))* [','])) |
((test [':=' test] | star_expr)
(comp_for | (',' (test [':=' test] | star_expr))* [','])) )

classdef: 'class' NAME ['(' [arglist] ')'] ':' suite

arglist: argument (',' argument)* [',']

# "test '=' test" is really "keyword '=' test", but we have no such token.
# These need to be in a single rule to avoid grammar that is ambiguous
# to our LL(1) parser. Even though 'test' includes '*expr' in star_expr,
# we explicitly match '*' here, too, to give it proper precedence.
# Illegal combinations and orderings are blocked in ast.c:
# multiple (test comp_for) arguments are blocked; keyword unpackings
# that precede iterable unpackings are blocked; etc.
argument: ( test [comp_for] |
test ':=' test [comp_for] |
test 'as' test |
test '=' asexpr_test |
'**' test |
'*' test )

comp_iter: comp_for | comp_if
comp_for: [ASYNC] 'for' exprlist 'in' or_test [comp_iter]
comp_if: 'if' old_test [comp_iter]

# As noted above, testlist_safe extends the syntax allowed in list
# comprehensions and generators. We can't use it indiscriminately in all
# derivations using a comp_for-like pattern because the testlist_safe derivation
# contains comma which clashes with trailing comma in arglist.
#
# This was an issue because the parser would not follow the correct derivation
# when parsing syntactically valid Python code. Since testlist_safe was created
# specifically to handle list comprehensions and generator expressions enclosed
# with parentheses, it's safe to only use it in those. That avoids the issue; we
# can parse code like set(x for x in [],).
#
# The syntax supported by this set of rules is not a valid Python 3 syntax,
# hence the prefix "old".
#
# See https://bugs.python.org/issue27494
old_comp_iter: old_comp_for | old_comp_if
old_comp_for: [ASYNC] 'for' exprlist 'in' testlist_safe [old_comp_iter]
old_comp_if: 'if' old_test [old_comp_iter]

testlist1: test (',' test)*

# not used in grammar, but may appear in "node" passed from Parser to Compiler
encoding_decl: NAME

yield_expr: 'yield' [yield_arg]
yield_arg: 'from' test | testlist_star_expr


# 3.10 match statement definition

# PS: normally the grammar is much much more restricted, but
# at this moment for not trying to bother much with encoding the
# exact same DSL in a LL(1) parser, we will just accept an expression
# and let the ast.parse() step of the safe mode to reject invalid
# grammar.

# The reason why it is more restricted is that, patterns are some
# sort of a DSL (more advanced than our LHS on assignments, but
# still in a very limited python subset). They are not really
# expressions, but who cares. If we can parse them, that is enough
# to reformat them.

match_stmt: "match" subject_expr ':' NEWLINE INDENT case_block+ DEDENT

# This is more permissive than the actual version. For example it
# accepts `match *something:`, even though single-item starred expressions
# are forbidden.
subject_expr: (namedexpr_test|star_expr) (',' (namedexpr_test|star_expr))* [',']

# cases
case_block: "case" patterns [guard] ':' suite
guard: 'if' namedexpr_test
patterns: pattern (',' pattern)* [',']
pattern: (expr|star_expr) ['as' expr]
Loading