Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 개선 제안 한국어 번역

PEP 634 – 구조적 패턴 매칭: 사양

Author:
Brandt Bucher <brandt at python.org>, Guido van Rossum <guido at python.org>
BDFL-Delegate:

Discussions-To:
Python-Dev list
Status:
Final
Type:
Standards Track
Created:
12-Sep-2020
Python-Version:
3.10
Post-History:
22-Oct-2020, 08-Feb-2021
Replaces:
622
Resolution:
Python-Committers message

Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain or CC0-1.0, whichever is more permissive 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

Important

This PEP is a historical document. The up-to-date, canonical documentation can now be found at The match statement.

×

See PEP 1 for how to propose changes.

초록

이 PEP는 match 문의 기술 사양을 제공합니다. 이는 PEP 622을 대체하며, 해당 문서는 여기에서 세 부분으로 나뉩니다:

이 PEP에는 의도적으로 주석을 포함하지 않았으며, 설계 선택의 동기와 모든 설명은 PEP 635에 있습니다. 처음 읽는 독자는 개념, 패턴의 구문 및 의미론을 더 쉽게 소개하는 PEP 636부터 시작하는 것이 좋습니다.

구문 및 의미론

완전한 문법은 부록 A를 참조하십시오.

개요 및 용어

패턴 매칭 프로세스는 패턴(case뒤에 옴)과 대상 값(match뒤에 옴)을 입력으로 받습니다. 이 프로세스를 설명하는 표현으로는 “패턴이 대상 값과 일치한다(또는 대상 값에 대해 매칭된다)”와 “대상 값에 대해(또는 대상 값과) 패턴을 매칭한다”가 있습니다.

패턴 매칭의 주요 결과는 성공 또는 실패입니다. 성공한 경우 “패턴이 성공한다”, “매칭이 성공한다” 또는 “패턴이 대상 값과 일치한다”고 말할 수 있습니다.

많은 경우 패턴은 서브패턴을 포함하며, 성공 또는 실패는 해당 서브패턴을 값에 대해(예: OR 패턴의 경우) 또는 값의 일부에 대해(예: 시퀀스 패턴의 경우) 매칭한 결과에 따라 결정됩니다. 이 프로세스는 일반적으로 전체 결과가 결정될 때까지 서브패턴을 왼쪽에서 오른쪽으로 처리합니다. 예를 들어 OR 패턴은 처음으로 성공하는 서브패턴에서 성공하는 반면, 시퀀스 패턴은 처음으로 실패하는 서브패턴에서 실패합니다.

패턴 매칭의 부차적인 결과로 하나 이상의 이름 바인딩이 발생할 수 있습니다. “패턴이 값을 이름에 바인딩한다”고 말할 수 있습니다. 서브패턴을 처음 성공할 때까지 시도하는 경우에는 성공한 서브패턴으로 인한 바인딩만 유효하며, 처음 실패할 때까지 시도하는 경우에는 바인딩이 병합됩니다. 아래에서 설명하는 몇 가지 추가 규칙이 이러한 경우에 적용됩니다.

Match 문

구문:

match_stmt: "match" subject_expr ':' NEWLINE INDENT case_block+ DEDENT
subject_expr:
    | star_named_expression ',' star_named_expressions?
    | named_expression
case_block: "case" patterns [guard] ':' block
guard: 'if' named_expression

star_named_expression, star_named_expressions, named_expressionblock 규칙은 표준 Python 문법의 일부입니다.

patterns 규칙은 아래에 지정되어 있습니다.

문맥상 match_stmtcompound_statement의 새로운 대안입니다:

compound_statement:
    | if_stmt
    ...
    | match_stmt

matchcase 키워드는 소프트 키워드입니다. 즉, 다른 문법적 문맥에서는 예약어가 아니며, 예상되는 위치에 콜론이 없는 경우 줄의 시작 부분에서도 예약어가 아닙니다. 이는 해당 키워드가 match 문 또는 case 블록의 일부일 때만 키워드로 인식되며, 그 밖의 모든 문맥에서는 변수명이나 인자명으로 사용할 수 있음을 의미합니다.

Match 의미론

match 문은 먼저 subject 식을 평가합니다. 쉼표가 있으면 표준 규칙에 따라 튜플을 구성합니다.

그런 다음 결과 subject 값을 사용하여, 해당 값과 패턴이 성공적으로 일치하고 가드 조건이 (있는 경우) “truthy”인 첫 번째 case 블록을 선택합니다. 조건을 충족하는 case 블록이 없으면 match 문이 완료되고, 그렇지 않으면 선택된 case 블록의 블록을 실행합니다. 복합 문 안에 중첩된 블록을 실행할 때의 일반적인 규칙이 적용됩니다(예: if 문).

성공적인 패턴 일치 중에 만들어진 이름 바인딩은 실행된 블록이 끝난 후에도 유지되며 match 문 이후에 사용할 수 있습니다.

패턴 일치가 실패하는 동안에도 일부 하위 패턴은 성공할 수 있습니다. 예를 들어 값 [0, 1, 2]을 패턴 (0, x, 1)과 일치시킬 때, 리스트 요소를 왼쪽에서 오른쪽으로 일치시키면 하위 패턴 x가 성공할 수 있습니다. 구현은 이러한 부분 일치에 대한 바인딩을 지속적으로 만들 수도 있고 만들지 않을 수도 있습니다. match 문을 포함하는 사용자 코드는 실패한 일치에 대해 바인딩이 만들어진다고 의존해서는 안 되며, 실패한 일치로 인해 변수가 변경되지 않는다고 가정해서도 안 됩니다. 동작의 이 부분은 서로 다른 구현이 최적화를 추가할 수 있도록, 또한 이 기능의 확장성을 제한할 수 있는 의미적 제약이 도입되는 것을 방지하도록 의도적으로 명세하지 않습니다.

정확한 패턴 바인딩 규칙은 패턴 유형마다 다르며 아래에 명시되어 있습니다.

가드

case 블록에 가드가 있으면 case 블록의 패턴이 성공한 후 가드의 식을 평가합니다. 이 과정에서 예외가 발생하면 예외가 상위로 전파됩니다. 그렇지 않으면 조건이 “truthy”인 경우 case 블록을 선택하고, “falsy”인 경우 case 블록을 선택하지 않습니다.

가드는 식이므로 부작용을 일으킬 수 있습니다. 가드 평가는 첫 번째 case 블록부터 마지막 case 블록까지 한 번에 하나씩 진행하며, 패턴이 모두 성공하지 않는 case 블록은 건너뜁니다. (즉, 해당 패턴의 성공 여부를 판단하는 과정이 순서와 무관하게 진행될 수 있더라도 가드 평가는 순서대로 진행되어야 합니다.) case 블록이 선택되면 가드 평가를 중지해야 합니다.

반박 불가능한 case 블록

문법만으로 항상 성공함을 증명할 수 있으면 패턴을 반박 불가능하다고 간주합니다. 특히 캡처 패턴과 와일드카드 패턴은 반박 불가능하며, 왼쪽이 반박 불가능한 AS 패턴, 하나 이상의 반박 불가능한 패턴을 포함하는 OR 패턴, 괄호로 묶인 반박 불가능한 패턴도 그러합니다.

가드가 없고 패턴이 반박 불가능하면 case 블록을 반박 불가능하다고 간주합니다.

match 문에는 반박 불가능한 case 블록이 최대 하나만 있을 수 있으며, 반드시 마지막에 와야 합니다.

패턴

패턴의 최상위 문법은 다음과 같습니다.:

patterns: open_sequence_pattern | pattern
pattern: as_pattern | or_pattern
as_pattern: or_pattern 'as' capture_pattern
or_pattern: '|'.closed_pattern+
closed_pattern:
    | literal_pattern
    | capture_pattern
    | wildcard_pattern
    | value_pattern
    | group_pattern
    | sequence_pattern
    | mapping_pattern
    | class_pattern

AS 패턴

구문:

as_pattern: or_pattern 'as' capture_pattern

(참고: 오른쪽의 이름은 _일 수 없습니다.)

AS 패턴은 as 키워드의 왼쪽에 있는 OR 패턴을 subject와 일치시킵니다. 이 작업이 실패하면 AS 패턴도 실패합니다. 그렇지 않으면 AS 패턴은 as 키워드의 오른쪽에 있는 이름에 subject를 바인딩하고 성공합니다.

OR 패턴

구문:

or_pattern: '|'.closed_pattern+

두 개 이상의 패턴이 세로 막대(|)로 구분되면 이를 OR 패턴이라고 합니다. (닫힌 패턴 하나만 있는 경우에는 그렇지 않습니다.)

마지막 서브패턴만 반박 불가능할 수 있습니다.

각 서브패턴은 동일한 이름 집합을 바인딩해야 합니다.

OR 패턴은 서브패턴 중 하나가 성공할 때까지 각 서브패턴을 차례로 대상에 매칭합니다. 그러면 OR 패턴은 성공한 것으로 간주합니다. 서브패턴 중 어느 것도 성공하지 못하면 OR 패턴은 실패합니다.

리터럴 패턴

구문:

literal_pattern:
    | signed_number
    | signed_number '+' NUMBER
    | signed_number '-' NUMBER
    | strings
    | 'None'
    | 'True'
    | 'False'
signed_number: NUMBER | '-' NUMBER

strings 규칙과 NUMBER 토큰은 표준 Python 문법에 정의되어 있습니다.

삼중 따옴표 문자열을 지원합니다. 원시 문자열과 바이트 문자열을 지원합니다. F-문자열은 지원하지 않습니다.

signed_number '+' NUMBERsigned_number '-' NUMBER 형식은 복소수를 표현하는 경우에만 허용되며, 왼쪽에는 실수, 오른쪽에는 허수가 있어야 합니다.

리터럴 패턴은 다음 비교 규칙을 사용하여 대상 값이 리터럴로 표현된 값과 같다고 비교되면 성공합니다.

  • 숫자와 문자열은 == 연산자를 사용하여 비교합니다.
  • 단일 리터럴 None, TrueFalseis 연산자를 사용하여 비교합니다.

캡처 패턴

구문:

capture_pattern: !"_" NAME

단일 밑줄(_)는 캡처 패턴이 아닙니다(이는 !"_"로 표현됩니다). 이는 Wildcard Pattern으로 처리합니다.

캡처 패턴은 항상 성공합니다. 캡처 패턴은 PEP 572에서 월러스 연산자에 대해 정립된 이름 바인딩의 스코프 규칙을 사용하여 대상 값을 이름에 바인딩합니다. (요약하면, 적용 가능한 nonlocal 또는 global 문이 없는 한 해당 이름은 가장 가까운 바깥 함수 스코프의 지역 변수가 됩니다.)

주어진 패턴에서 각 이름은 한 번만 바인딩될 수 있습니다. 따라서 예를 들어 case x, x: ...는 허용되지 않지만, case [x] | x: ...는 허용됩니다.

와일드카드 패턴

구문:

wildcard_pattern: "_"

와일드카드 패턴은 항상 성공합니다. 어떤 이름도 바인딩하지 않습니다.

값 패턴

구문:

value_pattern: attr
attr: name_or_attr '.' NAME
name_or_attr: attr | NAME

표준 Python 이름 해석 규칙을 사용하여 패턴의 점으로 구분된 이름을 조회합니다. 그러나 동일한 match 문에서 동일한 값 패턴이 여러 번 나타나는 경우, 인터프리터는 동일한 조회를 반복하는 대신 처음 찾은 값을 캐시하여 재사용할 수 있습니다. (명확히 하자면, 이 캐시는 특정 match 문의 특정 실행에 엄격히 연결됩니다.)

이렇게 찾은 값이 == 연산자를 사용하여 대상 값과 같다고 비교되면 패턴이 성공합니다.

그룹 패턴

구문:

group_pattern: '(' pattern ')'

(pattern의 구문은 위의 패턴을 참조하십시오. 여기에는 쉼표가 없습니다 – 쉼표가 하나 이상 있는 괄호로 묶인 항목의 나열은 ()와 마찬가지로 시퀀스 패턴입니다.)

괄호로 묶인 패턴에는 추가 구문이 없습니다. 사용자는 의도한 그룹화를 강조하기 위해 패턴 주위에 괄호를 추가할 수 있습니다.

시퀀스 패턴

구문:

sequence_pattern:
  | '[' [maybe_sequence_pattern] ']'
  | '(' [open_sequence_pattern] ')'
open_sequence_pattern: maybe_star_pattern ',' [maybe_sequence_pattern]
maybe_sequence_pattern: ','.maybe_star_pattern+ ','?
maybe_star_pattern: star_pattern | pattern
star_pattern: '*' (capture_pattern | wildcard_pattern)

(후행 쉼표가 없는 하나의 괄호로 묶인 패턴은 시퀀스 패턴이 아니라 그룹 패턴입니다. 그러나 [...]로 묶인 하나의 패턴은 여전히 시퀀스 패턴입니다.)

[...]를 사용하는 시퀀스 패턴, (...)를 사용하는 시퀀스 패턴, 개방 시퀀스 패턴 사이에는 의미상 차이가 없습니다.

시퀀스 패턴에는 별표 서브패턴이 최대 하나 포함될 수 있습니다. 별표 서브패턴은 어느 위치에나 올 수 있습니다. 별표 서브패턴이 없으면 시퀀스 패턴은 고정 길이 시퀀스 패턴이고, 그렇지 않으면 가변 길이 시퀀스 패턴입니다.

시퀀스 패턴이 성공하려면 대상이 시퀀스여야 하며, 여기서 시퀀스라는 것은 해당 클래스가 다음 중 하나인 경우로 정의됩니다:

  • collections.abc.Sequence를 상속하는 클래스
  • collections.abc.Sequence로 등록된 Python 클래스
  • Py_TPFLAGS_SEQUENCE 비트가 설정된 내장 클래스
  • 위 항목 중 어느 하나를 상속하는 클래스(부모의 Sequence 등록 이전에 정의된 클래스를 포함)

다음 표준 라이브러리 클래스에는 Py_TPFLAGS_SEQUENCE 비트가 설정됩니다:

  • array.array
  • collections.deque
  • list
  • memoryview
  • range
  • tuple

Note

일반적으로 시퀀스로 간주되는 str, bytes, bytearray는 위 목록에 포함되지 않으며 시퀀스 패턴과 일치하지 않습니다.

대상 시퀀스의 길이가 서브패턴 수와 같지 않으면 고정 길이 시퀀스 패턴은 실패합니다.

대상 시퀀스의 길이가 별표가 아닌 서브패턴 수보다 작으면 가변 길이 시퀀스 패턴은 실패합니다.

대상 시퀀스의 길이는 내장 len() 함수를 사용하여 구합니다(즉, __len__ 프로토콜을 통해 구합니다). 그러나 인터프리터는 값 패턴에 대해 설명한 것과 유사한 방식으로 이 값을 캐시할 수 있습니다.

고정 길이 시퀀스 패턴은 왼쪽에서 오른쪽으로 서브패턴을 대상 시퀀스의 해당 항목과 일치시킵니다. 서브패턴이 실패하는 즉시 일치가 중지됩니다(실패로 처리됩니다). 모든 서브패턴이 해당 항목과의 일치에 성공하면 시퀀스 패턴이 성공합니다.

