mirror of
https://github.com/Instagram/LibCST.git
synced 2025-12-23 10:35:53 +00:00
Implement PEP-634 - Match statement (#568)
* ParenthesizedNode implementation for Box * match statement rust CST and grammar * match statement python CST and docs * run rust unit tests in release mode for now
This commit is contained in:
parent
67db03915d
commit
9932a6d339
13 changed files with 5912 additions and 10 deletions
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
|
|
@ -260,7 +260,7 @@ jobs:
|
|||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --manifest-path=native/Cargo.toml
|
||||
args: --manifest-path=native/Cargo.toml --release
|
||||
- name: clippy
|
||||
uses: actions-rs/clippy-check@v1
|
||||
with:
|
||||
|
|
|
|||
|
|
@ -164,6 +164,23 @@ from libcst._nodes.statement import (
|
|||
ImportAlias,
|
||||
ImportFrom,
|
||||
IndentedBlock,
|
||||
Match,
|
||||
MatchAs,
|
||||
MatchCase,
|
||||
MatchClass,
|
||||
MatchKeywordElement,
|
||||
MatchList,
|
||||
MatchMapping,
|
||||
MatchMappingElement,
|
||||
MatchOr,
|
||||
MatchOrElement,
|
||||
MatchPattern,
|
||||
MatchSequence,
|
||||
MatchSequenceElement,
|
||||
MatchSingleton,
|
||||
MatchStar,
|
||||
MatchTuple,
|
||||
MatchValue,
|
||||
NameItem,
|
||||
Nonlocal,
|
||||
Pass,
|
||||
|
|
@ -380,6 +397,23 @@ __all__ = [
|
|||
"ImportAlias",
|
||||
"ImportFrom",
|
||||
"IndentedBlock",
|
||||
"Match",
|
||||
"MatchCase",
|
||||
"MatchAs",
|
||||
"MatchClass",
|
||||
"MatchKeywordElement",
|
||||
"MatchList",
|
||||
"MatchMapping",
|
||||
"MatchMappingElement",
|
||||
"MatchOr",
|
||||
"MatchOrElement",
|
||||
"MatchPattern",
|
||||
"MatchSequence",
|
||||
"MatchSequenceElement",
|
||||
"MatchSingleton",
|
||||
"MatchStar",
|
||||
"MatchTuple",
|
||||
"MatchValue",
|
||||
"NameItem",
|
||||
"Nonlocal",
|
||||
"Pass",
|
||||
|
|
|
|||
|
|
@ -6,13 +6,14 @@
|
|||
import inspect
|
||||
import re
|
||||
from abc import ABC, abstractmethod
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Optional, Pattern, Sequence, Union
|
||||
|
||||
from libcst._add_slots import add_slots
|
||||
from libcst._maybe_sentinel import MaybeSentinel
|
||||
from libcst._nodes.base import CSTNode, CSTValidationError
|
||||
from libcst._nodes.expression import (
|
||||
_BaseParenthesizedNode,
|
||||
Annotation,
|
||||
Arg,
|
||||
Asynchronous,
|
||||
|
|
@ -24,11 +25,15 @@ from libcst._nodes.expression import (
|
|||
ConcatenatedString,
|
||||
ExpressionPosition,
|
||||
From,
|
||||
LeftCurlyBrace,
|
||||
LeftParen,
|
||||
LeftSquareBracket,
|
||||
List,
|
||||
Name,
|
||||
Parameters,
|
||||
RightCurlyBrace,
|
||||
RightParen,
|
||||
RightSquareBracket,
|
||||
SimpleString,
|
||||
Tuple,
|
||||
)
|
||||
|
|
@ -40,7 +45,15 @@ from libcst._nodes.internal import (
|
|||
visit_sentinel,
|
||||
visit_sequence,
|
||||
)
|
||||
from libcst._nodes.op import AssignEqual, BaseAugOp, Comma, Dot, ImportStar, Semicolon
|
||||
from libcst._nodes.op import (
|
||||
AssignEqual,
|
||||
BaseAugOp,
|
||||
BitOr,
|
||||
Comma,
|
||||
Dot,
|
||||
ImportStar,
|
||||
Semicolon,
|
||||
)
|
||||
from libcst._nodes.whitespace import (
|
||||
BaseParenthesizableWhitespace,
|
||||
EmptyLine,
|
||||
|
|
@ -2566,3 +2579,811 @@ class Nonlocal(BaseSmallStatement):
|
|||
state.add_token("; ")
|
||||
elif isinstance(semicolon, Semicolon):
|
||||
semicolon._codegen(state)
|
||||
|
||||
|
||||
class MatchPattern(_BaseParenthesizedNode, ABC):
|
||||
"""
|
||||
A base class for anything that can appear as a pattern in a :class:`Match`
|
||||
statement.
|
||||
"""
|
||||
|
||||
|
||||
@add_slots
|
||||
@dataclass(frozen=True)
|
||||
class Match(BaseCompoundStatement):
|
||||
"""
|
||||
A ``match`` statement.
|
||||
"""
|
||||
|
||||
#: The subject of the match.
|
||||
subject: BaseExpression
|
||||
|
||||
#: A non-empty list of match cases.
|
||||
cases: Sequence["MatchCase"]
|
||||
|
||||
#: Sequence of empty lines appearing before this compound statement line.
|
||||
leading_lines: Sequence[EmptyLine] = ()
|
||||
|
||||
#: Whitespace between the ``match`` keyword and the subject.
|
||||
whitespace_after_match: SimpleWhitespace = SimpleWhitespace.field(" ")
|
||||
|
||||
#: Whitespace after the subject but before the colon.
|
||||
whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
|
||||
|
||||
#: Any optional trailing comment and the final ``NEWLINE`` at the end of the line.
|
||||
whitespace_after_colon: TrailingWhitespace = TrailingWhitespace.field()
|
||||
|
||||
#: A string represents a specific indentation. A ``None`` value uses the modules's
|
||||
#: default indentation. This is included because indentation is allowed to be
|
||||
#: inconsistent across a file, just not ambiguously.
|
||||
indent: Optional[str] = None
|
||||
|
||||
#: Any trailing comments or lines after the dedent that are owned by this match
|
||||
#: block. Statements own preceeding and same-line trailing comments, but not
|
||||
#: trailing lines, so it falls on :class:`Match` to own it. In the case
|
||||
#: that a statement follows a :class:`Match` block, that statement will own the
|
||||
#: comments and lines that are at the same indent as the statement, and this
|
||||
#: :class:`Match` will own the comments and lines that are indented further.
|
||||
footer: Sequence[EmptyLine] = ()
|
||||
|
||||
def _validate(self) -> None:
|
||||
if len(self.cases) == 0:
|
||||
raise CSTValidationError("A match statement must have at least one case.")
|
||||
|
||||
if self.whitespace_after_match.empty:
|
||||
raise CSTValidationError(
|
||||
"Must have at least one space after a 'match' keyword"
|
||||
)
|
||||
|
||||
indent = self.indent
|
||||
if indent is not None:
|
||||
if len(indent) == 0:
|
||||
raise CSTValidationError(
|
||||
"A match statement must have a non-zero width indent."
|
||||
)
|
||||
if _INDENT_WHITESPACE_RE.fullmatch(indent) is None:
|
||||
raise CSTValidationError(
|
||||
"An indent must be composed of only whitespace characters."
|
||||
)
|
||||
|
||||
def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Match":
|
||||
return Match(
|
||||
leading_lines=visit_sequence(
|
||||
self, "leading_lines", self.leading_lines, visitor
|
||||
),
|
||||
whitespace_after_match=visit_required(
|
||||
self, "whitespace_after_match", self.whitespace_after_match, visitor
|
||||
),
|
||||
subject=visit_required(self, "subject", self.subject, visitor),
|
||||
whitespace_before_colon=visit_required(
|
||||
self, "whitespace_before_colon", self.whitespace_before_colon, visitor
|
||||
),
|
||||
whitespace_after_colon=visit_required(
|
||||
self, "whitespace_after_colon", self.whitespace_after_colon, visitor
|
||||
),
|
||||
indent=self.indent,
|
||||
cases=visit_sequence(self, "cases", self.cases, visitor),
|
||||
footer=visit_sequence(self, "footer", self.footer, visitor),
|
||||
)
|
||||
|
||||
def _codegen_impl(self, state: CodegenState) -> None:
|
||||
for ll in self.leading_lines:
|
||||
ll._codegen(state)
|
||||
state.add_indent_tokens()
|
||||
|
||||
with state.record_syntactic_position(self, end_node=self.cases[-1]):
|
||||
state.add_token("match")
|
||||
self.whitespace_after_match._codegen(state)
|
||||
self.subject._codegen(state)
|
||||
self.whitespace_before_colon._codegen(state)
|
||||
state.add_token(":")
|
||||
self.whitespace_after_colon._codegen(state)
|
||||
|
||||
indent = self.indent
|
||||
state.increase_indent(state.default_indent if indent is None else indent)
|
||||
for c in self.cases:
|
||||
c._codegen(state)
|
||||
|
||||
for f in self.footer:
|
||||
f._codegen(state)
|
||||
|
||||
state.decrease_indent()
|
||||
|
||||
|
||||
@add_slots
|
||||
@dataclass(frozen=True)
|
||||
class MatchCase(CSTNode):
|
||||
"""
|
||||
A single ``case`` block of a :class:`Match` statement.
|
||||
"""
|
||||
|
||||
#: The pattern that ``subject`` will be matched against.
|
||||
pattern: MatchPattern
|
||||
|
||||
#: The body of this case block, to be evaluated if ``pattern`` matches ``subject``
|
||||
#: and ``guard`` evaluates to a truthy value.
|
||||
body: BaseSuite
|
||||
|
||||
#: Optional expression that will be evaluated if ``pattern`` matches ``subject``.
|
||||
guard: Optional[BaseExpression] = None
|
||||
|
||||
#: Sequence of empty lines appearing before this case block.
|
||||
leading_lines: Sequence[EmptyLine] = ()
|
||||
|
||||
#: Whitespace directly after the ``case`` keyword.
|
||||
whitespace_after_case: SimpleWhitespace = SimpleWhitespace.field(" ")
|
||||
|
||||
#: Whitespace before the ``if`` keyword in case there's a guard expression.
|
||||
whitespace_before_if: SimpleWhitespace = SimpleWhitespace.field("")
|
||||
|
||||
#: Whitespace after the ``if`` keyword in case there's a guard expression.
|
||||
whitespace_after_if: SimpleWhitespace = SimpleWhitespace.field("")
|
||||
|
||||
#: Whitespace before the colon.
|
||||
whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
|
||||
|
||||
def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "CSTNode":
|
||||
return MatchCase(
|
||||
leading_lines=visit_sequence(
|
||||
self, "leading_lines", self.leading_lines, visitor
|
||||
),
|
||||
whitespace_after_case=visit_required(
|
||||
self, "whitespace_after_case", self.whitespace_after_case, visitor
|
||||
),
|
||||
pattern=visit_required(self, "pattern", self.pattern, visitor),
|
||||
whitespace_before_if=visit_optional(
|
||||
self, "whitespace_before_if", self.whitespace_before_if, visitor
|
||||
),
|
||||
whitespace_after_if=visit_optional(
|
||||
self, "whitespace_after_if", self.whitespace_after_if, visitor
|
||||
),
|
||||
guard=visit_optional(self, "guard", self.guard, visitor),
|
||||
body=visit_required(self, "body", self.body, visitor),
|
||||
)
|
||||
|
||||
def _codegen_impl(self, state: CodegenState) -> None:
|
||||
for ll in self.leading_lines:
|
||||
ll._codegen(state)
|
||||
state.add_indent_tokens()
|
||||
with state.record_syntactic_position(self, end_node=self.body):
|
||||
state.add_token("case")
|
||||
self.whitespace_after_case._codegen(state)
|
||||
self.pattern._codegen(state)
|
||||
|
||||
guard = self.guard
|
||||
if guard is not None:
|
||||
self.whitespace_before_if._codegen(state)
|
||||
state.add_token("if")
|
||||
self.whitespace_after_if._codegen(state)
|
||||
guard._codegen(state)
|
||||
|
||||
self.whitespace_before_colon._codegen(state)
|
||||
state.add_token(":")
|
||||
self.body._codegen(state)
|
||||
|
||||
|
||||
@add_slots
|
||||
@dataclass(frozen=True)
|
||||
class MatchValue(MatchPattern):
|
||||
"""
|
||||
A match literal or value pattern that compares by equality.
|
||||
"""
|
||||
|
||||
#: an expression to compare to
|
||||
value: BaseExpression
|
||||
|
||||
def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "CSTNode":
|
||||
return MatchValue(value=visit_required(self, "value", self.value, visitor))
|
||||
|
||||
def _codegen_impl(self, state: CodegenState) -> None:
|
||||
with state.record_syntactic_position(self):
|
||||
self.value._codegen(state)
|
||||
|
||||
@property
|
||||
def lpar(self) -> Sequence[LeftParen]:
|
||||
return self.value.lpar
|
||||
|
||||
@lpar.setter
|
||||
def lpar(self, value: Sequence[LeftParen]) -> None:
|
||||
self.value.lpar = value
|
||||
|
||||
|
||||
@add_slots
|
||||
@dataclass(frozen=True)
|
||||
class MatchSingleton(MatchPattern):
|
||||
"""
|
||||
A match literal pattern that compares by identity.
|
||||
"""
|
||||
|
||||
#: a literal to compare to
|
||||
value: Name
|
||||
|
||||
def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "CSTNode":
|
||||
return MatchSingleton(value=visit_required(self, "value", self.value, visitor))
|
||||
|
||||
def _validate(self) -> None:
|
||||
if self.value.value not in {"True", "False", "None"}:
|
||||
raise CSTValidationError(
|
||||
"A match singleton can only be True, False, or None"
|
||||
)
|
||||
|
||||
def _codegen_impl(self, state: CodegenState) -> None:
|
||||
with state.record_syntactic_position(self):
|
||||
self.value._codegen(state)
|
||||
|
||||
@property
|
||||
def lpar(self) -> Sequence[LeftParen]:
|
||||
return self.value.lpar
|
||||
|
||||
@lpar.setter
|
||||
def lpar(self, value: Sequence[LeftParen]) -> None:
|
||||
self.value.lpar = value
|
||||
|
||||
|
||||
@add_slots
|
||||
@dataclass(frozen=True)
|
||||
class MatchSequenceElement(CSTNode):
|
||||
"""
|
||||
An element in a sequence match pattern.
|
||||
"""
|
||||
|
||||
value: MatchPattern
|
||||
|
||||
#: An optional trailing comma.
|
||||
comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
|
||||
|
||||
def _visit_and_replace_children(
|
||||
self, visitor: CSTVisitorT
|
||||
) -> "MatchSequenceElement":
|
||||
return MatchSequenceElement(
|
||||
value=visit_required(self, "value", self.value, visitor),
|
||||
comma=visit_sentinel(self, "comma", self.comma, visitor),
|
||||
)
|
||||
|
||||
def _codegen_impl(
|
||||
self,
|
||||
state: CodegenState,
|
||||
default_comma: bool = False,
|
||||
default_comma_whitespace: bool = True,
|
||||
) -> None:
|
||||
with state.record_syntactic_position(self):
|
||||
self.value._codegen(state)
|
||||
comma = self.comma
|
||||
if comma is MaybeSentinel.DEFAULT and default_comma:
|
||||
state.add_token(", " if default_comma_whitespace else ",")
|
||||
elif isinstance(comma, Comma):
|
||||
comma._codegen(state)
|
||||
|
||||
|
||||
@add_slots
|
||||
@dataclass(frozen=True)
|
||||
class MatchStar(CSTNode):
|
||||
"""
|
||||
A starred element in a sequence match pattern. Matches the rest of the sequence.
|
||||
"""
|
||||
|
||||
#: The name of the pattern binding. A ``None`` value represents ``*_``.
|
||||
name: Optional[Name] = None
|
||||
|
||||
#: An optional trailing comma.
|
||||
comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
|
||||
|
||||
#: Optional whitespace between the star and the name.
|
||||
whitespace_before_name: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
|
||||
|
||||
def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchStar":
|
||||
return MatchStar(
|
||||
whitespace_before_name=visit_required(
|
||||
self, "whitespace_before_name", self.whitespace_before_name, visitor
|
||||
),
|
||||
name=visit_optional(self, "name", self.name, visitor),
|
||||
comma=visit_sentinel(self, "comma", self.comma, visitor),
|
||||
)
|
||||
|
||||
def _codegen_impl(
|
||||
self,
|
||||
state: CodegenState,
|
||||
default_comma: bool = False,
|
||||
default_comma_whitespace: bool = True,
|
||||
) -> None:
|
||||
with state.record_syntactic_position(self):
|
||||
state.add_token("*")
|
||||
self.whitespace_before_name._codegen(state)
|
||||
name = self.name
|
||||
if name is None:
|
||||
state.add_token("_")
|
||||
else:
|
||||
name._codegen(state)
|
||||
comma = self.comma
|
||||
if comma is MaybeSentinel.DEFAULT and default_comma:
|
||||
state.add_token(", " if default_comma_whitespace else ",")
|
||||
elif isinstance(comma, Comma):
|
||||
comma._codegen(state)
|
||||
|
||||
|
||||
class MatchSequence(MatchPattern, ABC):
|
||||
"""
|
||||
A match sequence pattern. It's either a :class:`MatchList` or a :class:`MatchTuple`.
|
||||
Matches a variable length sequence if one of the patterns is a :class:`MatchStar`,
|
||||
otherwise matches a fixed length sequence.
|
||||
"""
|
||||
|
||||
#: Patterns to be matched against the subject elements if it is a sequence.
|
||||
patterns: Sequence[Union[MatchSequenceElement, MatchStar]]
|
||||
|
||||
|
||||
@add_slots
|
||||
@dataclass(frozen=True)
|
||||
class MatchList(MatchSequence):
|
||||
"""
|
||||
A list match pattern. It's either an "open sequence pattern" (without brackets) or a
|
||||
regular list literal (with brackets).
|
||||
"""
|
||||
|
||||
#: Patterns to be matched against the subject elements if it is a sequence.
|
||||
patterns: Sequence[Union[MatchSequenceElement, MatchStar]]
|
||||
|
||||
#: An optional left bracket. If missing, this is an open sequence pattern.
|
||||
lbracket: Optional[LeftSquareBracket] = LeftSquareBracket.field()
|
||||
|
||||
#: An optional left bracket. If missing, this is an open sequence pattern.
|
||||
rbracket: Optional[RightSquareBracket] = RightSquareBracket.field()
|
||||
|
||||
#: Parenthesis at the beginning of the node
|
||||
lpar: Sequence[LeftParen] = ()
|
||||
#: Parentheses after the pattern, but before a comma (if there is one).
|
||||
rpar: Sequence[RightParen] = ()
|
||||
|
||||
def _validate(self) -> None:
|
||||
if self.lbracket and not self.rbracket:
|
||||
raise CSTValidationError("Cannot have left bracket without right bracket")
|
||||
if self.rbracket and not self.lbracket:
|
||||
raise CSTValidationError("Cannot have right bracket without left bracket")
|
||||
|
||||
if not self.patterns and not self.lbracket:
|
||||
raise CSTValidationError(
|
||||
"Must have brackets if matching against empty list"
|
||||
)
|
||||
|
||||
super(MatchList, self)._validate()
|
||||
|
||||
def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchList":
|
||||
return MatchList(
|
||||
lpar=visit_sequence(self, "lpar", self.lpar, visitor),
|
||||
lbracket=visit_optional(self, "lbracket", self.lbracket, visitor),
|
||||
patterns=visit_sequence(self, "patterns", self.patterns, visitor),
|
||||
rbracket=visit_optional(self, "rbracket", self.rbracket, visitor),
|
||||
rpar=visit_sequence(self, "rpar", self.rpar, visitor),
|
||||
)
|
||||
|
||||
def _codegen_impl(self, state: CodegenState) -> None:
|
||||
with self._parenthesize(state):
|
||||
lbracket = self.lbracket
|
||||
if lbracket is not None:
|
||||
lbracket._codegen(state)
|
||||
pats = self.patterns
|
||||
for idx, pat in enumerate(pats):
|
||||
pat._codegen(state, default_comma=(idx < len(pats) - 1))
|
||||
rbracket = self.rbracket
|
||||
if rbracket is not None:
|
||||
rbracket._codegen(state)
|
||||
|
||||
|
||||
@add_slots
|
||||
@dataclass(frozen=True)
|
||||
class MatchTuple(MatchSequence):
|
||||
"""
|
||||
A tuple match pattern.
|
||||
"""
|
||||
|
||||
#: Patterns to be matched against the subject elements if it is a sequence.
|
||||
patterns: Sequence[Union[MatchSequenceElement, MatchStar]]
|
||||
|
||||
#: Parenthesis at the beginning of the node
|
||||
lpar: Sequence[LeftParen] = field(default_factory=lambda: (LeftParen(),))
|
||||
#: Parentheses after the pattern, but before a comma (if there is one).
|
||||
rpar: Sequence[RightParen] = field(default_factory=lambda: (RightParen(),))
|
||||
|
||||
def _validate(self) -> None:
|
||||
if len(self.lpar) < 1:
|
||||
raise CSTValidationError(
|
||||
"Tuple patterns must have at least pair of parenthesis"
|
||||
)
|
||||
|
||||
super(MatchTuple, self)._validate()
|
||||
|
||||
def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchTuple":
|
||||
return MatchTuple(
|
||||
lpar=visit_sequence(self, "lpar", self.lpar, visitor),
|
||||
patterns=visit_sequence(self, "patterns", self.patterns, visitor),
|
||||
rpar=visit_sequence(self, "rpar", self.rpar, visitor),
|
||||
)
|
||||
|
||||
def _codegen_impl(self, state: CodegenState) -> None:
|
||||
with self._parenthesize(state):
|
||||
pats = self.patterns
|
||||
patlen = len(pats)
|
||||
for idx, pat in enumerate(pats):
|
||||
pat._codegen(
|
||||
state,
|
||||
default_comma=patlen == 1 or (idx < patlen - 1),
|
||||
default_comma_whitespace=patlen != 1,
|
||||
)
|
||||
|
||||
|
||||
@add_slots
|
||||
@dataclass(frozen=True)
|
||||
class MatchMappingElement(CSTNode):
|
||||
"""
|
||||
A ``key: value`` pair in a match mapping pattern.
|
||||
"""
|
||||
|
||||
key: BaseExpression
|
||||
|
||||
#: The pattern to be matched corresponding to ``key``.
|
||||
pattern: MatchPattern
|
||||
|
||||
#: An optional trailing comma.
|
||||
comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
|
||||
|
||||
#: Whitespace between ``key`` and the colon.
|
||||
whitespace_before_colon: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
|
||||
|
||||
#: Whitespace between the colon and ``pattern``.
|
||||
whitespace_after_colon: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ")
|
||||
|
||||
def _visit_and_replace_children(
|
||||
self, visitor: CSTVisitorT
|
||||
) -> "MatchMappingElement":
|
||||
return MatchMappingElement(
|
||||
key=visit_required(self, "key", self.key, visitor),
|
||||
whitespace_before_colon=visit_required(
|
||||
self, "whitespace_before_colon", self.whitespace_before_colon, visitor
|
||||
),
|
||||
whitespace_after_colon=visit_required(
|
||||
self, "whitespace_after_colon", self.whitespace_after_colon, visitor
|
||||
),
|
||||
pattern=visit_required(self, "pattern", self.pattern, visitor),
|
||||
comma=visit_sentinel(self, "comma", self.comma, visitor),
|
||||
)
|
||||
|
||||
def _codegen_impl(self, state: CodegenState, default_comma: bool = False) -> None:
|
||||
with state.record_syntactic_position(self):
|
||||
self.key._codegen(state)
|
||||
self.whitespace_before_colon._codegen(state)
|
||||
state.add_token(":")
|
||||
self.whitespace_after_colon._codegen(state)
|
||||
self.pattern._codegen(state)
|
||||
comma = self.comma
|
||||
if comma is MaybeSentinel.DEFAULT and default_comma:
|
||||
state.add_token(", ")
|
||||
elif isinstance(comma, Comma):
|
||||
comma._codegen(state)
|
||||
|
||||
|
||||
@add_slots
|
||||
@dataclass(frozen=True)
|
||||
class MatchMapping(MatchPattern):
|
||||
"""
|
||||
A match mapping pattern.
|
||||
"""
|
||||
|
||||
#: A sequence of mapping elements.
|
||||
elements: Sequence[MatchMappingElement] = ()
|
||||
|
||||
#: Left curly brace at the beginning of the pattern.
|
||||
lbrace: LeftCurlyBrace = LeftCurlyBrace.field()
|
||||
|
||||
#: Right curly brace at the end of the pattern.
|
||||
rbrace: RightCurlyBrace = RightCurlyBrace.field()
|
||||
|
||||
#: An optional name to capture the remaining elements of the mapping.
|
||||
rest: Optional[Name] = None
|
||||
|
||||
#: Optional whitespace between stars and ``rest``.
|
||||
whitespace_before_rest: SimpleWhitespace = SimpleWhitespace.field("")
|
||||
|
||||
#: An optional trailing comma attached to ``rest``.
|
||||
trailing_comma: Optional[Comma] = None
|
||||
|
||||
#: Parenthesis at the beginning of the node
|
||||
lpar: Sequence[LeftParen] = ()
|
||||
#: Parentheses after the pattern
|
||||
rpar: Sequence[RightParen] = ()
|
||||
|
||||
def _validate(self) -> None:
|
||||
if isinstance(self.trailing_comma, Comma) and self.rest is not None:
|
||||
raise CSTValidationError("Cannot have a trailing comma without **rest")
|
||||
super(MatchMapping, self)._validate()
|
||||
|
||||
def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchMapping":
|
||||
return MatchMapping(
|
||||
lpar=visit_sequence(self, "lpar", self.lpar, visitor),
|
||||
lbrace=visit_required(self, "lbrace", self.lbrace, visitor),
|
||||
elements=visit_sequence(self, "elements", self.elements, visitor),
|
||||
whitespace_before_rest=visit_required(
|
||||
self, "whitespace_before_rest", self.whitespace_before_rest, visitor
|
||||
),
|
||||
rest=visit_optional(self, "rest", self.rest, visitor),
|
||||
trailing_comma=visit_optional(
|
||||
self, "trailing_comma", self.trailing_comma, visitor
|
||||
),
|
||||
rbrace=visit_required(self, "rbrace", self.rbrace, visitor),
|
||||
rpar=visit_sequence(self, "rpar", self.rpar, visitor),
|
||||
)
|
||||
|
||||
def _codegen_impl(self, state: CodegenState) -> None:
|
||||
with self._parenthesize(state):
|
||||
self.lbrace._codegen(state)
|
||||
elems = self.elements
|
||||
rest = self.rest
|
||||
for idx, el in enumerate(elems):
|
||||
el._codegen(
|
||||
state, default_comma=rest is not None or idx < len(elems) - 1
|
||||
)
|
||||
|
||||
if rest is not None:
|
||||
state.add_token("**")
|
||||
self.whitespace_before_rest._codegen(state)
|
||||
rest._codegen(state)
|
||||
comma = self.trailing_comma
|
||||
if comma is not None:
|
||||
comma._codegen(state)
|
||||
|
||||
self.rbrace._codegen(state)
|
||||
|
||||
|
||||
@add_slots
|
||||
@dataclass(frozen=True)
|
||||
class MatchKeywordElement(CSTNode):
|
||||
"""
|
||||
A key=value pair in a :class:`MatchClass`.
|
||||
"""
|
||||
|
||||
key: Name
|
||||
|
||||
#: The pattern to be matched against the attribute named ``key``.
|
||||
pattern: MatchPattern
|
||||
|
||||
#: An optional trailing comma.
|
||||
comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
|
||||
|
||||
#: Whitespace between ``key`` and the equals sign.
|
||||
whitespace_before_equal: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
|
||||
|
||||
#: Whitespace between the equals sign and ``pattern``.
|
||||
whitespace_after_equal: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
|
||||
|
||||
def _visit_and_replace_children(
|
||||
self, visitor: CSTVisitorT
|
||||
) -> "MatchKeywordElement":
|
||||
return MatchKeywordElement(
|
||||
key=visit_required(self, "key", self.key, visitor),
|
||||
whitespace_before_equal=visit_required(
|
||||
self, "whitespace_before_equal", self.whitespace_before_equal, visitor
|
||||
),
|
||||
whitespace_after_equal=visit_required(
|
||||
self, "whitespace_after_equal", self.whitespace_after_equal, visitor
|
||||
),
|
||||
pattern=visit_required(self, "pattern", self.pattern, visitor),
|
||||
comma=visit_sentinel(self, "comma", self.comma, visitor),
|
||||
)
|
||||
|
||||
def _codegen_impl(self, state: CodegenState, default_comma: bool = False) -> None:
|
||||
with state.record_syntactic_position(self):
|
||||
self.key._codegen(state)
|
||||
self.whitespace_before_equal._codegen(state)
|
||||
state.add_token("=")
|
||||
self.whitespace_after_equal._codegen(state)
|
||||
self.pattern._codegen(state)
|
||||
comma = self.comma
|
||||
if comma is MaybeSentinel.DEFAULT and default_comma:
|
||||
state.add_token(", ")
|
||||
elif isinstance(comma, Comma):
|
||||
comma._codegen(state)
|
||||
|
||||
|
||||
@add_slots
|
||||
@dataclass(frozen=True)
|
||||
class MatchClass(MatchPattern):
|
||||
"""
|
||||
A match class pattern.
|
||||
"""
|
||||
|
||||
#: An expression giving the nominal class to be matched.
|
||||
cls: BaseExpression
|
||||
|
||||
#: A sequence of patterns to be matched against the class defined sequence of
|
||||
#: pattern matching attributes.
|
||||
patterns: Sequence[MatchSequenceElement] = ()
|
||||
|
||||
#: A sequence of additional attribute names and corresponding patterns to be
|
||||
#: matched.
|
||||
kwds: Sequence[MatchKeywordElement] = ()
|
||||
|
||||
#: Whitespace between the class name and the left parenthesis.
|
||||
whitespace_after_cls: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
|
||||
|
||||
#: Whitespace between the left parenthesis and the first pattern.
|
||||
whitespace_before_patterns: BaseParenthesizableWhitespace = SimpleWhitespace.field(
|
||||
""
|
||||
)
|
||||
|
||||
#: Whitespace between the last pattern and the right parenthesis.
|
||||
whitespace_after_kwds: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
|
||||
|
||||
#: Parenthesis at the beginning of the node
|
||||
lpar: Sequence[LeftParen] = ()
|
||||
#: Parentheses after the pattern
|
||||
rpar: Sequence[RightParen] = ()
|
||||
|
||||
def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchClass":
|
||||
return MatchClass(
|
||||
lpar=visit_sequence(self, "lpar", self.lpar, visitor),
|
||||
cls=visit_required(self, "cls", self.cls, visitor),
|
||||
whitespace_after_cls=visit_required(
|
||||
self, "whitespace_after_cls", self.whitespace_after_cls, visitor
|
||||
),
|
||||
whitespace_before_patterns=visit_required(
|
||||
self,
|
||||
"whitespace_before_patterns",
|
||||
self.whitespace_before_patterns,
|
||||
visitor,
|
||||
),
|
||||
patterns=visit_sequence(self, "patterns", self.patterns, visitor),
|
||||
kwds=visit_sequence(self, "kwds", self.kwds, visitor),
|
||||
whitespace_after_kwds=visit_required(
|
||||
self, "whitespace_after_kwds", self.whitespace_after_kwds, visitor
|
||||
),
|
||||
)
|
||||
|
||||
def _codegen_impl(self, state: CodegenState) -> None:
|
||||
with self._parenthesize(state):
|
||||
self.cls._codegen(state)
|
||||
self.whitespace_after_cls._codegen(state)
|
||||
state.add_token("(")
|
||||
self.whitespace_before_patterns._codegen(state)
|
||||
pats = self.patterns
|
||||
kwds = self.kwds
|
||||
for idx, pat in enumerate(pats):
|
||||
pat._codegen(state, default_comma=idx + 1 < len(pats) + len(kwds))
|
||||
for idx, kwd in enumerate(kwds):
|
||||
kwd._codegen(state, default_comma=idx + 1 < len(kwds))
|
||||
self.whitespace_after_kwds._codegen(state)
|
||||
state.add_token(")")
|
||||
|
||||
|
||||
@add_slots
|
||||
@dataclass(frozen=True)
|
||||
class MatchAs(MatchPattern):
|
||||
"""
|
||||
A match "as-pattern", capture pattern, or wildcard pattern.
|
||||
"""
|
||||
|
||||
#: The match pattern that the subject will be matched against. If this is ``None``,
|
||||
#: the node represents a capture pattern (i.e. a bare name) and will always succeed.
|
||||
pattern: Optional[MatchPattern] = None
|
||||
|
||||
#: The name that will be bound if the pattern is successful. If this is ``None``,
|
||||
#: ``pattern`` must also be ``None`` and the node represents the wildcard pattern
|
||||
#: (i.e. ``_``).
|
||||
name: Optional[Name] = None
|
||||
|
||||
#: Whitespace between ``pattern`` and the ``as`` keyword (if ``pattern`` is not
|
||||
#: ``None``)
|
||||
whitespace_before_as: Union[
|
||||
BaseParenthesizableWhitespace, MaybeSentinel
|
||||
] = MaybeSentinel.DEFAULT
|
||||
|
||||
#: Whitespace between the ``as`` keyword and ``name`` (if ``pattern`` is not
|
||||
#: ``None``)
|
||||
whitespace_after_as: Union[
|
||||
BaseParenthesizableWhitespace, MaybeSentinel
|
||||
] = MaybeSentinel.DEFAULT
|
||||
|
||||
#: Parenthesis at the beginning of the node
|
||||
lpar: Sequence[LeftParen] = ()
|
||||
#: Parentheses after the pattern
|
||||
rpar: Sequence[RightParen] = ()
|
||||
|
||||
def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchAs":
|
||||
return MatchAs(
|
||||
lpar=visit_sequence(self, "lpar", self.lpar, visitor),
|
||||
pattern=visit_optional(self, "pattern", self.pattern, visitor),
|
||||
whitespace_before_as=visit_sentinel(
|
||||
self, "whitespace_before_as", self.whitespace_before_as, visitor
|
||||
),
|
||||
whitespace_after_as=visit_sentinel(
|
||||
self, "whitespace_after_as", self.whitespace_after_as, visitor
|
||||
),
|
||||
name=visit_optional(self, "name", self.name, visitor),
|
||||
rpar=visit_sequence(self, "rpar", self.rpar, visitor),
|
||||
)
|
||||
|
||||
def _validate(self) -> None:
|
||||
if self.name is None and self.pattern is not None:
|
||||
raise CSTValidationError("Pattern must be None if name is None")
|
||||
super(MatchAs, self)._validate()
|
||||
|
||||
def _codegen_impl(self, state: CodegenState) -> None:
|
||||
with self._parenthesize(state):
|
||||
pat = self.pattern
|
||||
name = self.name
|
||||
if pat is not None:
|
||||
pat._codegen(state)
|
||||
ws_before = self.whitespace_before_as
|
||||
if ws_before is MaybeSentinel.DEFAULT:
|
||||
state.add_token(" ")
|
||||
elif isinstance(ws_before, BaseParenthesizableWhitespace):
|
||||
ws_before._codegen(state)
|
||||
state.add_token("as")
|
||||
ws_after = self.whitespace_after_as
|
||||
if ws_after is MaybeSentinel.DEFAULT:
|
||||
state.add_token(" ")
|
||||
elif isinstance(ws_after, BaseParenthesizableWhitespace):
|
||||
ws_after._codegen(state)
|
||||
if name is None:
|
||||
state.add_token("_")
|
||||
else:
|
||||
name._codegen(state)
|
||||
|
||||
|
||||
@add_slots
|
||||
@dataclass(frozen=True)
|
||||
class MatchOrElement(CSTNode):
|
||||
"""
|
||||
An element in a :class:`MatchOr` node.
|
||||
"""
|
||||
|
||||
pattern: MatchPattern
|
||||
|
||||
#: An optional ``|`` separator.
|
||||
separator: Union[BitOr, MaybeSentinel] = MaybeSentinel.DEFAULT
|
||||
|
||||
def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchOrElement":
|
||||
return MatchOrElement(
|
||||
pattern=visit_required(self, "pattern", self.pattern, visitor),
|
||||
separator=visit_sentinel(self, "separator", self.separator, visitor),
|
||||
)
|
||||
|
||||
def _codegen_impl(
|
||||
self, state: CodegenState, default_separator: bool = False
|
||||
) -> None:
|
||||
with state.record_syntactic_position(self):
|
||||
self.pattern._codegen(state)
|
||||
sep = self.separator
|
||||
if sep is MaybeSentinel.DEFAULT and default_separator:
|
||||
state.add_token(" | ")
|
||||
elif isinstance(sep, BitOr):
|
||||
sep._codegen(state)
|
||||
|
||||
|
||||
@add_slots
|
||||
@dataclass(frozen=True)
|
||||
class MatchOr(MatchPattern):
|
||||
"""
|
||||
A match "or-pattern". It matches each of its subpatterns in turn to the subject,
|
||||
until one succeeds. The or-pattern is then deemed to succeed. If none of the
|
||||
subpatterns succeed the or-pattern fails.
|
||||
"""
|
||||
|
||||
#: The subpatterns to be tried in turn.
|
||||
patterns: Sequence[MatchOrElement]
|
||||
|
||||
#: Parenthesis at the beginning of the node
|
||||
lpar: Sequence[LeftParen] = ()
|
||||
#: Parentheses after the pattern
|
||||
rpar: Sequence[RightParen] = ()
|
||||
|
||||
def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "MatchOr":
|
||||
return MatchOr(
|
||||
lpar=visit_sequence(self, "lpar", self.lpar, visitor),
|
||||
patterns=visit_sequence(self, "patterns", self.patterns, visitor),
|
||||
rpar=visit_sequence(self, "rpar", self.rpar, visitor),
|
||||
)
|
||||
|
||||
def _codegen_impl(self, state: CodegenState) -> None:
|
||||
with self._parenthesize(state):
|
||||
pats = self.patterns
|
||||
for idx, pat in enumerate(pats):
|
||||
pat._codegen(state, default_separator=idx + 1 < len(pats))
|
||||
|
|
|
|||
430
libcst/_nodes/tests/test_match.py
Normal file
430
libcst/_nodes/tests/test_match.py
Normal file
|
|
@ -0,0 +1,430 @@
|
|||
# Copyright (c) Facebook, Inc. and its affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
from typing import Any
|
||||
|
||||
import libcst as cst
|
||||
from libcst import parse_statement
|
||||
from libcst._nodes.tests.base import CSTNodeTest
|
||||
from libcst._parser.entrypoints import is_native
|
||||
from libcst.testing.utils import data_provider
|
||||
|
||||
parser = parse_statement if is_native() else None
|
||||
|
||||
|
||||
class MatchTest(CSTNodeTest):
|
||||
# pyre-fixme[56]: Invalid decoration - Pyre was not able to infer the type
|
||||
@data_provider(
|
||||
(
|
||||
# Values and singletons
|
||||
{
|
||||
"node": cst.Match(
|
||||
subject=cst.Name("x"),
|
||||
cases=[
|
||||
cst.MatchCase(
|
||||
pattern=cst.MatchSingleton(cst.Name("None")),
|
||||
body=cst.SimpleStatementSuite((cst.Pass(),)),
|
||||
),
|
||||
cst.MatchCase(
|
||||
pattern=cst.MatchValue(cst.SimpleString('"foo"')),
|
||||
body=cst.SimpleStatementSuite((cst.Pass(),)),
|
||||
),
|
||||
],
|
||||
),
|
||||
"code": "match x:\n"
|
||||
+ " case None: pass\n"
|
||||
+ ' case "foo": pass\n',
|
||||
"parser": parser,
|
||||
},
|
||||
# List patterns
|
||||
{
|
||||
"node": cst.Match(
|
||||
subject=cst.Name("x"),
|
||||
cases=[
|
||||
cst.MatchCase( # empty list
|
||||
pattern=cst.MatchList(
|
||||
[],
|
||||
lbracket=cst.LeftSquareBracket(),
|
||||
rbracket=cst.RightSquareBracket(),
|
||||
),
|
||||
body=cst.SimpleStatementSuite((cst.Pass(),)),
|
||||
),
|
||||
cst.MatchCase( # single element list
|
||||
pattern=cst.MatchList(
|
||||
[
|
||||
cst.MatchSequenceElement(
|
||||
cst.MatchSingleton(cst.Name("None"))
|
||||
)
|
||||
],
|
||||
lbracket=cst.LeftSquareBracket(),
|
||||
rbracket=cst.RightSquareBracket(),
|
||||
),
|
||||
body=cst.SimpleStatementSuite((cst.Pass(),)),
|
||||
),
|
||||
cst.MatchCase( # single element list with trailing comma
|
||||
pattern=cst.MatchList(
|
||||
[
|
||||
cst.MatchSequenceElement(
|
||||
cst.MatchSingleton(cst.Name("None")),
|
||||
cst.Comma(),
|
||||
)
|
||||
],
|
||||
lbracket=cst.LeftSquareBracket(),
|
||||
rbracket=cst.RightSquareBracket(),
|
||||
),
|
||||
body=cst.SimpleStatementSuite((cst.Pass(),)),
|
||||
),
|
||||
],
|
||||
),
|
||||
"code": (
|
||||
"match x:\n"
|
||||
+ " case []: pass\n"
|
||||
+ " case [None]: pass\n"
|
||||
+ " case [None,]: pass\n"
|
||||
),
|
||||
"parser": parser,
|
||||
},
|
||||
# Tuple patterns
|
||||
{
|
||||
"node": cst.Match(
|
||||
subject=cst.Name("x"),
|
||||
cases=[
|
||||
cst.MatchCase( # empty tuple
|
||||
pattern=cst.MatchTuple(
|
||||
[],
|
||||
),
|
||||
body=cst.SimpleStatementSuite((cst.Pass(),)),
|
||||
),
|
||||
cst.MatchCase( # two element tuple
|
||||
pattern=cst.MatchTuple(
|
||||
[
|
||||
cst.MatchSequenceElement(
|
||||
cst.MatchSingleton(cst.Name("None")),
|
||||
cst.Comma(),
|
||||
),
|
||||
cst.MatchSequenceElement(
|
||||
cst.MatchSingleton(cst.Name("None")),
|
||||
),
|
||||
],
|
||||
),
|
||||
body=cst.SimpleStatementSuite((cst.Pass(),)),
|
||||
),
|
||||
cst.MatchCase( # single element tuple with trailing comma
|
||||
pattern=cst.MatchTuple(
|
||||
[
|
||||
cst.MatchSequenceElement(
|
||||
cst.MatchSingleton(cst.Name("None")),
|
||||
cst.Comma(),
|
||||
)
|
||||
],
|
||||
),
|
||||
body=cst.SimpleStatementSuite((cst.Pass(),)),
|
||||
),
|
||||
cst.MatchCase( # two element tuple
|
||||
pattern=cst.MatchTuple(
|
||||
[
|
||||
cst.MatchSequenceElement(
|
||||
cst.MatchSingleton(cst.Name("None")),
|
||||
cst.Comma(),
|
||||
),
|
||||
cst.MatchStar(
|
||||
comma=cst.Comma(),
|
||||
),
|
||||
cst.MatchSequenceElement(
|
||||
cst.MatchSingleton(cst.Name("None")),
|
||||
),
|
||||
],
|
||||
),
|
||||
body=cst.SimpleStatementSuite((cst.Pass(),)),
|
||||
),
|
||||
],
|
||||
),
|
||||
"code": (
|
||||
"match x:\n"
|
||||
+ " case (): pass\n"
|
||||
+ " case (None,None): pass\n"
|
||||
+ " case (None,): pass\n"
|
||||
+ " case (None,*_,None): pass\n"
|
||||
),
|
||||
"parser": parser,
|
||||
},
|
||||
# Mapping patterns
|
||||
{
|
||||
"node": cst.Match(
|
||||
subject=cst.Name("x"),
|
||||
cases=[
|
||||
cst.MatchCase( # empty mapping
|
||||
pattern=cst.MatchMapping(
|
||||
[],
|
||||
),
|
||||
body=cst.SimpleStatementSuite((cst.Pass(),)),
|
||||
),
|
||||
cst.MatchCase( # two element mapping
|
||||
pattern=cst.MatchMapping(
|
||||
[
|
||||
cst.MatchMappingElement(
|
||||
key=cst.SimpleString('"a"'),
|
||||
pattern=cst.MatchSingleton(cst.Name("None")),
|
||||
comma=cst.Comma(),
|
||||
),
|
||||
cst.MatchMappingElement(
|
||||
key=cst.SimpleString('"b"'),
|
||||
pattern=cst.MatchSingleton(cst.Name("None")),
|
||||
),
|
||||
],
|
||||
),
|
||||
body=cst.SimpleStatementSuite((cst.Pass(),)),
|
||||
),
|
||||
cst.MatchCase( # single element mapping with trailing comma
|
||||
pattern=cst.MatchMapping(
|
||||
[
|
||||
cst.MatchMappingElement(
|
||||
key=cst.SimpleString('"a"'),
|
||||
pattern=cst.MatchSingleton(cst.Name("None")),
|
||||
comma=cst.Comma(),
|
||||
)
|
||||
],
|
||||
),
|
||||
body=cst.SimpleStatementSuite((cst.Pass(),)),
|
||||
),
|
||||
cst.MatchCase( # rest
|
||||
pattern=cst.MatchMapping(
|
||||
rest=cst.Name("rest"),
|
||||
),
|
||||
body=cst.SimpleStatementSuite((cst.Pass(),)),
|
||||
),
|
||||
],
|
||||
),
|
||||
"code": (
|
||||
"match x:\n"
|
||||
+ " case {}: pass\n"
|
||||
+ ' case {"a": None,"b": None}: pass\n'
|
||||
+ ' case {"a": None,}: pass\n'
|
||||
+ " case {**rest}: pass\n"
|
||||
),
|
||||
"parser": parser,
|
||||
},
|
||||
# Class patterns
|
||||
{
|
||||
"node": cst.Match(
|
||||
subject=cst.Name("x"),
|
||||
cases=[
|
||||
cst.MatchCase( # empty class
|
||||
pattern=cst.MatchClass(
|
||||
cls=cst.Attribute(cst.Name("a"), cst.Name("b")),
|
||||
),
|
||||
body=cst.SimpleStatementSuite((cst.Pass(),)),
|
||||
),
|
||||
cst.MatchCase( # single pattern class
|
||||
pattern=cst.MatchClass(
|
||||
cls=cst.Attribute(cst.Name("a"), cst.Name("b")),
|
||||
patterns=[
|
||||
cst.MatchSequenceElement(
|
||||
cst.MatchSingleton(cst.Name("None"))
|
||||
)
|
||||
],
|
||||
),
|
||||
body=cst.SimpleStatementSuite((cst.Pass(),)),
|
||||
),
|
||||
cst.MatchCase( # single pattern class with trailing comma
|
||||
pattern=cst.MatchClass(
|
||||
cls=cst.Attribute(cst.Name("a"), cst.Name("b")),
|
||||
patterns=[
|
||||
cst.MatchSequenceElement(
|
||||
cst.MatchSingleton(cst.Name("None")),
|
||||
comma=cst.Comma(),
|
||||
)
|
||||
],
|
||||
),
|
||||
body=cst.SimpleStatementSuite((cst.Pass(),)),
|
||||
),
|
||||
cst.MatchCase( # single keyword pattern class
|
||||
pattern=cst.MatchClass(
|
||||
cls=cst.Attribute(cst.Name("a"), cst.Name("b")),
|
||||
kwds=[
|
||||
cst.MatchKeywordElement(
|
||||
key=cst.Name("foo"),
|
||||
pattern=cst.MatchSingleton(cst.Name("None")),
|
||||
)
|
||||
],
|
||||
),
|
||||
body=cst.SimpleStatementSuite((cst.Pass(),)),
|
||||
),
|
||||
cst.MatchCase( # single keyword pattern class with trailing comma
|
||||
pattern=cst.MatchClass(
|
||||
cls=cst.Attribute(cst.Name("a"), cst.Name("b")),
|
||||
kwds=[
|
||||
cst.MatchKeywordElement(
|
||||
key=cst.Name("foo"),
|
||||
pattern=cst.MatchSingleton(cst.Name("None")),
|
||||
comma=cst.Comma(),
|
||||
)
|
||||
],
|
||||
),
|
||||
body=cst.SimpleStatementSuite((cst.Pass(),)),
|
||||
),
|
||||
cst.MatchCase( # now all at once
|
||||
pattern=cst.MatchClass(
|
||||
cls=cst.Attribute(cst.Name("a"), cst.Name("b")),
|
||||
patterns=[
|
||||
cst.MatchSequenceElement(
|
||||
cst.MatchSingleton(cst.Name("None")),
|
||||
cst.Comma(),
|
||||
),
|
||||
cst.MatchSequenceElement(
|
||||
cst.MatchSingleton(cst.Name("None")),
|
||||
cst.Comma(),
|
||||
),
|
||||
],
|
||||
kwds=[
|
||||
cst.MatchKeywordElement(
|
||||
key=cst.Name("foo"),
|
||||
pattern=cst.MatchSingleton(cst.Name("None")),
|
||||
comma=cst.Comma(),
|
||||
),
|
||||
cst.MatchKeywordElement(
|
||||
key=cst.Name("bar"),
|
||||
pattern=cst.MatchSingleton(cst.Name("None")),
|
||||
comma=cst.Comma(),
|
||||
),
|
||||
],
|
||||
),
|
||||
body=cst.SimpleStatementSuite((cst.Pass(),)),
|
||||
),
|
||||
],
|
||||
),
|
||||
"code": (
|
||||
"match x:\n"
|
||||
+ " case a.b(): pass\n"
|
||||
+ " case a.b(None): pass\n"
|
||||
+ " case a.b(None,): pass\n"
|
||||
+ " case a.b(foo=None): pass\n"
|
||||
+ " case a.b(foo=None,): pass\n"
|
||||
+ " case a.b(None,None,foo=None,bar=None,): pass\n"
|
||||
),
|
||||
"parser": parser,
|
||||
},
|
||||
# as pattern
|
||||
{
|
||||
"node": cst.Match(
|
||||
subject=cst.Name("x"),
|
||||
cases=[
|
||||
cst.MatchCase(
|
||||
pattern=cst.MatchAs(),
|
||||
body=cst.SimpleStatementSuite((cst.Pass(),)),
|
||||
),
|
||||
cst.MatchCase(
|
||||
pattern=cst.MatchAs(name=cst.Name("foo")),
|
||||
body=cst.SimpleStatementSuite((cst.Pass(),)),
|
||||
),
|
||||
cst.MatchCase(
|
||||
pattern=cst.MatchAs(
|
||||
pattern=cst.MatchSingleton(cst.Name("None")),
|
||||
name=cst.Name("bar"),
|
||||
whitespace_before_as=cst.SimpleWhitespace(" "),
|
||||
whitespace_after_as=cst.SimpleWhitespace(" "),
|
||||
),
|
||||
body=cst.SimpleStatementSuite((cst.Pass(),)),
|
||||
),
|
||||
],
|
||||
),
|
||||
"code": "match x:\n"
|
||||
+ " case _: pass\n"
|
||||
+ " case foo: pass\n"
|
||||
+ " case None as bar: pass\n",
|
||||
"parser": parser,
|
||||
},
|
||||
# or pattern
|
||||
{
|
||||
"node": cst.Match(
|
||||
subject=cst.Name("x"),
|
||||
cases=[
|
||||
cst.MatchCase(
|
||||
pattern=cst.MatchOr(
|
||||
[
|
||||
cst.MatchOrElement(
|
||||
cst.MatchSingleton(cst.Name("None")),
|
||||
cst.BitOr(),
|
||||
),
|
||||
cst.MatchOrElement(
|
||||
cst.MatchSingleton(cst.Name("False")),
|
||||
cst.BitOr(),
|
||||
),
|
||||
cst.MatchOrElement(
|
||||
cst.MatchSingleton(cst.Name("True"))
|
||||
),
|
||||
]
|
||||
),
|
||||
body=cst.SimpleStatementSuite((cst.Pass(),)),
|
||||
)
|
||||
],
|
||||
),
|
||||
"code": "match x:\n case None | False | True: pass\n",
|
||||
"parser": parser,
|
||||
},
|
||||
{ # exercise sentinels
|
||||
"node": cst.Match(
|
||||
subject=cst.Name("x"),
|
||||
cases=[
|
||||
cst.MatchCase(
|
||||
pattern=cst.MatchList(
|
||||
[cst.MatchStar(), cst.MatchStar()],
|
||||
lbracket=None,
|
||||
rbracket=None,
|
||||
),
|
||||
body=cst.SimpleStatementSuite((cst.Pass(),)),
|
||||
),
|
||||
cst.MatchCase(
|
||||
pattern=cst.MatchTuple(
|
||||
[
|
||||
cst.MatchSequenceElement(
|
||||
cst.MatchSingleton(cst.Name("None"))
|
||||
)
|
||||
]
|
||||
),
|
||||
body=cst.SimpleStatementSuite((cst.Pass(),)),
|
||||
),
|
||||
cst.MatchCase(
|
||||
pattern=cst.MatchAs(
|
||||
pattern=cst.MatchTuple(
|
||||
[
|
||||
cst.MatchSequenceElement(
|
||||
cst.MatchSingleton(cst.Name("None"))
|
||||
)
|
||||
]
|
||||
),
|
||||
name=cst.Name("bar"),
|
||||
),
|
||||
body=cst.SimpleStatementSuite((cst.Pass(),)),
|
||||
),
|
||||
cst.MatchCase(
|
||||
pattern=cst.MatchOr(
|
||||
[
|
||||
cst.MatchOrElement(
|
||||
cst.MatchSingleton(cst.Name("None")),
|
||||
),
|
||||
cst.MatchOrElement(
|
||||
cst.MatchSingleton(cst.Name("False")),
|
||||
),
|
||||
cst.MatchOrElement(
|
||||
cst.MatchSingleton(cst.Name("True"))
|
||||
),
|
||||
]
|
||||
),
|
||||
body=cst.SimpleStatementSuite((cst.Pass(),)),
|
||||
),
|
||||
],
|
||||
),
|
||||
"code": "match x:\n"
|
||||
+ " case *_, *_: pass\n"
|
||||
+ " case (None,): pass\n"
|
||||
+ " case (None,) as bar: pass\n"
|
||||
+ " case None | False | True: pass\n",
|
||||
"parser": None,
|
||||
},
|
||||
)
|
||||
)
|
||||
def test_valid(self, **kwargs: Any) -> None:
|
||||
self.validate_node(**kwargs)
|
||||
|
|
@ -159,6 +159,23 @@ if TYPE_CHECKING:
|
|||
ImportAlias,
|
||||
ImportFrom,
|
||||
IndentedBlock,
|
||||
Match,
|
||||
MatchAs,
|
||||
MatchCase,
|
||||
MatchClass,
|
||||
MatchKeywordElement,
|
||||
MatchList,
|
||||
MatchMapping,
|
||||
MatchMappingElement,
|
||||
MatchOr,
|
||||
MatchOrElement,
|
||||
MatchPattern,
|
||||
MatchSequence,
|
||||
MatchSequenceElement,
|
||||
MatchSingleton,
|
||||
MatchStar,
|
||||
MatchTuple,
|
||||
MatchValue,
|
||||
NameItem,
|
||||
Nonlocal,
|
||||
Pass,
|
||||
|
|
@ -3134,6 +3151,636 @@ class CSTTypedBaseFunctions:
|
|||
def leave_ListComp_rpar(self, node: "ListComp") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_Match(self, node: "Match") -> Optional[bool]:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_Match_subject(self, node: "Match") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_Match_subject(self, node: "Match") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_Match_cases(self, node: "Match") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_Match_cases(self, node: "Match") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_Match_leading_lines(self, node: "Match") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_Match_leading_lines(self, node: "Match") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_Match_whitespace_after_match(self, node: "Match") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_Match_whitespace_after_match(self, node: "Match") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_Match_whitespace_before_colon(self, node: "Match") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_Match_whitespace_before_colon(self, node: "Match") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_Match_whitespace_after_colon(self, node: "Match") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_Match_whitespace_after_colon(self, node: "Match") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_Match_indent(self, node: "Match") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_Match_indent(self, node: "Match") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_Match_footer(self, node: "Match") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_Match_footer(self, node: "Match") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchAs(self, node: "MatchAs") -> Optional[bool]:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchAs_pattern(self, node: "MatchAs") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchAs_pattern(self, node: "MatchAs") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchAs_name(self, node: "MatchAs") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchAs_name(self, node: "MatchAs") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchAs_whitespace_before_as(self, node: "MatchAs") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchAs_whitespace_before_as(self, node: "MatchAs") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchAs_whitespace_after_as(self, node: "MatchAs") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchAs_whitespace_after_as(self, node: "MatchAs") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchAs_lpar(self, node: "MatchAs") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchAs_lpar(self, node: "MatchAs") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchAs_rpar(self, node: "MatchAs") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchAs_rpar(self, node: "MatchAs") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchCase(self, node: "MatchCase") -> Optional[bool]:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchCase_pattern(self, node: "MatchCase") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchCase_pattern(self, node: "MatchCase") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchCase_body(self, node: "MatchCase") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchCase_body(self, node: "MatchCase") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchCase_guard(self, node: "MatchCase") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchCase_guard(self, node: "MatchCase") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchCase_leading_lines(self, node: "MatchCase") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchCase_leading_lines(self, node: "MatchCase") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchCase_whitespace_after_case(self, node: "MatchCase") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchCase_whitespace_after_case(self, node: "MatchCase") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchCase_whitespace_before_if(self, node: "MatchCase") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchCase_whitespace_before_if(self, node: "MatchCase") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchCase_whitespace_after_if(self, node: "MatchCase") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchCase_whitespace_after_if(self, node: "MatchCase") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchCase_whitespace_before_colon(self, node: "MatchCase") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchCase_whitespace_before_colon(self, node: "MatchCase") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchClass(self, node: "MatchClass") -> Optional[bool]:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchClass_cls(self, node: "MatchClass") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchClass_cls(self, node: "MatchClass") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchClass_patterns(self, node: "MatchClass") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchClass_patterns(self, node: "MatchClass") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchClass_kwds(self, node: "MatchClass") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchClass_kwds(self, node: "MatchClass") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchClass_whitespace_after_cls(self, node: "MatchClass") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchClass_whitespace_after_cls(self, node: "MatchClass") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchClass_whitespace_before_patterns(self, node: "MatchClass") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchClass_whitespace_before_patterns(self, node: "MatchClass") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchClass_whitespace_after_kwds(self, node: "MatchClass") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchClass_whitespace_after_kwds(self, node: "MatchClass") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchClass_lpar(self, node: "MatchClass") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchClass_lpar(self, node: "MatchClass") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchClass_rpar(self, node: "MatchClass") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchClass_rpar(self, node: "MatchClass") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchKeywordElement(self, node: "MatchKeywordElement") -> Optional[bool]:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchKeywordElement_key(self, node: "MatchKeywordElement") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchKeywordElement_key(self, node: "MatchKeywordElement") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchKeywordElement_pattern(self, node: "MatchKeywordElement") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchKeywordElement_pattern(self, node: "MatchKeywordElement") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchKeywordElement_comma(self, node: "MatchKeywordElement") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchKeywordElement_comma(self, node: "MatchKeywordElement") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchKeywordElement_whitespace_before_equal(
|
||||
self, node: "MatchKeywordElement"
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchKeywordElement_whitespace_before_equal(
|
||||
self, node: "MatchKeywordElement"
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchKeywordElement_whitespace_after_equal(
|
||||
self, node: "MatchKeywordElement"
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchKeywordElement_whitespace_after_equal(
|
||||
self, node: "MatchKeywordElement"
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchList(self, node: "MatchList") -> Optional[bool]:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchList_patterns(self, node: "MatchList") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchList_patterns(self, node: "MatchList") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchList_lbracket(self, node: "MatchList") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchList_lbracket(self, node: "MatchList") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchList_rbracket(self, node: "MatchList") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchList_rbracket(self, node: "MatchList") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchList_lpar(self, node: "MatchList") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchList_lpar(self, node: "MatchList") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchList_rpar(self, node: "MatchList") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchList_rpar(self, node: "MatchList") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchMapping(self, node: "MatchMapping") -> Optional[bool]:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchMapping_elements(self, node: "MatchMapping") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchMapping_elements(self, node: "MatchMapping") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchMapping_lbrace(self, node: "MatchMapping") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchMapping_lbrace(self, node: "MatchMapping") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchMapping_rbrace(self, node: "MatchMapping") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchMapping_rbrace(self, node: "MatchMapping") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchMapping_rest(self, node: "MatchMapping") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchMapping_rest(self, node: "MatchMapping") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchMapping_whitespace_before_rest(self, node: "MatchMapping") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchMapping_whitespace_before_rest(self, node: "MatchMapping") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchMapping_trailing_comma(self, node: "MatchMapping") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchMapping_trailing_comma(self, node: "MatchMapping") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchMapping_lpar(self, node: "MatchMapping") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchMapping_lpar(self, node: "MatchMapping") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchMapping_rpar(self, node: "MatchMapping") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchMapping_rpar(self, node: "MatchMapping") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchMappingElement(self, node: "MatchMappingElement") -> Optional[bool]:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchMappingElement_key(self, node: "MatchMappingElement") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchMappingElement_key(self, node: "MatchMappingElement") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchMappingElement_pattern(self, node: "MatchMappingElement") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchMappingElement_pattern(self, node: "MatchMappingElement") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchMappingElement_comma(self, node: "MatchMappingElement") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchMappingElement_comma(self, node: "MatchMappingElement") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchMappingElement_whitespace_before_colon(
|
||||
self, node: "MatchMappingElement"
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchMappingElement_whitespace_before_colon(
|
||||
self, node: "MatchMappingElement"
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchMappingElement_whitespace_after_colon(
|
||||
self, node: "MatchMappingElement"
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchMappingElement_whitespace_after_colon(
|
||||
self, node: "MatchMappingElement"
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchOr(self, node: "MatchOr") -> Optional[bool]:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchOr_patterns(self, node: "MatchOr") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchOr_patterns(self, node: "MatchOr") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchOr_lpar(self, node: "MatchOr") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchOr_lpar(self, node: "MatchOr") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchOr_rpar(self, node: "MatchOr") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchOr_rpar(self, node: "MatchOr") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchOrElement(self, node: "MatchOrElement") -> Optional[bool]:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchOrElement_pattern(self, node: "MatchOrElement") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchOrElement_pattern(self, node: "MatchOrElement") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchOrElement_separator(self, node: "MatchOrElement") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchOrElement_separator(self, node: "MatchOrElement") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchPattern(self, node: "MatchPattern") -> Optional[bool]:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchSequence(self, node: "MatchSequence") -> Optional[bool]:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchSequenceElement(
|
||||
self, node: "MatchSequenceElement"
|
||||
) -> Optional[bool]:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchSequenceElement_value(self, node: "MatchSequenceElement") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchSequenceElement_value(self, node: "MatchSequenceElement") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchSequenceElement_comma(self, node: "MatchSequenceElement") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchSequenceElement_comma(self, node: "MatchSequenceElement") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchSingleton(self, node: "MatchSingleton") -> Optional[bool]:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchSingleton_value(self, node: "MatchSingleton") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchSingleton_value(self, node: "MatchSingleton") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchStar(self, node: "MatchStar") -> Optional[bool]:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchStar_name(self, node: "MatchStar") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchStar_name(self, node: "MatchStar") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchStar_comma(self, node: "MatchStar") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchStar_comma(self, node: "MatchStar") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchStar_whitespace_before_name(self, node: "MatchStar") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchStar_whitespace_before_name(self, node: "MatchStar") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchTuple(self, node: "MatchTuple") -> Optional[bool]:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchTuple_patterns(self, node: "MatchTuple") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchTuple_patterns(self, node: "MatchTuple") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchTuple_lpar(self, node: "MatchTuple") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchTuple_lpar(self, node: "MatchTuple") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchTuple_rpar(self, node: "MatchTuple") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchTuple_rpar(self, node: "MatchTuple") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchValue(self, node: "MatchValue") -> Optional[bool]:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatchValue_value(self, node: "MatchValue") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchValue_value(self, node: "MatchValue") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def visit_MatrixMultiply(self, node: "MatrixMultiply") -> Optional[bool]:
|
||||
pass
|
||||
|
|
@ -5172,6 +5819,74 @@ class CSTTypedVisitorFunctions(CSTTypedBaseFunctions):
|
|||
def leave_ListComp(self, original_node: "ListComp") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_Match(self, original_node: "Match") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchAs(self, original_node: "MatchAs") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchCase(self, original_node: "MatchCase") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchClass(self, original_node: "MatchClass") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchKeywordElement(self, original_node: "MatchKeywordElement") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchList(self, original_node: "MatchList") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchMapping(self, original_node: "MatchMapping") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchMappingElement(self, original_node: "MatchMappingElement") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchOr(self, original_node: "MatchOr") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchOrElement(self, original_node: "MatchOrElement") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchPattern(self, original_node: "MatchPattern") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchSequence(self, original_node: "MatchSequence") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchSequenceElement(self, original_node: "MatchSequenceElement") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchSingleton(self, original_node: "MatchSingleton") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchStar(self, original_node: "MatchStar") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchTuple(self, original_node: "MatchTuple") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchValue(self, original_node: "MatchValue") -> None:
|
||||
pass
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatrixMultiply(self, original_node: "MatrixMultiply") -> None:
|
||||
pass
|
||||
|
|
@ -5521,7 +6236,7 @@ class CSTTypedTransformerFunctions(CSTTypedBaseFunctions):
|
|||
@mark_no_op
|
||||
def leave_BitOr(
|
||||
self, original_node: "BitOr", updated_node: "BitOr"
|
||||
) -> "BaseBinaryOp":
|
||||
) -> Union["BaseBinaryOp", MaybeSentinel]:
|
||||
return updated_node
|
||||
|
||||
@mark_no_op
|
||||
|
|
@ -5956,6 +6671,116 @@ class CSTTypedTransformerFunctions(CSTTypedBaseFunctions):
|
|||
) -> "BaseExpression":
|
||||
return updated_node
|
||||
|
||||
@mark_no_op
|
||||
def leave_Match(
|
||||
self, original_node: "Match", updated_node: "Match"
|
||||
) -> Union["BaseStatement", FlattenSentinel["BaseStatement"], RemovalSentinel]:
|
||||
return updated_node
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchAs(
|
||||
self, original_node: "MatchAs", updated_node: "MatchAs"
|
||||
) -> "MatchPattern":
|
||||
return updated_node
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchCase(
|
||||
self, original_node: "MatchCase", updated_node: "MatchCase"
|
||||
) -> "MatchCase":
|
||||
return updated_node
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchClass(
|
||||
self, original_node: "MatchClass", updated_node: "MatchClass"
|
||||
) -> "MatchPattern":
|
||||
return updated_node
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchKeywordElement(
|
||||
self, original_node: "MatchKeywordElement", updated_node: "MatchKeywordElement"
|
||||
) -> Union[
|
||||
"MatchKeywordElement", FlattenSentinel["MatchKeywordElement"], RemovalSentinel
|
||||
]:
|
||||
return updated_node
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchList(
|
||||
self, original_node: "MatchList", updated_node: "MatchList"
|
||||
) -> "MatchPattern":
|
||||
return updated_node
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchMapping(
|
||||
self, original_node: "MatchMapping", updated_node: "MatchMapping"
|
||||
) -> "MatchPattern":
|
||||
return updated_node
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchMappingElement(
|
||||
self, original_node: "MatchMappingElement", updated_node: "MatchMappingElement"
|
||||
) -> Union[
|
||||
"MatchMappingElement", FlattenSentinel["MatchMappingElement"], RemovalSentinel
|
||||
]:
|
||||
return updated_node
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchOr(
|
||||
self, original_node: "MatchOr", updated_node: "MatchOr"
|
||||
) -> "MatchPattern":
|
||||
return updated_node
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchOrElement(
|
||||
self, original_node: "MatchOrElement", updated_node: "MatchOrElement"
|
||||
) -> Union["MatchOrElement", FlattenSentinel["MatchOrElement"], RemovalSentinel]:
|
||||
return updated_node
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchPattern(
|
||||
self, original_node: "MatchPattern", updated_node: "MatchPattern"
|
||||
) -> "MatchPattern":
|
||||
return updated_node
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchSequence(
|
||||
self, original_node: "MatchSequence", updated_node: "MatchSequence"
|
||||
) -> "MatchPattern":
|
||||
return updated_node
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchSequenceElement(
|
||||
self,
|
||||
original_node: "MatchSequenceElement",
|
||||
updated_node: "MatchSequenceElement",
|
||||
) -> Union[
|
||||
"MatchSequenceElement", FlattenSentinel["MatchSequenceElement"], RemovalSentinel
|
||||
]:
|
||||
return updated_node
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchSingleton(
|
||||
self, original_node: "MatchSingleton", updated_node: "MatchSingleton"
|
||||
) -> "MatchPattern":
|
||||
return updated_node
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchStar(
|
||||
self, original_node: "MatchStar", updated_node: "MatchStar"
|
||||
) -> "MatchStar":
|
||||
return updated_node
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchTuple(
|
||||
self, original_node: "MatchTuple", updated_node: "MatchTuple"
|
||||
) -> "MatchPattern":
|
||||
return updated_node
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatchValue(
|
||||
self, original_node: "MatchValue", updated_node: "MatchValue"
|
||||
) -> "MatchPattern":
|
||||
return updated_node
|
||||
|
||||
@mark_no_op
|
||||
def leave_MatrixMultiply(
|
||||
self, original_node: "MatrixMultiply", updated_node: "MatrixMultiply"
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -155,6 +155,23 @@ from libcst._nodes.statement import (
|
|||
ImportAlias,
|
||||
ImportFrom,
|
||||
IndentedBlock,
|
||||
Match,
|
||||
MatchAs,
|
||||
MatchCase,
|
||||
MatchClass,
|
||||
MatchKeywordElement,
|
||||
MatchList,
|
||||
MatchMapping,
|
||||
MatchMappingElement,
|
||||
MatchOr,
|
||||
MatchOrElement,
|
||||
MatchPattern,
|
||||
MatchSequence,
|
||||
MatchSequenceElement,
|
||||
MatchSingleton,
|
||||
MatchStar,
|
||||
MatchTuple,
|
||||
MatchValue,
|
||||
NameItem,
|
||||
Nonlocal,
|
||||
Pass,
|
||||
|
|
@ -200,7 +217,7 @@ TYPED_FUNCTION_RETURN_MAPPING: TypingDict[Type[CSTNode], object] = {
|
|||
BitAnd: BaseBinaryOp,
|
||||
BitAndAssign: BaseAugOp,
|
||||
BitInvert: BaseUnaryOp,
|
||||
BitOr: BaseBinaryOp,
|
||||
BitOr: Union[BaseBinaryOp, MaybeSentinel],
|
||||
BitOrAssign: BaseAugOp,
|
||||
BitXor: BaseBinaryOp,
|
||||
BitXorAssign: BaseAugOp,
|
||||
|
|
@ -270,6 +287,23 @@ TYPED_FUNCTION_RETURN_MAPPING: TypingDict[Type[CSTNode], object] = {
|
|||
LessThanEqual: BaseCompOp,
|
||||
List: BaseExpression,
|
||||
ListComp: BaseExpression,
|
||||
Match: Union[BaseStatement, RemovalSentinel],
|
||||
MatchAs: MatchPattern,
|
||||
MatchCase: MatchCase,
|
||||
MatchClass: MatchPattern,
|
||||
MatchKeywordElement: Union[MatchKeywordElement, RemovalSentinel],
|
||||
MatchList: MatchPattern,
|
||||
MatchMapping: MatchPattern,
|
||||
MatchMappingElement: Union[MatchMappingElement, RemovalSentinel],
|
||||
MatchOr: MatchPattern,
|
||||
MatchOrElement: Union[MatchOrElement, RemovalSentinel],
|
||||
MatchPattern: MatchPattern,
|
||||
MatchSequence: MatchPattern,
|
||||
MatchSequenceElement: Union[MatchSequenceElement, RemovalSentinel],
|
||||
MatchSingleton: MatchPattern,
|
||||
MatchStar: MatchStar,
|
||||
MatchTuple: MatchPattern,
|
||||
MatchValue: MatchPattern,
|
||||
MatrixMultiply: BaseBinaryOp,
|
||||
MatrixMultiplyAssign: BaseAugOp,
|
||||
Minus: BaseUnaryOp,
|
||||
|
|
|
|||
|
|
@ -13,8 +13,11 @@ pub use statement::{
|
|||
AnnAssign, Annotation, AsName, Assert, Assign, AssignTarget, AssignTargetExpression, AugAssign,
|
||||
Break, ClassDef, CompoundStatement, Continue, Decorator, Del, DelTargetExpression, Else,
|
||||
ExceptHandler, ExceptStarHandler, Expr, Finally, For, FunctionDef, Global, If, Import,
|
||||
ImportAlias, ImportFrom, ImportNames, IndentedBlock, NameItem, Nonlocal, OrElse, Pass, Raise,
|
||||
Return, SimpleStatementLine, SimpleStatementSuite, SmallStatement, Statement, Suite, Try,
|
||||
ImportAlias, ImportFrom, ImportNames, IndentedBlock, Match, MatchAs, MatchCase, MatchClass,
|
||||
MatchKeywordElement, MatchList, MatchMapping, MatchMappingElement, MatchOr, MatchOrElement,
|
||||
MatchPattern, MatchSequence, MatchSequenceElement, MatchSingleton, MatchStar, MatchTuple,
|
||||
MatchValue, NameItem, Nonlocal, OrElse, Pass, Raise, Return, SimpleStatementLine,
|
||||
SimpleStatementSuite, SmallStatement, StarrableMatchSequenceElement, Statement, Suite, Try,
|
||||
TryStar, While, With, WithItem,
|
||||
};
|
||||
|
||||
|
|
@ -32,8 +35,8 @@ pub use expression::{
|
|||
|
||||
mod op;
|
||||
pub use op::{
|
||||
AssignEqual, AugOp, BinaryOp, BooleanOp, Colon, Comma, CompOp, Dot, ImportStar, Semicolon,
|
||||
UnaryOp,
|
||||
AssignEqual, AugOp, BinaryOp, BitOr, BooleanOp, Colon, Comma, CompOp, Dot, ImportStar,
|
||||
Semicolon, UnaryOp,
|
||||
};
|
||||
|
||||
mod module;
|
||||
|
|
|
|||
|
|
@ -1418,3 +1418,33 @@ impl<'a> Codegen<'a> for AugOp<'a> {
|
|||
aft.codegen(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, IntoPy)]
|
||||
pub struct BitOr<'a> {
|
||||
pub whitespace_before: ParenthesizableWhitespace<'a>,
|
||||
pub whitespace_after: ParenthesizableWhitespace<'a>,
|
||||
|
||||
pub(crate) tok: TokenRef<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Inflate<'a> for BitOr<'a> {
|
||||
fn inflate(mut self, config: &Config<'a>) -> Result<Self> {
|
||||
self.whitespace_before = parse_parenthesizable_whitespace(
|
||||
config,
|
||||
&mut (*self.tok).whitespace_before.borrow_mut(),
|
||||
)?;
|
||||
self.whitespace_after = parse_parenthesizable_whitespace(
|
||||
config,
|
||||
&mut (*self.tok).whitespace_after.borrow_mut(),
|
||||
)?;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Codegen<'a> for BitOr<'a> {
|
||||
fn codegen(&self, state: &mut CodegenState<'a>) {
|
||||
self.whitespace_before.codegen(state);
|
||||
state.add_token("|");
|
||||
self.whitespace_after.codegen(state);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ use super::{
|
|||
use crate::{
|
||||
nodes::{
|
||||
traits::{Inflate, Result, WithComma, WithLeadingLines},
|
||||
Arg, AssignEqual, Asynchronous, AugOp, Element, ParenthesizedNode,
|
||||
Arg, AssignEqual, Asynchronous, AugOp, BitOr, Element, ParenthesizedNode,
|
||||
},
|
||||
tokenizer::{
|
||||
whitespace_parser::{
|
||||
|
|
@ -23,6 +23,7 @@ use crate::{
|
|||
},
|
||||
Token,
|
||||
},
|
||||
LeftCurlyBrace, LeftSquareBracket, RightCurlyBrace, RightSquareBracket,
|
||||
};
|
||||
use libcst_derive::{Codegen, Inflate, IntoPy, ParenthesizedNode};
|
||||
|
||||
|
|
@ -55,6 +56,7 @@ pub enum CompoundStatement<'a> {
|
|||
Try(Try<'a>),
|
||||
TryStar(TryStar<'a>),
|
||||
With(With<'a>),
|
||||
Match(Match<'a>),
|
||||
}
|
||||
|
||||
impl<'a> WithLeadingLines<'a> for CompoundStatement<'a> {
|
||||
|
|
@ -68,6 +70,7 @@ impl<'a> WithLeadingLines<'a> for CompoundStatement<'a> {
|
|||
Self::Try(t) => &mut t.leading_lines,
|
||||
Self::TryStar(t) => &mut t.leading_lines,
|
||||
Self::With(w) => &mut w.leading_lines,
|
||||
Self::Match(m) => &mut m.leading_lines,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2100,3 +2103,796 @@ impl<'a> Del<'a> {
|
|||
Self { semicolon, ..self }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, IntoPy)]
|
||||
pub struct Match<'a> {
|
||||
pub subject: Expression<'a>,
|
||||
pub cases: Vec<MatchCase<'a>>,
|
||||
|
||||
pub leading_lines: Vec<EmptyLine<'a>>,
|
||||
pub whitespace_after_match: SimpleWhitespace<'a>,
|
||||
pub whitespace_before_colon: SimpleWhitespace<'a>,
|
||||
pub whitespace_after_colon: TrailingWhitespace<'a>,
|
||||
pub indent: Option<&'a str>,
|
||||
pub footer: Vec<EmptyLine<'a>>,
|
||||
|
||||
pub(crate) match_tok: TokenRef<'a>,
|
||||
pub(crate) colon_tok: TokenRef<'a>,
|
||||
pub(crate) indent_tok: TokenRef<'a>,
|
||||
pub(crate) dedent_tok: TokenRef<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Codegen<'a> for Match<'a> {
|
||||
fn codegen(&self, state: &mut CodegenState<'a>) {
|
||||
for l in &self.leading_lines {
|
||||
l.codegen(state);
|
||||
}
|
||||
state.add_indent();
|
||||
state.add_token("match");
|
||||
self.whitespace_after_match.codegen(state);
|
||||
self.subject.codegen(state);
|
||||
self.whitespace_before_colon.codegen(state);
|
||||
state.add_token(":");
|
||||
self.whitespace_after_colon.codegen(state);
|
||||
|
||||
let indent = self.indent.unwrap_or(state.default_indent);
|
||||
state.indent(indent);
|
||||
|
||||
// Note: empty cases is a syntax error
|
||||
for c in &self.cases {
|
||||
c.codegen(state);
|
||||
}
|
||||
|
||||
for f in &self.footer {
|
||||
f.codegen(state);
|
||||
}
|
||||
state.dedent();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Inflate<'a> for Match<'a> {
|
||||
fn inflate(mut self, config: &Config<'a>) -> Result<Self> {
|
||||
self.leading_lines = parse_empty_lines(
|
||||
config,
|
||||
&mut self.match_tok.whitespace_before.borrow_mut(),
|
||||
None,
|
||||
)?;
|
||||
self.whitespace_after_match =
|
||||
parse_simple_whitespace(config, &mut self.match_tok.whitespace_after.borrow_mut())?;
|
||||
self.subject = self.subject.inflate(config)?;
|
||||
self.whitespace_before_colon =
|
||||
parse_simple_whitespace(config, &mut self.colon_tok.whitespace_before.borrow_mut())?;
|
||||
self.whitespace_after_colon =
|
||||
parse_trailing_whitespace(config, &mut self.colon_tok.whitespace_after.borrow_mut())?;
|
||||
self.indent = self.indent_tok.relative_indent;
|
||||
if self.indent == Some(config.default_indent) {
|
||||
self.indent = None;
|
||||
}
|
||||
self.cases = self.cases.inflate(config)?;
|
||||
// See note about footers in `IndentedBlock`'s inflate fn
|
||||
self.footer = parse_empty_lines(
|
||||
config,
|
||||
&mut self.dedent_tok.whitespace_after.borrow_mut(),
|
||||
Some(self.indent_tok.whitespace_before.borrow().absolute_indent),
|
||||
)?;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, IntoPy)]
|
||||
pub struct MatchCase<'a> {
|
||||
pub pattern: MatchPattern<'a>,
|
||||
pub guard: Option<Expression<'a>>,
|
||||
pub body: Suite<'a>,
|
||||
|
||||
pub leading_lines: Vec<EmptyLine<'a>>,
|
||||
pub whitespace_after_case: SimpleWhitespace<'a>,
|
||||
pub whitespace_before_if: SimpleWhitespace<'a>,
|
||||
pub whitespace_after_if: SimpleWhitespace<'a>,
|
||||
pub whitespace_before_colon: SimpleWhitespace<'a>,
|
||||
|
||||
pub(crate) case_tok: TokenRef<'a>,
|
||||
pub(crate) if_tok: Option<TokenRef<'a>>,
|
||||
pub(crate) colon_tok: TokenRef<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Codegen<'a> for MatchCase<'a> {
|
||||
fn codegen(&self, state: &mut CodegenState<'a>) {
|
||||
for l in &self.leading_lines {
|
||||
l.codegen(state);
|
||||
}
|
||||
state.add_indent();
|
||||
state.add_token("case");
|
||||
self.whitespace_after_case.codegen(state);
|
||||
self.pattern.codegen(state);
|
||||
if let Some(guard) = &self.guard {
|
||||
self.whitespace_before_if.codegen(state);
|
||||
state.add_token("if");
|
||||
self.whitespace_after_if.codegen(state);
|
||||
guard.codegen(state);
|
||||
}
|
||||
self.whitespace_before_colon.codegen(state);
|
||||
state.add_token(":");
|
||||
self.body.codegen(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Inflate<'a> for MatchCase<'a> {
|
||||
fn inflate(mut self, config: &Config<'a>) -> Result<Self> {
|
||||
self.leading_lines = parse_empty_lines(
|
||||
config,
|
||||
&mut self.case_tok.whitespace_before.borrow_mut(),
|
||||
None,
|
||||
)?;
|
||||
self.whitespace_after_case =
|
||||
parse_simple_whitespace(config, &mut self.case_tok.whitespace_after.borrow_mut())?;
|
||||
self.pattern = self.pattern.inflate(config)?;
|
||||
if let Some(if_tok) = self.if_tok.as_mut() {
|
||||
self.whitespace_before_if =
|
||||
parse_simple_whitespace(config, &mut if_tok.whitespace_before.borrow_mut())?;
|
||||
self.whitespace_after_if =
|
||||
parse_simple_whitespace(config, &mut if_tok.whitespace_after.borrow_mut())?;
|
||||
|
||||
self.guard = self.guard.inflate(config)?;
|
||||
}
|
||||
self.whitespace_before_colon =
|
||||
parse_simple_whitespace(config, &mut self.colon_tok.whitespace_before.borrow_mut())?;
|
||||
self.body = self.body.inflate(config)?;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, IntoPy, Codegen, Inflate, ParenthesizedNode)]
|
||||
pub enum MatchPattern<'a> {
|
||||
Value(MatchValue<'a>),
|
||||
Singleton(MatchSingleton<'a>),
|
||||
Sequence(MatchSequence<'a>),
|
||||
Mapping(MatchMapping<'a>),
|
||||
Class(MatchClass<'a>),
|
||||
As(Box<MatchAs<'a>>),
|
||||
Or(Box<MatchOr<'a>>),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, IntoPy)]
|
||||
pub struct MatchValue<'a> {
|
||||
pub value: Expression<'a>,
|
||||
}
|
||||
|
||||
impl<'a> ParenthesizedNode<'a> for MatchValue<'a> {
|
||||
fn lpar(&self) -> &Vec<LeftParen<'a>> {
|
||||
self.value.lpar()
|
||||
}
|
||||
fn rpar(&self) -> &Vec<RightParen<'a>> {
|
||||
self.value.rpar()
|
||||
}
|
||||
fn parenthesize<F>(&self, state: &mut CodegenState<'a>, f: F)
|
||||
where
|
||||
F: FnOnce(&mut CodegenState<'a>),
|
||||
{
|
||||
self.value.parenthesize(state, f)
|
||||
}
|
||||
fn with_parens(self, left: LeftParen<'a>, right: RightParen<'a>) -> Self {
|
||||
Self {
|
||||
value: self.value.with_parens(left, right),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Codegen<'a> for MatchValue<'a> {
|
||||
fn codegen(&self, state: &mut CodegenState<'a>) {
|
||||
self.value.codegen(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Inflate<'a> for MatchValue<'a> {
|
||||
fn inflate(mut self, config: &Config<'a>) -> Result<Self> {
|
||||
self.value = self.value.inflate(config)?;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, IntoPy)]
|
||||
pub struct MatchSingleton<'a> {
|
||||
pub value: Name<'a>,
|
||||
}
|
||||
|
||||
impl<'a> ParenthesizedNode<'a> for MatchSingleton<'a> {
|
||||
fn lpar(&self) -> &Vec<LeftParen<'a>> {
|
||||
self.value.lpar()
|
||||
}
|
||||
fn rpar(&self) -> &Vec<RightParen<'a>> {
|
||||
self.value.rpar()
|
||||
}
|
||||
fn parenthesize<F>(&self, state: &mut CodegenState<'a>, f: F)
|
||||
where
|
||||
F: FnOnce(&mut CodegenState<'a>),
|
||||
{
|
||||
self.value.parenthesize(state, f)
|
||||
}
|
||||
fn with_parens(self, left: LeftParen<'a>, right: RightParen<'a>) -> Self {
|
||||
Self {
|
||||
value: self.value.with_parens(left, right),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Codegen<'a> for MatchSingleton<'a> {
|
||||
fn codegen(&self, state: &mut CodegenState<'a>) {
|
||||
self.value.codegen(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Inflate<'a> for MatchSingleton<'a> {
|
||||
fn inflate(mut self, config: &Config<'a>) -> Result<Self> {
|
||||
self.value = self.value.inflate(config)?;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, IntoPy, Codegen, Inflate, ParenthesizedNode)]
|
||||
pub enum MatchSequence<'a> {
|
||||
MatchList(MatchList<'a>),
|
||||
MatchTuple(MatchTuple<'a>),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, IntoPy, ParenthesizedNode)]
|
||||
pub struct MatchList<'a> {
|
||||
pub patterns: Vec<StarrableMatchSequenceElement<'a>>,
|
||||
pub lbracket: Option<LeftSquareBracket<'a>>,
|
||||
pub rbracket: Option<RightSquareBracket<'a>>,
|
||||
pub lpar: Vec<LeftParen<'a>>,
|
||||
pub rpar: Vec<RightParen<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Codegen<'a> for MatchList<'a> {
|
||||
fn codegen(&self, state: &mut CodegenState<'a>) {
|
||||
self.parenthesize(state, |state| {
|
||||
self.lbracket.codegen(state);
|
||||
let len = self.patterns.len();
|
||||
if len == 1 {
|
||||
self.patterns.first().unwrap().codegen(state, false, false);
|
||||
} else {
|
||||
for (idx, pat) in self.patterns.iter().enumerate() {
|
||||
pat.codegen(state, idx < len - 1, true);
|
||||
}
|
||||
}
|
||||
self.rbracket.codegen(state);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Inflate<'a> for MatchList<'a> {
|
||||
fn inflate(mut self, config: &Config<'a>) -> Result<Self> {
|
||||
self.lpar = self.lpar.inflate(config)?;
|
||||
self.lbracket = self.lbracket.inflate(config)?;
|
||||
|
||||
let len = self.patterns.len();
|
||||
self.patterns = self
|
||||
.patterns
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(idx, el)| el.inflate_element(config, idx + 1 == len))
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
self.rbracket = self.rbracket.inflate(config)?;
|
||||
self.rpar = self.rpar.inflate(config)?;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, IntoPy, ParenthesizedNode)]
|
||||
pub struct MatchTuple<'a> {
|
||||
pub patterns: Vec<StarrableMatchSequenceElement<'a>>,
|
||||
pub lpar: Vec<LeftParen<'a>>,
|
||||
pub rpar: Vec<RightParen<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Codegen<'a> for MatchTuple<'a> {
|
||||
fn codegen(&self, state: &mut CodegenState<'a>) {
|
||||
self.parenthesize(state, |state| {
|
||||
let len = self.patterns.len();
|
||||
if len == 1 {
|
||||
self.patterns.first().unwrap().codegen(state, true, false);
|
||||
} else {
|
||||
for (idx, pat) in self.patterns.iter().enumerate() {
|
||||
pat.codegen(state, idx < len - 1, true);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Inflate<'a> for MatchTuple<'a> {
|
||||
fn inflate(mut self, config: &Config<'a>) -> Result<Self> {
|
||||
self.lpar = self.lpar.inflate(config)?;
|
||||
let len = self.patterns.len();
|
||||
self.patterns = self
|
||||
.patterns
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(idx, el)| el.inflate_element(config, idx + 1 == len))
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
self.rpar = self.rpar.inflate(config)?;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, IntoPy)]
|
||||
pub enum StarrableMatchSequenceElement<'a> {
|
||||
Simple(MatchSequenceElement<'a>),
|
||||
Starred(MatchStar<'a>),
|
||||
}
|
||||
|
||||
impl<'a> StarrableMatchSequenceElement<'a> {
|
||||
fn codegen(
|
||||
&self,
|
||||
state: &mut CodegenState<'a>,
|
||||
default_comma: bool,
|
||||
default_comma_whitespace: bool,
|
||||
) {
|
||||
match &self {
|
||||
Self::Simple(s) => s.codegen(state, default_comma, default_comma_whitespace),
|
||||
Self::Starred(s) => s.codegen(state, default_comma, default_comma_whitespace),
|
||||
}
|
||||
}
|
||||
fn inflate_element(self, config: &Config<'a>, last_element: bool) -> Result<Self> {
|
||||
Ok(match self {
|
||||
Self::Simple(s) => Self::Simple(s.inflate_element(config, last_element)?),
|
||||
Self::Starred(s) => Self::Starred(s.inflate_element(config, last_element)?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> WithComma<'a> for StarrableMatchSequenceElement<'a> {
|
||||
fn with_comma(self, comma: Comma<'a>) -> Self {
|
||||
match self {
|
||||
Self::Simple(s) => Self::Simple(s.with_comma(comma)),
|
||||
Self::Starred(s) => Self::Starred(s.with_comma(comma)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, IntoPy)]
|
||||
pub struct MatchSequenceElement<'a> {
|
||||
pub value: MatchPattern<'a>,
|
||||
pub comma: Option<Comma<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> MatchSequenceElement<'a> {
|
||||
fn codegen(
|
||||
&self,
|
||||
state: &mut CodegenState<'a>,
|
||||
default_comma: bool,
|
||||
default_comma_whitespace: bool,
|
||||
) {
|
||||
self.value.codegen(state);
|
||||
self.comma.codegen(state);
|
||||
if self.comma.is_none() && default_comma {
|
||||
state.add_token(if default_comma_whitespace { ", " } else { "," });
|
||||
}
|
||||
}
|
||||
|
||||
fn inflate_element(mut self, config: &Config<'a>, last_element: bool) -> Result<Self> {
|
||||
self.value = self.value.inflate(config)?;
|
||||
self.comma = if last_element {
|
||||
self.comma.map(|c| c.inflate_before(config)).transpose()
|
||||
} else {
|
||||
self.comma.inflate(config)
|
||||
}?;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> WithComma<'a> for MatchSequenceElement<'a> {
|
||||
fn with_comma(self, comma: Comma<'a>) -> Self {
|
||||
Self {
|
||||
comma: Some(comma),
|
||||
..self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, IntoPy)]
|
||||
pub struct MatchStar<'a> {
|
||||
pub name: Option<Name<'a>>,
|
||||
pub comma: Option<Comma<'a>>,
|
||||
pub whitespace_before_name: ParenthesizableWhitespace<'a>,
|
||||
|
||||
pub(crate) star_tok: TokenRef<'a>,
|
||||
}
|
||||
|
||||
impl<'a> MatchStar<'a> {
|
||||
fn codegen(
|
||||
&self,
|
||||
state: &mut CodegenState<'a>,
|
||||
default_comma: bool,
|
||||
default_comma_whitespace: bool,
|
||||
) {
|
||||
state.add_token("*");
|
||||
self.whitespace_before_name.codegen(state);
|
||||
if let Some(name) = &self.name {
|
||||
name.codegen(state);
|
||||
} else {
|
||||
state.add_token("_");
|
||||
}
|
||||
self.comma.codegen(state);
|
||||
if self.comma.is_none() && default_comma {
|
||||
state.add_token(if default_comma_whitespace { ", " } else { "," });
|
||||
}
|
||||
}
|
||||
|
||||
fn inflate_element(mut self, config: &Config<'a>, last_element: bool) -> Result<Self> {
|
||||
self.whitespace_before_name = parse_parenthesizable_whitespace(
|
||||
config,
|
||||
&mut self.star_tok.whitespace_after.borrow_mut(),
|
||||
)?;
|
||||
self.name = self.name.inflate(config)?;
|
||||
self.comma = if last_element {
|
||||
self.comma.map(|c| c.inflate_before(config)).transpose()
|
||||
} else {
|
||||
self.comma.inflate(config)
|
||||
}?;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> WithComma<'a> for MatchStar<'a> {
|
||||
fn with_comma(self, comma: Comma<'a>) -> Self {
|
||||
Self {
|
||||
comma: Some(comma),
|
||||
..self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, IntoPy, ParenthesizedNode)]
|
||||
pub struct MatchMapping<'a> {
|
||||
pub elements: Vec<MatchMappingElement<'a>>,
|
||||
pub rest: Option<Name<'a>>,
|
||||
pub trailing_comma: Option<Comma<'a>>,
|
||||
pub lbrace: LeftCurlyBrace<'a>,
|
||||
pub rbrace: RightCurlyBrace<'a>,
|
||||
pub lpar: Vec<LeftParen<'a>>,
|
||||
pub rpar: Vec<RightParen<'a>>,
|
||||
|
||||
pub whitespace_before_rest: SimpleWhitespace<'a>,
|
||||
|
||||
pub(crate) star_tok: Option<TokenRef<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Codegen<'a> for MatchMapping<'a> {
|
||||
fn codegen(&self, state: &mut CodegenState<'a>) {
|
||||
self.parenthesize(state, |state| {
|
||||
self.lbrace.codegen(state);
|
||||
let len = self.elements.len();
|
||||
for (idx, el) in self.elements.iter().enumerate() {
|
||||
el.codegen(state, self.rest.is_some() || idx < len - 1);
|
||||
}
|
||||
if let Some(rest) = &self.rest {
|
||||
state.add_token("**");
|
||||
self.whitespace_before_rest.codegen(state);
|
||||
rest.codegen(state);
|
||||
self.trailing_comma.codegen(state);
|
||||
}
|
||||
self.rbrace.codegen(state);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Inflate<'a> for MatchMapping<'a> {
|
||||
fn inflate(mut self, config: &Config<'a>) -> Result<Self> {
|
||||
self.lpar = self.lpar.inflate(config)?;
|
||||
self.lbrace = self.lbrace.inflate(config)?;
|
||||
|
||||
let len = self.elements.len();
|
||||
let no_star = self.star_tok.is_none();
|
||||
self.elements = self
|
||||
.elements
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(idx, el)| el.inflate_element(config, no_star && idx + 1 == len))
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
if let Some(star_tok) = self.star_tok.as_mut() {
|
||||
self.whitespace_before_rest =
|
||||
parse_simple_whitespace(config, &mut star_tok.whitespace_after.borrow_mut())?;
|
||||
self.rest = self.rest.inflate(config)?;
|
||||
self.trailing_comma = self
|
||||
.trailing_comma
|
||||
.map(|c| c.inflate_before(config))
|
||||
.transpose()?;
|
||||
}
|
||||
|
||||
self.rbrace = self.rbrace.inflate(config)?;
|
||||
self.rpar = self.rpar.inflate(config)?;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, IntoPy)]
|
||||
pub struct MatchMappingElement<'a> {
|
||||
pub key: Expression<'a>,
|
||||
pub pattern: MatchPattern<'a>,
|
||||
pub comma: Option<Comma<'a>>,
|
||||
|
||||
pub whitespace_before_colon: ParenthesizableWhitespace<'a>,
|
||||
pub whitespace_after_colon: ParenthesizableWhitespace<'a>,
|
||||
|
||||
pub(crate) colon_tok: TokenRef<'a>,
|
||||
}
|
||||
|
||||
impl<'a> MatchMappingElement<'a> {
|
||||
fn codegen(&self, state: &mut CodegenState<'a>, default_comma: bool) {
|
||||
self.key.codegen(state);
|
||||
self.whitespace_before_colon.codegen(state);
|
||||
state.add_token(":");
|
||||
self.whitespace_after_colon.codegen(state);
|
||||
self.pattern.codegen(state);
|
||||
self.comma.codegen(state);
|
||||
if self.comma.is_none() && default_comma {
|
||||
state.add_token(", ");
|
||||
}
|
||||
}
|
||||
|
||||
fn inflate_element(mut self, config: &Config<'a>, last_element: bool) -> Result<Self> {
|
||||
self.key = self.key.inflate(config)?;
|
||||
self.whitespace_before_colon = parse_parenthesizable_whitespace(
|
||||
config,
|
||||
&mut self.colon_tok.whitespace_before.borrow_mut(),
|
||||
)?;
|
||||
self.whitespace_after_colon = parse_parenthesizable_whitespace(
|
||||
config,
|
||||
&mut self.colon_tok.whitespace_after.borrow_mut(),
|
||||
)?;
|
||||
self.pattern = self.pattern.inflate(config)?;
|
||||
self.comma = if last_element {
|
||||
self.comma.map(|c| c.inflate_before(config)).transpose()
|
||||
} else {
|
||||
self.comma.inflate(config)
|
||||
}?;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> WithComma<'a> for MatchMappingElement<'a> {
|
||||
fn with_comma(self, comma: Comma<'a>) -> Self {
|
||||
Self {
|
||||
comma: Some(comma),
|
||||
..self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, IntoPy, ParenthesizedNode)]
|
||||
pub struct MatchClass<'a> {
|
||||
pub cls: NameOrAttribute<'a>,
|
||||
pub patterns: Vec<MatchSequenceElement<'a>>,
|
||||
pub kwds: Vec<MatchKeywordElement<'a>>,
|
||||
pub lpar: Vec<LeftParen<'a>>,
|
||||
pub rpar: Vec<RightParen<'a>>,
|
||||
|
||||
pub whitespace_after_cls: ParenthesizableWhitespace<'a>,
|
||||
pub whitespace_before_patterns: ParenthesizableWhitespace<'a>,
|
||||
pub whitespace_after_kwds: ParenthesizableWhitespace<'a>,
|
||||
|
||||
pub(crate) lpar_tok: TokenRef<'a>,
|
||||
pub(crate) rpar_tok: TokenRef<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Codegen<'a> for MatchClass<'a> {
|
||||
fn codegen(&self, state: &mut CodegenState<'a>) {
|
||||
self.parenthesize(state, |state| {
|
||||
self.cls.codegen(state);
|
||||
self.whitespace_after_cls.codegen(state);
|
||||
state.add_token("(");
|
||||
self.whitespace_before_patterns.codegen(state);
|
||||
let patlen = self.patterns.len();
|
||||
let kwdlen = self.kwds.len();
|
||||
for (idx, pat) in self.patterns.iter().enumerate() {
|
||||
pat.codegen(state, idx < patlen - 1 + kwdlen, patlen == 1 && kwdlen == 0);
|
||||
}
|
||||
for (idx, kwd) in self.kwds.iter().enumerate() {
|
||||
kwd.codegen(state, idx < kwdlen - 1);
|
||||
}
|
||||
self.whitespace_after_kwds.codegen(state);
|
||||
state.add_token(")");
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Inflate<'a> for MatchClass<'a> {
|
||||
fn inflate(mut self, config: &Config<'a>) -> Result<Self> {
|
||||
self.lpar = self.lpar.inflate(config)?;
|
||||
|
||||
self.cls = self.cls.inflate(config)?;
|
||||
self.whitespace_after_cls = parse_parenthesizable_whitespace(
|
||||
config,
|
||||
&mut self.lpar_tok.whitespace_before.borrow_mut(),
|
||||
)?;
|
||||
self.whitespace_before_patterns = parse_parenthesizable_whitespace(
|
||||
config,
|
||||
&mut self.lpar_tok.whitespace_after.borrow_mut(),
|
||||
)?;
|
||||
|
||||
let patlen = self.patterns.len();
|
||||
let kwdlen = self.kwds.len();
|
||||
self.patterns = self
|
||||
.patterns
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(idx, pat)| pat.inflate_element(config, idx + 1 == patlen + kwdlen))
|
||||
.collect::<Result<_>>()?;
|
||||
self.kwds = self
|
||||
.kwds
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(idx, kwd)| kwd.inflate_element(config, idx + 1 == kwdlen))
|
||||
.collect::<Result<_>>()?;
|
||||
|
||||
self.whitespace_after_kwds = parse_parenthesizable_whitespace(
|
||||
config,
|
||||
&mut self.rpar_tok.whitespace_before.borrow_mut(),
|
||||
)?;
|
||||
|
||||
self.rpar = self.rpar.inflate(config)?;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, IntoPy)]
|
||||
pub struct MatchKeywordElement<'a> {
|
||||
pub key: Name<'a>,
|
||||
pub pattern: MatchPattern<'a>,
|
||||
pub comma: Option<Comma<'a>>,
|
||||
|
||||
pub whitespace_before_equal: ParenthesizableWhitespace<'a>,
|
||||
pub whitespace_after_equal: ParenthesizableWhitespace<'a>,
|
||||
|
||||
pub(crate) equal_tok: TokenRef<'a>,
|
||||
}
|
||||
|
||||
impl<'a> MatchKeywordElement<'a> {
|
||||
fn codegen(&self, state: &mut CodegenState<'a>, default_comma: bool) {
|
||||
self.key.codegen(state);
|
||||
self.whitespace_before_equal.codegen(state);
|
||||
state.add_token("=");
|
||||
self.whitespace_after_equal.codegen(state);
|
||||
self.pattern.codegen(state);
|
||||
self.comma.codegen(state);
|
||||
if self.comma.is_none() && default_comma {
|
||||
state.add_token(", ");
|
||||
}
|
||||
}
|
||||
fn inflate_element(mut self, config: &Config<'a>, last_element: bool) -> Result<Self> {
|
||||
self.key = self.key.inflate(config)?;
|
||||
self.whitespace_before_equal = parse_parenthesizable_whitespace(
|
||||
config,
|
||||
&mut self.equal_tok.whitespace_before.borrow_mut(),
|
||||
)?;
|
||||
self.whitespace_after_equal = parse_parenthesizable_whitespace(
|
||||
config,
|
||||
&mut self.equal_tok.whitespace_after.borrow_mut(),
|
||||
)?;
|
||||
self.pattern = self.pattern.inflate(config)?;
|
||||
self.comma = if last_element {
|
||||
self.comma.map(|c| c.inflate_before(config)).transpose()
|
||||
} else {
|
||||
self.comma.inflate(config)
|
||||
}?;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> WithComma<'a> for MatchKeywordElement<'a> {
|
||||
fn with_comma(self, comma: Comma<'a>) -> Self {
|
||||
Self {
|
||||
comma: Some(comma),
|
||||
..self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, IntoPy, ParenthesizedNode)]
|
||||
pub struct MatchAs<'a> {
|
||||
pub pattern: Option<MatchPattern<'a>>,
|
||||
pub name: Option<Name<'a>>,
|
||||
pub lpar: Vec<LeftParen<'a>>,
|
||||
pub rpar: Vec<RightParen<'a>>,
|
||||
|
||||
pub whitespace_before_as: Option<ParenthesizableWhitespace<'a>>,
|
||||
pub whitespace_after_as: Option<ParenthesizableWhitespace<'a>>,
|
||||
|
||||
pub(crate) as_tok: Option<TokenRef<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Codegen<'a> for MatchAs<'a> {
|
||||
fn codegen(&self, state: &mut CodegenState<'a>) {
|
||||
self.parenthesize(state, |state| {
|
||||
if let Some(pat) = &self.pattern {
|
||||
pat.codegen(state);
|
||||
self.whitespace_before_as.codegen(state);
|
||||
state.add_token("as");
|
||||
self.whitespace_after_as.codegen(state);
|
||||
}
|
||||
if let Some(name) = &self.name {
|
||||
name.codegen(state);
|
||||
} else {
|
||||
state.add_token("_");
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Inflate<'a> for MatchAs<'a> {
|
||||
fn inflate(mut self, config: &Config<'a>) -> Result<Self> {
|
||||
self.lpar = self.lpar.inflate(config)?;
|
||||
self.pattern = self.pattern.inflate(config)?;
|
||||
if let Some(as_tok) = self.as_tok.as_mut() {
|
||||
self.whitespace_before_as = Some(parse_parenthesizable_whitespace(
|
||||
config,
|
||||
&mut as_tok.whitespace_before.borrow_mut(),
|
||||
)?);
|
||||
self.whitespace_after_as = Some(parse_parenthesizable_whitespace(
|
||||
config,
|
||||
&mut as_tok.whitespace_after.borrow_mut(),
|
||||
)?);
|
||||
}
|
||||
self.name = self.name.inflate(config)?;
|
||||
self.rpar = self.rpar.inflate(config)?;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, IntoPy)]
|
||||
pub struct MatchOrElement<'a> {
|
||||
pub pattern: MatchPattern<'a>,
|
||||
pub separator: Option<BitOr<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> MatchOrElement<'a> {
|
||||
fn codegen(&self, state: &mut CodegenState<'a>, default_separator: bool) {
|
||||
self.pattern.codegen(state);
|
||||
self.separator.codegen(state);
|
||||
if self.separator.is_none() && default_separator {
|
||||
state.add_token(" | ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Inflate<'a> for MatchOrElement<'a> {
|
||||
fn inflate(mut self, config: &Config<'a>) -> Result<Self> {
|
||||
self.pattern = self.pattern.inflate(config)?;
|
||||
self.separator = self.separator.inflate(config)?;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, IntoPy, ParenthesizedNode)]
|
||||
pub struct MatchOr<'a> {
|
||||
pub patterns: Vec<MatchOrElement<'a>>,
|
||||
pub lpar: Vec<LeftParen<'a>>,
|
||||
pub rpar: Vec<RightParen<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Codegen<'a> for MatchOr<'a> {
|
||||
fn codegen(&self, state: &mut CodegenState<'a>) {
|
||||
self.parenthesize(state, |state| {
|
||||
let len = self.patterns.len();
|
||||
for (idx, pat) in self.patterns.iter().enumerate() {
|
||||
pat.codegen(state, idx + 1 < len)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Inflate<'a> for MatchOr<'a> {
|
||||
fn inflate(mut self, config: &Config<'a>) -> Result<Self> {
|
||||
self.lpar = self.lpar.inflate(config)?;
|
||||
self.patterns = self.patterns.inflate(config)?;
|
||||
self.rpar = self.rpar.inflate(config)?;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use crate::{
|
|||
tokenizer::whitespace_parser::{Config, WhitespaceError},
|
||||
Codegen, CodegenState, Comma, EmptyLine, LeftParen, RightParen,
|
||||
};
|
||||
use std::ops::Deref;
|
||||
|
||||
pub trait WithComma<'a> {
|
||||
fn with_comma(self, comma: Comma<'a>) -> Self;
|
||||
|
|
@ -32,6 +33,24 @@ pub trait ParenthesizedNode<'a> {
|
|||
fn with_parens(self, left: LeftParen<'a>, right: RightParen<'a>) -> Self;
|
||||
}
|
||||
|
||||
impl<'a, T: ParenthesizedNode<'a>> ParenthesizedNode<'a> for Box<T> {
|
||||
fn lpar(&self) -> &Vec<LeftParen<'a>> {
|
||||
self.deref().lpar()
|
||||
}
|
||||
fn rpar(&self) -> &Vec<RightParen<'a>> {
|
||||
self.deref().rpar()
|
||||
}
|
||||
fn parenthesize<F>(&self, state: &mut CodegenState<'a>, f: F)
|
||||
where
|
||||
F: FnOnce(&mut CodegenState<'a>),
|
||||
{
|
||||
self.deref().parenthesize(state, f)
|
||||
}
|
||||
fn with_parens(self, left: LeftParen<'a>, right: RightParen<'a>) -> Self {
|
||||
Self::new((*self).with_parens(left, right))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WithLeadingLines<'a> {
|
||||
fn leading_lines(&mut self) -> &mut Vec<EmptyLine<'a>>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ use TokType::{
|
|||
};
|
||||
|
||||
pub type Result<'a, T> = std::result::Result<T, ParserError<'a>>;
|
||||
type GrammarResult<T> = std::result::Result<T, &'static str>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TokVec<'a>(Vec<Rc<Token<'a>>>);
|
||||
|
|
@ -150,6 +151,7 @@ parser! {
|
|||
/ &lit("try") t:try_stmt() { CompoundStatement::Try(t) }
|
||||
/ &lit("try") t:try_star_stmt() { CompoundStatement::TryStar(t) }
|
||||
/ &lit("while") w:while_stmt() { CompoundStatement::While(w) }
|
||||
/ m:match_stmt() { CompoundStatement::Match(m) }
|
||||
|
||||
// Simple statements
|
||||
|
||||
|
|
@ -529,6 +531,218 @@ parser! {
|
|||
}
|
||||
|
||||
|
||||
// Match statement
|
||||
|
||||
rule match_stmt() -> Match<'a>
|
||||
= kw:lit("match") subject:subject_expr() col:lit(":") tok(NL, "NEWLINE")
|
||||
i:tok(Indent, "INDENT") cases:case_block()+ d:tok(Dedent, "DEDENT") {
|
||||
make_match(kw, subject, col, i, cases, d)
|
||||
}
|
||||
|
||||
rule subject_expr() -> Expression<'a>
|
||||
= first:star_named_expression() c:comma() rest:star_named_expressions()? {
|
||||
Expression::Tuple(
|
||||
make_tuple_from_elements(first.with_comma(c), rest.unwrap_or_default())
|
||||
)
|
||||
}
|
||||
/ named_expression()
|
||||
|
||||
rule case_block() -> MatchCase<'a>
|
||||
= kw:lit("case") pattern:patterns() guard:guard()? col:lit(":") body:block() {
|
||||
make_case(kw, pattern, guard, col, body)
|
||||
}
|
||||
|
||||
rule guard() -> (TokenRef<'a>, Expression<'a>)
|
||||
= kw:lit("if") exp:named_expression() { (kw, exp) }
|
||||
|
||||
rule patterns() -> MatchPattern<'a>
|
||||
= pats:open_sequence_pattern() {
|
||||
MatchPattern::Sequence(make_list_pattern(None, pats, None))
|
||||
}
|
||||
/ pattern()
|
||||
|
||||
rule pattern() -> MatchPattern<'a>
|
||||
= as_pattern()
|
||||
/ or_pattern()
|
||||
|
||||
rule as_pattern() -> MatchPattern<'a>
|
||||
= pat:or_pattern() kw:lit("as") target:pattern_capture_target() {
|
||||
make_as_pattern(Some(pat), Some(kw), Some(target))
|
||||
}
|
||||
|
||||
rule or_pattern() -> MatchPattern<'a>
|
||||
= pats:separated(<closed_pattern()>, <lit("|")>) {
|
||||
make_or_pattern(pats.0, pats.1)
|
||||
}
|
||||
|
||||
rule closed_pattern() -> MatchPattern<'a>
|
||||
= literal_pattern()
|
||||
/ capture_pattern()
|
||||
/ wildcard_pattern()
|
||||
/ value_pattern()
|
||||
/ group_pattern()
|
||||
/ sequence_pattern()
|
||||
/ mapping_pattern()
|
||||
/ class_pattern()
|
||||
|
||||
rule literal_pattern() -> MatchPattern<'a>
|
||||
= val:signed_number() !(lit("+") / lit("-")) { make_match_value(val) }
|
||||
/ val:complex_number() { make_match_value(val) }
|
||||
/ val:strings() { make_match_value(val.into()) }
|
||||
/ n:lit("None") { make_match_singleton(make_name(n)) }
|
||||
/ n:lit("True") { make_match_singleton(make_name(n)) }
|
||||
/ n:lit("False") { make_match_singleton(make_name(n)) }
|
||||
|
||||
rule literal_expr() -> Expression<'a>
|
||||
= val:signed_number() !(lit("+") / lit("-")) { val }
|
||||
/ val:complex_number() { val }
|
||||
/ val:strings() { val.into() }
|
||||
/ n:lit("None") { Expression::Name(make_name(n)) }
|
||||
/ n:lit("True") { Expression::Name(make_name(n)) }
|
||||
/ n:lit("False") { Expression::Name(make_name(n)) }
|
||||
|
||||
rule complex_number() -> Expression<'a>
|
||||
= re:signed_real_number() op:(lit("+")/lit("-")) im:imaginary_number() {?
|
||||
make_binary_op(re, op, im).map_err(|_| "complex number")
|
||||
}
|
||||
|
||||
rule signed_number() -> Expression<'a>
|
||||
= n:tok(Number, "number") { make_number(n) }
|
||||
/ op:lit("-") n:tok(Number, "number") {?
|
||||
make_unary_op(op, make_number(n)).map_err(|_| "signed number")
|
||||
}
|
||||
|
||||
rule signed_real_number() -> Expression<'a>
|
||||
= real_number()
|
||||
/ op:lit("-") n:real_number() {?
|
||||
make_unary_op(op, n).map_err(|_| "signed real number")
|
||||
}
|
||||
|
||||
rule real_number() -> Expression<'a>
|
||||
= n:tok(Number, "number") {? ensure_real_number(n) }
|
||||
|
||||
rule imaginary_number() -> Expression<'a>
|
||||
= n:tok(Number, "number") {? ensure_imaginary_number(n) }
|
||||
|
||||
rule capture_pattern() -> MatchPattern<'a>
|
||||
= t:pattern_capture_target() { make_as_pattern(None, None, Some(t)) }
|
||||
|
||||
rule pattern_capture_target() -> Name<'a>
|
||||
= !lit("_") n:name() !(lit(".") / lit("(") / lit("=")) { n }
|
||||
|
||||
rule wildcard_pattern() -> MatchPattern<'a>
|
||||
= lit("_") { make_as_pattern(None, None, None) }
|
||||
|
||||
rule value_pattern() -> MatchPattern<'a>
|
||||
= v:attr() !(lit(".") / lit("(") / lit("=")) {
|
||||
make_match_value(v.into())
|
||||
}
|
||||
|
||||
// In upstream attr and name_or_attr are mutually recursive, but rust-peg
|
||||
// doesn't support this yet.
|
||||
rule attr() -> NameOrAttribute<'a>
|
||||
= &(name() lit(".")) v:name_or_attr() { v }
|
||||
|
||||
#[cache_left_rec]
|
||||
rule name_or_attr() -> NameOrAttribute<'a>
|
||||
= val:name_or_attr() d:lit(".") attr:name() {
|
||||
NameOrAttribute::A(make_attribute(val.into(), d, attr))
|
||||
}
|
||||
/ n:name() { NameOrAttribute::N(n) }
|
||||
|
||||
rule group_pattern() -> MatchPattern<'a>
|
||||
= l:lpar() pat:pattern() r:rpar() { pat.with_parens(l, r) }
|
||||
|
||||
rule sequence_pattern() -> MatchPattern<'a>
|
||||
= l:lbrak() pats:maybe_sequence_pattern()? r:rbrak() {
|
||||
MatchPattern::Sequence(
|
||||
make_list_pattern(Some(l), pats.unwrap_or_default(), Some(r))
|
||||
)
|
||||
}
|
||||
/ l:lpar() pats:open_sequence_pattern()? r:rpar() {
|
||||
MatchPattern::Sequence(make_tuple_pattern(l, pats.unwrap_or_default(), r))
|
||||
}
|
||||
|
||||
rule open_sequence_pattern() -> Vec<StarrableMatchSequenceElement<'a>>
|
||||
= pat:maybe_star_pattern() c:comma() pats:maybe_sequence_pattern()? {
|
||||
make_open_sequence_pattern(pat, c, pats.unwrap_or_default())
|
||||
}
|
||||
|
||||
rule maybe_sequence_pattern() -> Vec<StarrableMatchSequenceElement<'a>>
|
||||
= pats:separated_trailer(<maybe_star_pattern()>, <comma()>) {
|
||||
comma_separate(pats.0, pats.1, pats.2)
|
||||
}
|
||||
|
||||
rule maybe_star_pattern() -> StarrableMatchSequenceElement<'a>
|
||||
= s:star_pattern() { StarrableMatchSequenceElement::Starred(s) }
|
||||
/ p:pattern() {
|
||||
StarrableMatchSequenceElement::Simple(
|
||||
make_match_sequence_element(p)
|
||||
)
|
||||
}
|
||||
|
||||
rule star_pattern() -> MatchStar<'a>
|
||||
= star:lit("*") t:pattern_capture_target() {make_match_star(star, Some(t))}
|
||||
/ star:lit("*") t:wildcard_pattern() { make_match_star(star, None) }
|
||||
|
||||
rule mapping_pattern() -> MatchPattern<'a>
|
||||
= l:lbrace() r:rbrace() {
|
||||
make_match_mapping(l, vec![], None, None, None, None, r)
|
||||
}
|
||||
/ l:lbrace() rest:double_star_pattern() trail:comma()? r:rbrace() {
|
||||
make_match_mapping(l, vec![], None, Some(rest.0), Some(rest.1), trail, r)
|
||||
}
|
||||
/ l:lbrace() items:items_pattern() c:comma() rest:double_star_pattern()
|
||||
trail:comma()? r:rbrace() {
|
||||
make_match_mapping(l, items, Some(c), Some(rest.0), Some(rest.1), trail, r)
|
||||
}
|
||||
/ l:lbrace() items:items_pattern() trail:comma()? r:rbrace() {
|
||||
make_match_mapping(l, items, trail, None, None, None, r)
|
||||
}
|
||||
|
||||
rule items_pattern() -> Vec<MatchMappingElement<'a>>
|
||||
= pats:separated(<key_value_pattern()>, <comma()>) {
|
||||
comma_separate(pats.0, pats.1, None)
|
||||
}
|
||||
|
||||
rule key_value_pattern() -> MatchMappingElement<'a>
|
||||
= key:(literal_expr() / a:attr() {a.into()}) colon:lit(":") pat:pattern() {
|
||||
make_match_mapping_element(key, colon, pat)
|
||||
}
|
||||
|
||||
rule double_star_pattern() -> (TokenRef<'a>, Name<'a>)
|
||||
= star:lit("**") n:pattern_capture_target() { (star, n) }
|
||||
|
||||
rule class_pattern() -> MatchPattern<'a>
|
||||
= cls:name_or_attr() l:lit("(") r:lit(")") {
|
||||
make_class_pattern(cls, l, vec![], None, vec![], None, r)
|
||||
}
|
||||
/ cls:name_or_attr() l:lit("(") pats:positional_patterns() c:comma()? r:lit(")") {
|
||||
make_class_pattern(cls, l, pats, c, vec![], None, r)
|
||||
}
|
||||
/ cls:name_or_attr() l:lit("(") kwds:keyword_patterns() c:comma()? r:lit(")") {
|
||||
make_class_pattern(cls, l, vec![], None, kwds, c, r)
|
||||
}
|
||||
/ cls:name_or_attr() l:lit("(") pats:positional_patterns() c:comma()
|
||||
kwds:keyword_patterns() trail:comma()? r:lit(")") {
|
||||
make_class_pattern(cls, l, pats, Some(c), kwds, trail, r)
|
||||
}
|
||||
|
||||
rule positional_patterns() -> Vec<MatchSequenceElement<'a>>
|
||||
= pats:separated(<p:pattern() { make_match_sequence_element(p) }>, <comma()>) {
|
||||
comma_separate(pats.0, pats.1, None)
|
||||
}
|
||||
|
||||
rule keyword_patterns() -> Vec<MatchKeywordElement<'a>>
|
||||
= pats:separated(<keyword_pattern()>, <comma()>) {
|
||||
comma_separate(pats.0, pats.1, None)
|
||||
}
|
||||
|
||||
rule keyword_pattern() -> MatchKeywordElement<'a>
|
||||
= arg:name() eq:lit("=") value:pattern() {
|
||||
make_match_keyword_element(arg, eq, value)
|
||||
}
|
||||
|
||||
// Expressions
|
||||
|
||||
#[cache]
|
||||
|
|
@ -1977,6 +2191,15 @@ fn make_tuple<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
fn make_tuple_from_elements<'a>(first: Element<'a>, mut rest: Vec<Element<'a>>) -> Tuple<'a> {
|
||||
rest.insert(0, first);
|
||||
Tuple {
|
||||
elements: rest,
|
||||
lpar: Default::default(),
|
||||
rpar: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn make_kwarg<'a>(name: Name<'a>, eq: TokenRef<'a>, value: Expression<'a>) -> Arg<'a> {
|
||||
let equal = Some(make_assign_equal(eq));
|
||||
let keyword = Some(name);
|
||||
|
|
@ -3047,3 +3270,272 @@ fn make_named_expr<'a>(name: Name<'a>, tok: TokenRef<'a>, expr: Expression<'a>)
|
|||
walrus_tok: tok,
|
||||
}
|
||||
}
|
||||
|
||||
fn make_match<'a>(
|
||||
match_tok: TokenRef<'a>,
|
||||
subject: Expression<'a>,
|
||||
colon_tok: TokenRef<'a>,
|
||||
indent_tok: TokenRef<'a>,
|
||||
cases: Vec<MatchCase<'a>>,
|
||||
dedent_tok: TokenRef<'a>,
|
||||
) -> Match<'a> {
|
||||
Match {
|
||||
subject,
|
||||
cases,
|
||||
leading_lines: Default::default(),
|
||||
whitespace_after_match: Default::default(),
|
||||
whitespace_before_colon: Default::default(),
|
||||
whitespace_after_colon: Default::default(),
|
||||
indent: Default::default(),
|
||||
footer: Default::default(),
|
||||
match_tok,
|
||||
colon_tok,
|
||||
indent_tok,
|
||||
dedent_tok,
|
||||
}
|
||||
}
|
||||
|
||||
fn make_case<'a>(
|
||||
case_tok: TokenRef<'a>,
|
||||
pattern: MatchPattern<'a>,
|
||||
guard: Option<(TokenRef<'a>, Expression<'a>)>,
|
||||
colon_tok: TokenRef<'a>,
|
||||
body: Suite<'a>,
|
||||
) -> MatchCase<'a> {
|
||||
let (if_tok, guard) = match guard {
|
||||
Some((if_tok, guard)) => (Some(if_tok), Some(guard)),
|
||||
None => (None, None),
|
||||
};
|
||||
MatchCase {
|
||||
pattern,
|
||||
guard,
|
||||
body,
|
||||
leading_lines: Default::default(),
|
||||
whitespace_after_case: Default::default(),
|
||||
whitespace_before_if: Default::default(),
|
||||
whitespace_after_if: Default::default(),
|
||||
whitespace_before_colon: Default::default(),
|
||||
case_tok,
|
||||
if_tok,
|
||||
colon_tok,
|
||||
}
|
||||
}
|
||||
|
||||
fn make_match_value(value: Expression) -> MatchPattern {
|
||||
MatchPattern::Value(MatchValue { value })
|
||||
}
|
||||
|
||||
fn make_match_singleton(value: Name) -> MatchPattern {
|
||||
MatchPattern::Singleton(MatchSingleton { value })
|
||||
}
|
||||
|
||||
fn make_list_pattern<'a>(
|
||||
lbracket: Option<LeftSquareBracket<'a>>,
|
||||
patterns: Vec<StarrableMatchSequenceElement<'a>>,
|
||||
rbracket: Option<RightSquareBracket<'a>>,
|
||||
) -> MatchSequence<'a> {
|
||||
MatchSequence::MatchList(MatchList {
|
||||
patterns,
|
||||
lbracket,
|
||||
rbracket,
|
||||
lpar: Default::default(),
|
||||
rpar: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
fn make_as_pattern<'a>(
|
||||
pattern: Option<MatchPattern<'a>>,
|
||||
as_tok: Option<TokenRef<'a>>,
|
||||
name: Option<Name<'a>>,
|
||||
) -> MatchPattern<'a> {
|
||||
MatchPattern::As(Box::new(MatchAs {
|
||||
pattern,
|
||||
name,
|
||||
lpar: Default::default(),
|
||||
rpar: Default::default(),
|
||||
whitespace_before_as: Default::default(),
|
||||
whitespace_after_as: Default::default(),
|
||||
as_tok,
|
||||
}))
|
||||
}
|
||||
|
||||
fn make_bit_or(tok: TokenRef) -> BitOr {
|
||||
BitOr {
|
||||
whitespace_before: Default::default(),
|
||||
whitespace_after: Default::default(),
|
||||
tok,
|
||||
}
|
||||
}
|
||||
|
||||
fn make_or_pattern<'a>(
|
||||
first: MatchPattern<'a>,
|
||||
rest: Vec<(TokenRef<'a>, MatchPattern<'a>)>,
|
||||
) -> MatchPattern<'a> {
|
||||
if rest.is_empty() {
|
||||
return first;
|
||||
}
|
||||
|
||||
let mut patterns = vec![];
|
||||
let mut current = first;
|
||||
for (sep, next) in rest {
|
||||
let op = make_bit_or(sep);
|
||||
patterns.push(MatchOrElement {
|
||||
pattern: current,
|
||||
separator: Some(op),
|
||||
});
|
||||
current = next;
|
||||
}
|
||||
patterns.push(MatchOrElement {
|
||||
pattern: current,
|
||||
separator: None,
|
||||
});
|
||||
MatchPattern::Or(Box::new(MatchOr {
|
||||
patterns,
|
||||
lpar: Default::default(),
|
||||
rpar: Default::default(),
|
||||
}))
|
||||
}
|
||||
|
||||
fn ensure_real_number(tok: TokenRef) -> GrammarResult<Expression> {
|
||||
match make_number(tok) {
|
||||
e @ (Expression::Integer(_) | Expression::Float(_)) => Ok(e),
|
||||
_ => Err("real number"),
|
||||
}
|
||||
}
|
||||
|
||||
fn ensure_imaginary_number(tok: TokenRef) -> GrammarResult<Expression> {
|
||||
match make_number(tok) {
|
||||
e @ Expression::Imaginary(_) => Ok(e),
|
||||
_ => Err("imaginary number"),
|
||||
}
|
||||
}
|
||||
|
||||
fn make_tuple_pattern<'a>(
|
||||
lpar: LeftParen<'a>,
|
||||
patterns: Vec<StarrableMatchSequenceElement<'a>>,
|
||||
rpar: RightParen<'a>,
|
||||
) -> MatchSequence<'a> {
|
||||
MatchSequence::MatchTuple(MatchTuple {
|
||||
patterns,
|
||||
lpar: vec![lpar],
|
||||
rpar: vec![rpar],
|
||||
})
|
||||
}
|
||||
|
||||
fn make_open_sequence_pattern<'a>(
|
||||
first: StarrableMatchSequenceElement<'a>,
|
||||
comma: Comma<'a>,
|
||||
mut rest: Vec<StarrableMatchSequenceElement<'a>>,
|
||||
) -> Vec<StarrableMatchSequenceElement<'a>> {
|
||||
rest.insert(0, first.with_comma(comma));
|
||||
rest
|
||||
}
|
||||
|
||||
fn make_match_sequence_element(value: MatchPattern) -> MatchSequenceElement {
|
||||
MatchSequenceElement {
|
||||
value,
|
||||
comma: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn make_match_star<'a>(star_tok: TokenRef<'a>, name: Option<Name<'a>>) -> MatchStar<'a> {
|
||||
MatchStar {
|
||||
name,
|
||||
comma: Default::default(),
|
||||
whitespace_before_name: Default::default(),
|
||||
star_tok,
|
||||
}
|
||||
}
|
||||
|
||||
fn make_match_mapping<'a>(
|
||||
lbrace: LeftCurlyBrace<'a>,
|
||||
mut elements: Vec<MatchMappingElement<'a>>,
|
||||
el_comma: Option<Comma<'a>>,
|
||||
star_tok: Option<TokenRef<'a>>,
|
||||
rest: Option<Name<'a>>,
|
||||
trailing_comma: Option<Comma<'a>>,
|
||||
rbrace: RightCurlyBrace<'a>,
|
||||
) -> MatchPattern<'a> {
|
||||
if let Some(c) = el_comma {
|
||||
if let Some(el) = elements.pop() {
|
||||
elements.push(el.with_comma(c));
|
||||
}
|
||||
// TODO: else raise error
|
||||
}
|
||||
MatchPattern::Mapping(MatchMapping {
|
||||
elements,
|
||||
rest,
|
||||
trailing_comma,
|
||||
lbrace,
|
||||
rbrace,
|
||||
lpar: Default::default(),
|
||||
rpar: Default::default(),
|
||||
whitespace_before_rest: Default::default(),
|
||||
star_tok,
|
||||
})
|
||||
}
|
||||
|
||||
fn make_match_mapping_element<'a>(
|
||||
key: Expression<'a>,
|
||||
colon_tok: TokenRef<'a>,
|
||||
pattern: MatchPattern<'a>,
|
||||
) -> MatchMappingElement<'a> {
|
||||
MatchMappingElement {
|
||||
key,
|
||||
pattern,
|
||||
comma: Default::default(),
|
||||
whitespace_before_colon: Default::default(),
|
||||
whitespace_after_colon: Default::default(),
|
||||
colon_tok,
|
||||
}
|
||||
}
|
||||
|
||||
fn make_class_pattern<'a>(
|
||||
cls: NameOrAttribute<'a>,
|
||||
lpar_tok: TokenRef<'a>,
|
||||
mut patterns: Vec<MatchSequenceElement<'a>>,
|
||||
pat_comma: Option<Comma<'a>>,
|
||||
mut kwds: Vec<MatchKeywordElement<'a>>,
|
||||
kwd_comma: Option<Comma<'a>>,
|
||||
rpar_tok: TokenRef<'a>,
|
||||
) -> MatchPattern<'a> {
|
||||
if let Some(c) = pat_comma {
|
||||
if let Some(el) = patterns.pop() {
|
||||
patterns.push(el.with_comma(c));
|
||||
}
|
||||
// TODO: else raise error
|
||||
}
|
||||
if let Some(c) = kwd_comma {
|
||||
if let Some(el) = kwds.pop() {
|
||||
kwds.push(el.with_comma(c));
|
||||
}
|
||||
// TODO: else raise error
|
||||
}
|
||||
MatchPattern::Class(MatchClass {
|
||||
cls,
|
||||
patterns,
|
||||
kwds,
|
||||
lpar: Default::default(),
|
||||
rpar: Default::default(),
|
||||
whitespace_after_cls: Default::default(),
|
||||
whitespace_before_patterns: Default::default(),
|
||||
whitespace_after_kwds: Default::default(),
|
||||
lpar_tok,
|
||||
rpar_tok,
|
||||
})
|
||||
}
|
||||
|
||||
fn make_match_keyword_element<'a>(
|
||||
key: Name<'a>,
|
||||
equal_tok: TokenRef<'a>,
|
||||
pattern: MatchPattern<'a>,
|
||||
) -> MatchKeywordElement<'a> {
|
||||
MatchKeywordElement {
|
||||
key,
|
||||
pattern,
|
||||
comma: Default::default(),
|
||||
whitespace_before_equal: Default::default(),
|
||||
whitespace_after_equal: Default::default(),
|
||||
equal_tok,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
39
native/libcst/tests/fixtures/malicious_match.py
vendored
Normal file
39
native/libcst/tests/fixtures/malicious_match.py
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
|
||||
# foo
|
||||
|
||||
match ( foo ) : #comment
|
||||
|
||||
# more comments
|
||||
case False : # comment
|
||||
|
||||
...
|
||||
case ( True ) : ...
|
||||
case _ : ...
|
||||
case ( _ ) : ... # foo
|
||||
|
||||
# bar
|
||||
|
||||
match x:
|
||||
case "StringMatchValue" : pass
|
||||
case [1, 2] : pass
|
||||
case [ 1 , * foo , * _ , ]: pass
|
||||
case [ [ _, ] , *_ ]: pass
|
||||
case {1: _, 2: _}: pass
|
||||
case { "foo" : bar , ** rest } : pass
|
||||
case { 1 : {**rest} , } : pass
|
||||
case Point2D(): pass
|
||||
case Cls ( 0 , ) : pass
|
||||
case Cls ( x=0, y = 2) :pass
|
||||
case Cls ( 0 , 1 , x = 0 , y = 2 ) : pass
|
||||
case [x] as y: pass
|
||||
case [x] as y : pass
|
||||
case (True)as x:pass
|
||||
case Foo:pass
|
||||
case (Foo):pass
|
||||
case ( Foo ) : pass
|
||||
case [ ( Foo ) , ]: pass
|
||||
case Foo|Bar|Baz : pass
|
||||
case Foo | Bar | ( Baz): pass
|
||||
case x,y , * more :pass
|
||||
case y.z: pass
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue