Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Generic tree typing #967

Merged
merged 11 commits into from
Jan 21, 2022
39 changes: 30 additions & 9 deletions lark/visitors.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from typing import TypeVar, Tuple, List, Callable, Generic, Type, Union, Optional, cast

from abc import ABC
from functools import wraps

Expand All @@ -9,9 +10,14 @@

###{standalone
from inspect import getmembers, getmro

_Return_T = TypeVar('_Return_T')
_Return_V = TypeVar('_Return_V')
import sys
if sys.version_info >= (3, 8):
from typing import Protocol
else:
from typing_extensions import Protocol

_Return_T = TypeVar('_Return_T', covariant=True)
_Return_V = TypeVar('_Return_V', covariant=True)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quick question: Why is it Leaf_U but Return_V? Do the U and V stand for something?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The signature for the multiplication operator has a lot of moving pieces. It starts with a ITransformer[_Leaf_T, Tree[_Leaf_U]] instance (a transformer that converts a Tree[_Leaf_T] to Tree[_Leaf_U]). Then a ITransformer[_Leaf_U, _Return_V] instance (a transformer that converts a Tree[_Leaf_U] to _Return_V). Which results in a ITransformer[_Leaf_T, _Return_V] (a transformer that converts a Tree[_Leaf_T] to _Return_V).

There isn't a technical reason why it needs to be named with _Return_V instead of _Return_U. However, I was thinking that using _Return_V would make it clearer that the final type isn't necessarily related to the intermediary _Leaf_U. I'm open to switching to _Leaf_U or putting some of my above description into a comment in the source code if you think the current state should be changed.

_Leaf_T = TypeVar('_Leaf_T')
_Leaf_U = TypeVar('_Leaf_U')
_R = TypeVar('_R')
Expand Down Expand Up @@ -55,6 +61,21 @@ def __class_getitem__(cls, _):
return cls


class ITransformer(Protocol[_Leaf_T, _Return_T]):
"""Base interface for classes that can transform a Tree"""

def transform(self, tree: Tree[_Leaf_T]) -> _Return_T:
"""Transform the given tree, and return the final result"""
pass

def __mul__(
self: 'ITransformer[_Leaf_T, Tree[_Leaf_U]]',
other: 'ITransformer[_Leaf_U, _Return_V]'
) -> 'ITransformer[_Leaf_T, _Return_V]':
"""Chain two transformers together, returning a new transformer."""
pass


class Transformer(_Decoratable, ABC, Generic[_Leaf_T, _Return_T]):
"""Transformers visit each node of the tree, and run the appropriate method on it according to the node's data.

Expand Down Expand Up @@ -140,8 +161,8 @@ def transform(self, tree: Tree[_Leaf_T]) -> _Return_T:

def __mul__(
self: 'Transformer[_Leaf_T, Tree[_Leaf_U]]',
other: 'Union[Transformer[_Leaf_U, _Return_V], TransformerChain[_Leaf_U, _Return_V,]]'
) -> 'TransformerChain[_Leaf_T, _Return_V]':
other: ITransformer[_Leaf_U, _Return_V]
) -> ITransformer[_Leaf_T, _Return_V]:
"""Chain two transformers together, returning a new transformer.
"""
return TransformerChain(self, other)
Expand All @@ -163,9 +184,9 @@ def __default_token__(self, token):

class TransformerChain(Generic[_Leaf_T, _Return_T]):

transformers: 'Tuple[Union[Transformer, TransformerChain], ...]'
transformers: Tuple[ITransformer, ...]

def __init__(self, *transformers: 'Union[Transformer, TransformerChain]') -> None:
def __init__(self, *transformers: ITransformer) -> None:
self.transformers = transformers

def transform(self, tree: Tree[_Leaf_T]) -> _Return_T:
Expand All @@ -175,8 +196,8 @@ def transform(self, tree: Tree[_Leaf_T]) -> _Return_T:

def __mul__(
self: 'TransformerChain[_Leaf_T, Tree[_Leaf_U]]',
other: 'Union[Transformer[_Leaf_U, _Return_V], TransformerChain[_Leaf_U, _Return_V]]'
) -> 'TransformerChain[_Leaf_T, _Return_V]':
other: ITransformer[_Leaf_U, _Return_V]
) -> ITransformer[_Leaf_T, _Return_V]:
return TransformerChain(*self.transformers + (other,))


Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
packages = ['lark', 'lark.parsers', 'lark.tools', 'lark.grammars', 'lark.__pyinstaller'],

requires = [],
install_requires = [],
install_requires = ['typing_extensions >= 3.7, < 5.0; python_version < "3.8"'],

extras_require = {
"regex": ["regex"],
Expand Down
3 changes: 2 additions & 1 deletion test-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
Js2Py==0.68
regex
regex
typing_extensions >= 3.7, < 5.0; python_version < "3.8"