가변 길이 시퀀스 패턴은 먼저 고정 길이 시퀀스와 마찬가지로 선행하는 별표가 아닌 서브패턴을 대상 시퀀스의 해당 항목과 일치시킵니다. 이 작업이 성공하면 별표 서브패턴은 남은 대상 항목으로 구성된 리스트와 일치하며, 별표 서브패턴 뒤에 오는 별표가 아닌 서브패턴에 해당하는 항목은 끝에서 제거됩니다. 그런 다음 남은 별표가 아닌 서브패턴을 고정 길이 시퀀스와 마찬가지로 해당 대상 항목과 일치시킵니다.

매핑 패턴

구문:

mapping_pattern: '{' [items_pattern] '}'
items_pattern: ','.key_value_pattern+ ','?
key_value_pattern:
    | (literal_pattern | value_pattern) ':' pattern
    | double_star_pattern
double_star_pattern: '**' capture_pattern

(**_는 이 구문에서 허용되지 않습니다.)

매핑 패턴에는 최대 하나의 이중 별표 패턴이 포함될 수 있으며, 이 패턴은 마지막이어야 합니다.

매핑 패턴에는 중복된 키 값이 포함될 수 없습니다. (모든 키 패턴이 리터럴 패턴이면 이는 구문 오류로 간주되며, 그렇지 않으면 런타임 오류이고 ValueError가 발생합니다.)

매핑 패턴이 성공하려면 대상이 매핑이어야 하며, 매핑이라는 것은 해당 클래스가 다음 중 하나인 것으로 정의됩니다.

  • collections.abc.Mapping을 상속하는 클래스
  • collections.abc.Mapping으로 등록된 Python 클래스
  • Py_TPFLAGS_MAPPING 비트가 설정된 내장 클래스
  • 위 항목 중 어느 하나를 상속하는 클래스 (부모의 Mapping 등록 이전에 정의된 클래스를 포함)

표준 라이브러리 클래스인 dictmappingproxy에는 Py_TPFLAGS_MAPPING 비트가 설정됩니다.

매핑 패턴에 지정된 모든 키가 대상 매핑에 존재하고, 각 키의 패턴이 대상 매핑의 해당 항목과 일치하면 매핑 패턴이 성공합니다. 키는 항상 == 연산자로 비교합니다. '**' NAME 형식이 있으면 해당 이름은 대상 매핑에서 남은 키-값 쌍을 포함하는 dict에 바인딩됩니다.

매핑 패턴에서 중복 키가 발견되면 패턴은 유효하지 않은 것으로 간주되며 ValueError가 발생합니다.

키-값 쌍은 대상의 get() 메서드의 두 인자 형식을 사용하여 일치시킵니다. 따라서 일치하는 키-값 쌍은 매핑에 이미 존재해야 하며, __missing__ 또는 __getitem__으로 즉시 생성된 것이어서는 안 됩니다. 예를 들어 collections.defaultdict 인스턴스는 match 문이 시작될 때 이미 존재했던 키를 사용하는 패턴으로만 일치시킬 수 있습니다.

클래스 패턴

구문:

class_pattern:
    | name_or_attr '(' [pattern_arguments ','?] ')'
pattern_arguments:
    | positional_patterns [',' keyword_patterns]
    | keyword_patterns
positional_patterns: ','.pattern+
keyword_patterns: ','.keyword_pattern+
keyword_pattern: NAME '=' pattern

클래스 패턴에서는 동일한 키워드를 여러 번 반복할 수 없습니다.

name_or_attr가 내장 type의 인스턴스가 아니면 TypeError가 발생합니다.

대상이 name_or_attr의 인스턴스가 아니면 클래스 패턴이 실패합니다. 이는 isinstance()를 사용하여 검사합니다.

인자가 없는 경우, isinstance()에 대한 검사가 성공하면 패턴이 성공합니다. 그렇지 않으면:

  • 키워드 패턴만 있는 경우, 다음과 같이 하나씩 처리합니다:
    • 키워드를 대상의 속성으로 조회합니다.
      • 이로 인해 AttributeError이외의 예외가 발생하면 해당 예외가 상위로 전파됩니다.
      • 이로 인해 AttributeError가 발생하면 클래스 패턴이 실패합니다.
      • 그렇지 않으면 키워드에 연결된 서브패턴을 속성 값과 매칭합니다. 매칭에 실패하면 클래스 패턴이 실패합니다. 매칭에 성공하면 다음 키워드로 진행합니다.
    • 모든 키워드 패턴이 성공하면 클래스 패턴 전체가 성공합니다.
  • 위치 패턴이 하나라도 있으면 이를 키워드 패턴으로 변환하고(아래 참조), 구문상의 키워드 패턴(있는 경우)에 앞서는 추가 키워드 패턴으로 취급합니다.

위치 패턴은 다음과 같이 name_or_attr로 지정된 클래스의 __match_args__속성을 사용하여 키워드 패턴으로 변환합니다:

  • 여러 내장 타입(아래에 지정됨)의 경우, 전체 대상을 매칭하는 단일 위치 서브패턴을 허용합니다. (이 경우 키워드 패턴은 다른 타입에서와 동일하게 작동합니다.)
  • getattr(cls, "__match_args__", ()))에 해당하는 호출을 수행합니다.
  • 이로 인해 예외가 발생하면 해당 예외가 상위로 전파됩니다.
  • 반환된 값이 튜플이 아니면 변환에 실패하고 TypeError가 발생합니다.
  • 위치 패턴의 수가 len()을 사용해 얻은 __match_args__의 길이보다 많으면 TypeError가 발생합니다.
  • 그렇지 않으면 위치 패턴 i__match_args__[i]를 키워드로 사용하여 키워드 패턴으로 변환합니다. 단, 후자가 문자열이어야 하며, 그렇지 않으면 TypeError가 발생합니다.
  • 중복된 키워드가 있으면 TypeError가 발생합니다.

위치 패턴을 키워드 패턴으로 변환한 후에는 키워드 패턴만 있는 것처럼 매칭을 진행합니다.

위에서 언급했듯이, 다음 내장 타입에서는 위치 서브패턴의 처리가 다릅니다: bool, bytearray, bytes, dict, float, frozenset, int, list, set, str, tuple.

이 동작은 대략 다음과 동등합니다.:

class C:
    __match_args__ = ("__match_self_prop__",)
    @property
    def __match_self_prop__(self):
        return self

부작용 및 정의되지 않은 동작

매칭 과정에서 명시적으로 생성되는 유일한 부작용은 이름의 바인딩입니다. 그러나 이 과정은 속성 접근, 인스턴스 검사, len(), 대상과 그 일부에 대한 동등성 검사 및 항목 접근에 의존합니다. 또한 값 패턴과 클래스 패턴의 클래스 이름을 평가합니다. 이러한 동작은 일반적으로 부작용을 일으키지 않지만, 이론적으로는 부작용을 일으킬 수 있습니다. 이 제안은 어떤 메서드가 호출되는지 또는 몇 번 호출되는지에 대한 명세를 의도적으로 포함하지 않습니다. 따라서 이 동작은 정의되지 않으며 사용자 코드는 이에 의존해서는 안 됩니다.

또 다른 정의되지 않은 동작은 캡처 패턴 뒤에(같은 case 블록에서) 실패하는 다른 패턴이 이어지는 경우 캡처 패턴이 변수를 바인딩하는 것입니다. 이러한 바인딩은 구현 전략에 따라 더 일찍 또는 더 늦게 발생할 수 있으며, 유일한 제약 조건은 캡처 변수가 이를 명시적으로 사용하는 가드를 평가하기 전에 설정되어야 한다는 것입니다. 가드가 and절로 구성된 경우, 왼쪽에서 오른쪽의 평가 순서가 유지되기만 한다면 피연산자 평가는 패턴 매칭과 서로 섞여 진행될 수도 있습니다.

