"""
Definitions of tree structures.
"""
from typing import Tuple, List, Mapping, Any
from penman.types import (Variable, Role)
from penman.epigraph import Epidata
# Tree types
Branch = Tuple[Role, Any, Epidata]
Node = Tuple[Variable, List[Branch]]
[docs]class Tree:
"""
A tree structure.
A tree is essentially a node that contains other nodes, but this
Tree class is useful to contain any metadata and to provide
tree-based methods.
"""
__slots__ = 'node', 'metadata'
def __init__(self,
node: Node,
metadata: Mapping[str, str] = None):
self.node = node
self.metadata = metadata or {}
def __eq__(self, other) -> bool:
if isinstance(other, Tree):
other = other.node
return self.node == other
def __repr__(self) -> str:
return 'Tree({!r})'.format(self.node)
def __str__(self) -> str:
return 'Tree(\n {})'.format(_format(self.node, 2))
[docs] def nodes(self):
"""
Return the nodes in the tree as a flat list.
"""
return _nodes(self.node)
def _format(node, level):
var, edges = node
next_level = level + 2
indent = '\n' + ' ' * next_level
edges = [_format_edge(edge, next_level) for edge in edges]
return '({!r}, [{}{}])'.format(var, indent, (',' + indent).join(edges))
def _format_edge(edge, level):
role, target, epidata = edge
if is_atomic(target):
target = repr(target)
else:
target = _format(target, level)
return '({!r}, {}, {!r})'.format(role, target, epidata)
def _nodes(node):
var, edges = node
ns = [] if var is None else [node]
for _, target, _ in edges:
# if target is not atomic, assume it's a valid tree node
if not is_atomic(target):
ns.extend(_nodes(target))
return ns
[docs]def is_atomic(x) -> bool:
"""
Return `True` if *x* is a valid atomic value.
"""
return x is None or isinstance(x, (str, int, float))