"""ResultTree class."""
from typing import Dict, List, Optional, Tuple, Union
from text_lint.exceptions.results import SplitGroupNotFound
# A tree's value can be either:
# - A single captured value from a regex
# - A list created by splitting a captured value from a regex
AliasSingleAndSplitCaptures = Tuple[Union[str, List[str]], ...]
AliasTreeValue = Union[str, List[str]]
AliasRecursiveTreeResult = Dict[
"AliasTreeValue",
Union["AliasTreeValue", List["AliasRecursiveTreeResult"]],
]
[Doku]
class ResultTree:
"""Nested parsing results represented as tree."""
[Doku]
def __init__(self, value: "AliasTreeValue") -> None:
"""Initialize ResultTree instances.
:param value: The root value of the new ResultTree.
"""
self.value = value
self.children: List["ResultTree"] = []
[Doku]
@classmethod
def create(cls, value: "AliasTreeValue") -> "ResultTree":
"""Create a new ResultTree instance.
:param value: The root value of the new ResultTree.
:returns: The created tree.
"""
return cls(value=value)
def __key(self) -> Tuple[str]:
return (str(self.value),)
[Doku]
def __hash__(self) -> int:
"""Implement hashing for ResultTree instances.
:returns: The hashed value of this tree.
"""
return hash(self.__key())
[Doku]
def __eq__(self, other: object) -> bool:
"""Implement the equality operator for ResultTree instances.
:param other: The other object to compare to this ResultTree instance.
:returns: A boolean for whether this tree is equal to the specified tree.
:raises: NotImplemented
"""
if isinstance(other, ResultTree):
return self.value == other.value
return NotImplemented
[Doku]
def add_matches(
self,
capture_groups: AliasSingleAndSplitCaptures,
splits: Dict[int, Optional[str]],
index: int = 0,
) -> None:
"""Add a set of capture groups to the tree.
:param capture_groups: A tuple of values captured from assertion regexes.
:param splits: A dictionary of splits to perform on the captured values.
:param index: The index of the current captured value being processed.
"""
for split in splits.keys():
if split > len(capture_groups):
raise SplitGroupNotFound(split)
self._add_matches(
capture_groups,
splits,
index,
)
def _add_matches(
self,
capture_groups: AliasSingleAndSplitCaptures,
splits: Dict[int, Optional[str]],
index: int,
) -> None:
if index < len(capture_groups):
if index + 1 in splits:
indexed_capture = capture_groups[index]
# Assertion: Capture groups should not be split twice
assert isinstance(indexed_capture, str)
split_capture = indexed_capture.split(splits[index + 1])
capture_groups = (
capture_groups[:index] + (split_capture,) +
capture_groups[index + 1:]
)
self._add_matches_recursive(capture_groups, splits, index)
def _add_matches_recursive(
self,
capture_groups: AliasSingleAndSplitCaptures,
splits: Dict[int, Optional[str]],
index: int,
) -> None:
nested_result = self.create(value=capture_groups[index])
self.children.append(nested_result)
# pylint: disable=protected-access
nested_result._add_matches(capture_groups, splits, index + 1)
[Doku]
def clone(self) -> "ResultTree":
"""Generate a separate copy of this tree.
:returns: The cloned tree instance.
"""
clone = ResultTree.create(self.value)
clone.children = [child.clone() for child in self.children]
return clone
[Doku]
def representation(self) -> "AliasRecursiveTreeResult":
"""Return a dictionary representation of the nested tree structure.
:returns: A dictionary representation of this tree.
"""
return {
"value": self.value,
"capture": [result.representation() for result in self.children]
}