표준 라이브러리

패턴 매칭 사용을 용이하게 하기 위해, 표준 라이브러리에 몇 가지 변경이 이루어질 것입니다:

  • Namedtuple과 dataclass는 자동 생성된 __match_args__를 가지게 됩니다.
  • dataclass의 경우, 생성된 __match_args__의 속성 순서는 생성된 __init__() 메서드의 대응하는 인자 순서와 같습니다. 여기에는 속성이 상위 클래스로부터 상속되는 상황도 포함됩니다. init=False인 필드는 __match_args__에서 제외됩니다.

또한, 기존 표준 라이브러리 클래스들을 검토하여 도움이 될 만한 곳에 __match_args__를 추가하는 체계적인 노력이 이루어질 것입니다.

부록 A – 전체 문법

다음은 match_stmt의 전체 문법입니다. 이는 compound_stmt에 대한 추가적인 대안입니다. matchcase는 소프트 키워드, 즉 다른 문법적 맥락에서는(예상되는 위치에 콜론이 없는 경우 줄의 시작 부분을 포함하여) 예약어가 아니라는 점을 기억하십시오. 관례상, 하드 키워드는 작은따옴표를 사용하고 소프트 키워드는 큰따옴표를 사용합니다.

표준 EBNF를 넘어서 사용된 기타 표기법:

  • SEP.RULE+RULE (SEP RULE)*의 축약형입니다
  • !RULE는 부정 전방탐색 어서션입니다
match_stmt: "match" subject_expr ':' NEWLINE INDENT case_block+ DEDENT
subject_expr:
    | star_named_expression ',' [star_named_expressions]
    | named_expression
case_block: "case" patterns [guard] ':' block
guard: 'if' named_expression

patterns: open_sequence_pattern | pattern
pattern: as_pattern | or_pattern
as_pattern: or_pattern 'as' capture_pattern
or_pattern: '|'.closed_pattern+
closed_pattern:
    | literal_pattern
    | capture_pattern
    | wildcard_pattern
    | value_pattern
    | group_pattern
    | sequence_pattern
    | mapping_pattern
    | class_pattern

literal_pattern:
    | signed_number !('+' | '-')
    | signed_number '+' NUMBER
    | signed_number '-' NUMBER
    | strings
    | 'None'
    | 'True'
    | 'False'
signed_number: NUMBER | '-' NUMBER

capture_pattern: !"_" NAME !('.' | '(' | '=')

wildcard_pattern: "_"

value_pattern: attr !('.' | '(' | '=')
attr: name_or_attr '.' NAME
name_or_attr: attr | NAME

group_pattern: '(' pattern ')'

sequence_pattern:
  | '[' [maybe_sequence_pattern] ']'
  | '(' [open_sequence_pattern] ')'
open_sequence_pattern: maybe_star_pattern ',' [maybe_sequence_pattern]
maybe_sequence_pattern: ','.maybe_star_pattern+ ','?
maybe_star_pattern: star_pattern | pattern
star_pattern: '*' (capture_pattern | wildcard_pattern)

mapping_pattern: '{' [items_pattern] '}'
items_pattern: ','.key_value_pattern+ ','?
key_value_pattern:
    | (literal_pattern | value_pattern) ':' pattern
    | double_star_pattern
double_star_pattern: '**' capture_pattern

class_pattern:
    | name_or_attr '(' [pattern_arguments ','?] ')'
pattern_arguments:
    | positional_patterns [',' keyword_patterns]
    | keyword_patterns
positional_patterns: ','.pattern+
keyword_patterns: ','.keyword_pattern+
keyword_pattern: NAME '=' pattern