madduck's git repository
Every one of the projects in this repository is available at the canonical
URL git://git.madduck.net/madduck/pub/<projectpath> — see
each project's metadata for the exact URL.
All patches and comments are welcome. Please squash your changes to logical
commits before using git-format-patch and git-send-email to
patches@git.madduck.net.
If you'd read over the Git project's submission guidelines and adhered to them,
I'd be especially grateful.
SSH access, as well as push access can be individually
arranged.
If you use my repositories frequently, consider adding the following
snippet to ~/.gitconfig and using the third clone URL listed for each
project:
[url "git://git.madduck.net/madduck/"]
insteadOf = madduck:
There's also a pattern matching implementation here.
"""
There's also a pattern matching implementation here.
"""
-# mypy: allow-untyped-defs
+# mypy: allow-untyped-defs, allow-incomplete-defs
from typing import (
Any,
from typing import (
Any,
Iterator,
List,
Optional,
Iterator,
List,
Optional,
HUGE: int = 0x7FFFFFFF # maximum repeat count, default max
HUGE: int = 0x7FFFFFFF # maximum repeat count, default max
-_type_reprs: Dict[int, Union[Text, int]] = {}
+_type_reprs: Dict[int, Union[str, int]] = {}
-def type_repr(type_num: int) -> Union[Text, int]:
+def type_repr(type_num: int) -> Union[str, int]:
global _type_reprs
if not _type_reprs:
from .pygram import python_symbols
global _type_reprs
if not _type_reprs:
from .pygram import python_symbols
_P = TypeVar("_P", bound="Base")
NL = Union["Node", "Leaf"]
_P = TypeVar("_P", bound="Base")
NL = Union["Node", "Leaf"]
-Context = Tuple[Text, Tuple[int, int]]
-RawNode = Tuple[int, Optional[Text], Optional[Context], Optional[List[NL]]]
+Context = Tuple[str, Tuple[int, int]]
+RawNode = Tuple[int, Optional[str], Optional[Context], Optional[List[NL]]]
"""
Abstract base class for Node and Leaf.
"""
Abstract base class for Node and Leaf.
return self._eq(other)
@property
return self._eq(other)
@property
- def prefix(self) -> Text:
+ def prefix(self) -> str:
raise NotImplementedError
def _eq(self: _P, other: _P) -> bool:
raise NotImplementedError
def _eq(self: _P, other: _P) -> bool:
return 0
return 1 + self.parent.depth()
return 0
return 1 + self.parent.depth()
- def get_suffix(self) -> Text:
+ def get_suffix(self) -> str:
"""
Return the string immediately following the invocant node. This is
effectively equivalent to node.next_sibling.prefix
"""
Return the string immediately following the invocant node. This is
effectively equivalent to node.next_sibling.prefix
"""Concrete implementation for interior nodes."""
fixers_applied: Optional[List[Any]]
"""Concrete implementation for interior nodes."""
fixers_applied: Optional[List[Any]]
- used_names: Optional[Set[Text]]
+ used_names: Optional[Set[str]]
def __init__(
self,
type: int,
children: List[NL],
context: Optional[Any] = None,
def __init__(
self,
type: int,
children: List[NL],
context: Optional[Any] = None,
- prefix: Optional[Text] = None,
+ prefix: Optional[str] = None,
fixers_applied: Optional[List[Any]] = None,
) -> None:
"""
fixers_applied: Optional[List[Any]] = None,
) -> None:
"""
else:
self.fixers_applied = None
else:
self.fixers_applied = None
- def __repr__(self) -> Text:
+ def __repr__(self) -> str:
"""Return a canonical string representation."""
assert self.type is not None
"""Return a canonical string representation."""
assert self.type is not None
- return "%s(%s, %r)" % (
+ return "{}({}, {!r})".format(
self.__class__.__name__,
type_repr(self.type),
self.children,
)
self.__class__.__name__,
type_repr(self.type),
self.children,
)
- def __str__(self) -> Text:
+ def __str__(self) -> str:
"""
Return a pretty string representation.
"""
Return a pretty string representation.
"""
return "".join(map(str, self.children))
"""
return "".join(map(str, self.children))
- def _eq(self, other) -> bool:
+ def _eq(self, other: Base) -> bool:
"""Compare two nodes for equality."""
return (self.type, self.children) == (other.type, other.children)
"""Compare two nodes for equality."""
return (self.type, self.children) == (other.type, other.children)
yield from child.pre_order()
@property
yield from child.pre_order()
@property
- def prefix(self) -> Text:
+ def prefix(self) -> str:
"""
The whitespace and comments preceding this node in the input.
"""
"""
The whitespace and comments preceding this node in the input.
"""
return self.children[0].prefix
@prefix.setter
return self.children[0].prefix
@prefix.setter
- def prefix(self, prefix) -> None:
+ def prefix(self, prefix: str) -> None:
if self.children:
self.children[0].prefix = prefix
if self.children:
self.children[0].prefix = prefix
"""Concrete implementation for leaf nodes."""
# Default values for instance variables
"""Concrete implementation for leaf nodes."""
# Default values for instance variables
fixers_applied: List[Any]
bracket_depth: int
fixers_applied: List[Any]
bracket_depth: int
- opening_bracket: "Leaf"
- used_names: Optional[Set[Text]]
+ # Changed later in brackets.py
+ opening_bracket: Optional["Leaf"] = None
+ used_names: Optional[Set[str]]
_prefix = "" # Whitespace and comments preceding this token in the input
lineno: int = 0 # Line where this token starts in the input
column: int = 0 # Column where this token starts in the input
_prefix = "" # Whitespace and comments preceding this token in the input
lineno: int = 0 # Line where this token starts in the input
column: int = 0 # Column where this token starts in the input
+ # If not None, this Leaf is created by converting a block of fmt off/skip
+ # code, and `fmt_pass_converted_first_leaf` points to the first Leaf in the
+ # converted code.
+ fmt_pass_converted_first_leaf: Optional["Leaf"] = None
def __init__(
self,
type: int,
def __init__(
self,
type: int,
context: Optional[Context] = None,
context: Optional[Context] = None,
- prefix: Optional[Text] = None,
+ prefix: Optional[str] = None,
fixers_applied: List[Any] = [],
fixers_applied: List[Any] = [],
+ opening_bracket: Optional["Leaf"] = None,
+ fmt_pass_converted_first_leaf: Optional["Leaf"] = None,
) -> None:
"""
Initializer.
) -> None:
"""
Initializer.
self._prefix = prefix
self.fixers_applied: Optional[List[Any]] = fixers_applied[:]
self.children = []
self._prefix = prefix
self.fixers_applied: Optional[List[Any]] = fixers_applied[:]
self.children = []
+ self.opening_bracket = opening_bracket
+ self.fmt_pass_converted_first_leaf = fmt_pass_converted_first_leaf
def __repr__(self) -> str:
"""Return a canonical string representation."""
from .pgen2.token import tok_name
assert self.type is not None
def __repr__(self) -> str:
"""Return a canonical string representation."""
from .pgen2.token import tok_name
assert self.type is not None
- return "%s(%s, %r)" % (
+ return "{}({}, {!r})".format(
self.__class__.__name__,
tok_name.get(self.type, self.type),
self.value,
)
self.__class__.__name__,
tok_name.get(self.type, self.type),
self.value,
)
- def __str__(self) -> Text:
+ def __str__(self) -> str:
"""
Return a pretty string representation.
"""
Return a pretty string representation.
"""
return self._prefix + str(self.value)
"""
return self._prefix + str(self.value)
- def _eq(self, other) -> bool:
+ def _eq(self, other: "Leaf") -> bool:
"""Compare two nodes for equality."""
return (self.type, self.value) == (other.type, other.value)
"""Compare two nodes for equality."""
return (self.type, self.value) == (other.type, other.value)
- def prefix(self) -> Text:
+ def prefix(self) -> str:
"""
The whitespace and comments preceding this token in the input.
"""
return self._prefix
@prefix.setter
"""
The whitespace and comments preceding this token in the input.
"""
return self._prefix
@prefix.setter
- def prefix(self, prefix) -> None:
+ def prefix(self, prefix: str) -> None:
self.changed()
self._prefix = prefix
self.changed()
self._prefix = prefix
return Leaf(type, value or "", context=context)
return Leaf(type, value or "", context=context)
-_Results = Dict[Text, NL]
+_Results = Dict[str, NL]
-class BasePattern(object):
"""
A pattern is a tree matching pattern.
"""
A pattern is a tree matching pattern.
type: Optional[int]
type = None # Node type (token if < 256, symbol if >= 256)
content: Any = None # Optional content matching pattern
type: Optional[int]
type = None # Node type (token if < 256, symbol if >= 256)
content: Any = None # Optional content matching pattern
- name: Optional[Text] = None # Optional name used to store match in results dict
+ name: Optional[str] = None # Optional name used to store match in results dict
def __new__(cls, *args, **kwds):
"""Constructor that prevents BasePattern from being instantiated."""
assert cls is not BasePattern, "Cannot instantiate BasePattern"
return object.__new__(cls)
def __new__(cls, *args, **kwds):
"""Constructor that prevents BasePattern from being instantiated."""
assert cls is not BasePattern, "Cannot instantiate BasePattern"
return object.__new__(cls)
- def __repr__(self) -> Text:
+ def __repr__(self) -> str:
assert self.type is not None
args = [type_repr(self.type), self.content, self.name]
while args and args[-1] is None:
del args[-1]
assert self.type is not None
args = [type_repr(self.type), self.content, self.name]
while args and args[-1] is None:
del args[-1]
- return "%s(%s)" % (self.__class__.__name__, ", ".join(map(repr, args)))
+ return "{}({})".format(self.__class__.__name__, ", ".join(map(repr, args)))
def _submatch(self, node, results=None) -> bool:
raise NotImplementedError
def _submatch(self, node, results=None) -> bool:
raise NotImplementedError
def __init__(
self,
type: Optional[int] = None,
def __init__(
self,
type: Optional[int] = None,
- content: Optional[Text] = None,
- name: Optional[Text] = None,
+ content: Optional[str] = None,
+ name: Optional[str] = None,
) -> None:
"""
Initializer. Takes optional type, content, and name.
) -> None:
"""
Initializer. Takes optional type, content, and name.
self.content = content
self.name = name
self.content = content
self.name = name
- def match(self, node: NL, results=None):
+ def match(self, node: NL, results=None) -> bool:
"""Override match() to insist on a leaf node."""
if not isinstance(node, Leaf):
return False
"""Override match() to insist on a leaf node."""
if not isinstance(node, Leaf):
return False
def __init__(
self,
type: Optional[int] = None,
def __init__(
self,
type: Optional[int] = None,
- content: Optional[Iterable[Text]] = None,
- name: Optional[Text] = None,
+ content: Optional[Iterable[str]] = None,
+ name: Optional[str] = None,
) -> None:
"""
Initializer. Takes optional type, content, and name.
) -> None:
"""
Initializer. Takes optional type, content, and name.
if isinstance(item, WildcardPattern): # type: ignore[unreachable]
self.wildcards = True # type: ignore[unreachable]
self.type = type
if isinstance(item, WildcardPattern): # type: ignore[unreachable]
self.wildcards = True # type: ignore[unreachable]
self.type = type
- self.content = newcontent
+ self.content = newcontent # TODO: this is unbound when content is None
self.name = name
def _submatch(self, node, results=None) -> bool:
self.name = name
def _submatch(self, node, results=None) -> bool:
- content: Optional[Text] = None,
+ content: Optional[str] = None,
min: int = 0,
max: int = HUGE,
min: int = 0,
max: int = HUGE,
- name: Optional[Text] = None,
+ name: Optional[str] = None,
) -> None:
"""
Initializer.
) -> None:
"""
Initializer.
class NegatedPattern(BasePattern):
class NegatedPattern(BasePattern):
- def __init__(self, content: Optional[Any] = None) -> None:
+ def __init__(self, content: Optional[BasePattern] = None) -> None:
# We only match an empty sequence of nodes in its entirety
return len(nodes) == 0
# We only match an empty sequence of nodes in its entirety
return len(nodes) == 0
- def generate_matches(self, nodes) -> Iterator[Tuple[int, _Results]]:
+ def generate_matches(self, nodes: List[NL]) -> Iterator[Tuple[int, _Results]]:
if self.content is None:
# Return a match if there is an empty sequence
if len(nodes) == 0:
if self.content is None:
# Return a match if there is an empty sequence
if len(nodes) == 0: