-
-
Notifications
You must be signed in to change notification settings - Fork 414
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Generic tree typing #967
Generic tree typing #967
Changes from 2 commits
b1ee70c
9d1dcd4
9c844c9
862fb79
f6b39d9
f6e1abe
8a8b70d
acf2a26
732143a
1646426
c9c0140
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,10 +7,10 @@ | |
import sys | ||
from copy import deepcopy | ||
|
||
from typing import List, Callable, Iterator, Union, Optional, TYPE_CHECKING | ||
from typing import List, Callable, Iterator, Union, Optional, Generic, TypeVar, Any, TYPE_CHECKING | ||
|
||
if TYPE_CHECKING: | ||
from .lexer import TerminalDef | ||
from .lexer import TerminalDef, Token | ||
if sys.version_info >= (3, 8): | ||
from typing import Literal | ||
else: | ||
|
@@ -35,7 +35,10 @@ def __init__(self): | |
self.empty = True | ||
|
||
|
||
class Tree(object): | ||
_Leaf_T = TypeVar("_Leaf_T") | ||
|
||
|
||
class Tree(Generic[_Leaf_T]): | ||
erezsh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"""The main tree class. | ||
|
||
Creates a new tree, and stores "data" and "children" in attributes of the same name. | ||
|
@@ -49,9 +52,9 @@ class Tree(object): | |
""" | ||
|
||
data: str | ||
children: 'List[Union[str, Tree]]' | ||
children: 'List[Union[_Leaf_T, Tree[_Leaf_T]]]' | ||
|
||
def __init__(self, data: str, children: 'List[Union[str, Tree]]', meta: Optional[Meta]=None) -> None: | ||
def __init__(self, data: str, children: 'List[Union[_Leaf_T, Tree[_Leaf_T]]]', meta: Optional[Meta]=None) -> None: | ||
self.data = data | ||
self.children = children | ||
self._meta = meta | ||
|
@@ -100,7 +103,7 @@ def __ne__(self, other): | |
def __hash__(self) -> int: | ||
return hash((self.data, tuple(self.children))) | ||
|
||
def iter_subtrees(self) -> 'Iterator[Tree]': | ||
def iter_subtrees(self) -> 'Iterator[Tree[_Leaf_T]]': | ||
"""Depth-first iteration. | ||
|
||
Iterates over all the subtrees, never returning to the same node twice (Lark's parse-tree is actually a DAG). | ||
|
@@ -109,17 +112,18 @@ def iter_subtrees(self) -> 'Iterator[Tree]': | |
subtrees = OrderedDict() | ||
for subtree in queue: | ||
subtrees[id(subtree)] = subtree | ||
queue += [c for c in reversed(subtree.children) | ||
# Reason for type ignore https://github.com/python/mypy/issues/10999 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With the current type system it isn't possible to convey that " |
||
queue += [c for c in reversed(subtree.children) # type: ignore | ||
if isinstance(c, Tree) and id(c) not in subtrees] | ||
|
||
del queue | ||
return reversed(list(subtrees.values())) | ||
|
||
def find_pred(self, pred: 'Callable[[Tree], bool]') -> 'Iterator[Tree]': | ||
def find_pred(self, pred: 'Callable[[Tree[_Leaf_T]], bool]') -> 'Iterator[Tree[_Leaf_T]]': | ||
"""Returns all nodes of the tree that evaluate pred(node) as true.""" | ||
return filter(pred, self.iter_subtrees()) | ||
|
||
def find_data(self, data: str) -> 'Iterator[Tree]': | ||
def find_data(self, data: str) -> 'Iterator[Tree[_Leaf_T]]': | ||
"""Returns all nodes of the tree whose data equals the given data.""" | ||
return self.find_pred(lambda t: t.data == data) | ||
|
||
|
@@ -131,7 +135,7 @@ def expand_kids_by_index(self, *indices: int) -> None: | |
kid = self.children[i] | ||
self.children[i:i+1] = kid.children | ||
|
||
def scan_values(self, pred: 'Callable[[Union[str, Tree]], bool]') -> Iterator[str]: | ||
def scan_values(self, pred: 'Callable[[Union[_Leaf_T, Tree[_Leaf_T]]], bool]') -> Iterator[_Leaf_T]: | ||
"""Return all values in the tree that evaluate pred(value) as true. | ||
|
||
This can be used to find all the tokens in the tree. | ||
|
@@ -164,30 +168,32 @@ def iter_subtrees_topdown(self): | |
def __deepcopy__(self, memo): | ||
return type(self)(self.data, deepcopy(self.children, memo), meta=self._meta) | ||
|
||
def copy(self) -> 'Tree': | ||
def copy(self) -> 'Tree[_Leaf_T]': | ||
return type(self)(self.data, self.children) | ||
|
||
def set(self, data: str, children: 'List[Union[str, Tree]]') -> None: | ||
def set(self, data: str, children: 'List[Union[_Leaf_T, Tree[_Leaf_T]]]') -> None: | ||
self.data = data | ||
self.children = children | ||
|
||
|
||
ParseTree = Tree['Token'] | ||
|
||
|
||
class SlottedTree(Tree): | ||
__slots__ = 'data', 'children', 'rule', '_meta' | ||
|
||
|
||
def pydot__tree_to_png(tree: Tree, filename: str, rankdir: 'Literal["TB", "LR", "BT", "RL"]'="LR", **kwargs) -> None: | ||
def pydot__tree_to_png(tree: Tree[Any], filename: str, rankdir: 'Literal["TB", "LR", "BT", "RL"]'="LR", **kwargs) -> None: | ||
plannigan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
graph = pydot__tree_to_graph(tree, rankdir, **kwargs) | ||
graph.write_png(filename) | ||
|
||
|
||
def pydot__tree_to_dot(tree, filename, rankdir="LR", **kwargs): | ||
def pydot__tree_to_dot(tree: Tree[Any], filename, rankdir="LR", **kwargs): | ||
graph = pydot__tree_to_graph(tree, rankdir, **kwargs) | ||
graph.write(filename) | ||
|
||
|
||
def pydot__tree_to_graph(tree, rankdir="LR", **kwargs): | ||
def pydot__tree_to_graph(tree: Tree[Any], rankdir="LR", **kwargs): | ||
"""Creates a colorful image that represents the tree (data+children, without meta) | ||
|
||
Possible values for `rankdir` are "TB", "LR", "BT", "RL", corresponding to | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you change this to
'Tree[Token]'
, there is no need for aParseTree
variable.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you talking about it not being needed in this instance or not being needed in general?
ParseTree
when type checking.ParseTree
isn't strictly required at all in general. However, I think it is a nice ease of use to a have a specific name for trees returned byparse()
since it is a core type that users of the library will be interacting with a lot.