from __future__ import annotations
from abc import ABCMeta
from collections.abc import Callable
from typing import Any, Self
from . import TooManyChildren, TooFewChildren
from .abc import LogicGroup, LogicNode, ActionNode, LGM, NO_CONDITION
from .expression import ContextLogicExpression as _CLE, AttrExpression as _AE, MathExpression as _ME, ComparisonExpression as _CE, LogicalExpression as _LE
__all__ = ['NoAction', 'LongAction', 'ShortAction', 'RootLogicNode', 'ContextLogicExpression', 'AttrExpression', 'MathExpression', 'ComparisonExpression', 'LogicalExpression']
[docs]
class NoAction(ActionNode):
[docs]
def __init__(self, auto_connect: bool = True):
super().__init__(
repr='<NoAction>',
auto_connect=auto_connect
)
self.sig = 0
[docs]
def eval(self, enforce_dtype: bool = False) -> ActionNode:
return self
[docs]
class LongAction(ActionNode):
[docs]
def __init__(self, sig: int = 1, auto_connect: bool = True):
super().__init__(
repr=f'<LongAction>(sig = {sig})',
auto_connect=auto_connect
)
self.sig = sig
[docs]
def eval(self, enforce_dtype: bool = False) -> ActionNode:
return self
[docs]
class ShortAction(ActionNode):
[docs]
def __init__(self, sig: int = -1, auto_connect: bool = True):
super().__init__(
repr=f'<ShortAction>(sig = {sig})',
auto_connect=auto_connect
)
self.sig = sig
[docs]
def eval(self, enforce_dtype: bool = False) -> ActionNode:
return self
[docs]
class RootLogicNode(LogicNode):
[docs]
def __init__(self):
super().__init__(
expression=True,
repr=f'Entry Point'
)
def _entry_check(self):
return True
def _on_enter(self):
# pre-shelve call
LGM.enter_expression(node=self)
state = LGM.shelve()
state['inspection_mode'] = LGM.inspection_mode
LGM.inspection_mode = True
# post-shelve call
LGM._active_nodes.append(self)
def _on_exit(self):
# pre-unshelve call
# LGM.exit_expression(node=self)
state = LGM.unshelve()
# post-unshelve call
LGM.exit_expression(node=self)
LGM.inspection_mode = state['inspection_mode']
[docs]
def append(self, expression: Self, edge_condition: Any = None):
if self.nodes:
raise TooManyChildren()
super().append(expression=expression, edge_condition=NO_CONDITION)
[docs]
def eval_recursively(self, **kwargs):
return self.child.eval_recursively(**kwargs)
[docs]
def to_html(self, with_group=True, dry_run=True, filename="decision_graph.html", **kwargs):
return self.child.to_html(with_group=with_group, dry_run=dry_run, filename=filename, **kwargs)
@property
def child(self) -> LogicNode:
if self.nodes:
return self.last_node
raise TooFewChildren()
[docs]
class ContextLogicExpression(_CLE, LogicNode, metaclass=ABCMeta):
[docs]
def __init__(self, expression: float | int | bool | Exception | Callable[[], Any], dtype: type = None, repr: str = None, logic_group: LogicGroup = None):
super().__init__(expression=expression, dtype=dtype, repr=repr, logic_group=logic_group)
LogicNode.__init__(self=self, expression=expression, dtype=dtype, repr=repr)
# magic method to invoke AttrExpression
def __getitem__(self, key: str) -> AttrExpression:
return AttrExpression(attr=key, logic_group=self.logic_group)
def __getattr__(self, key: str) -> AttrExpression:
return AttrExpression(attr=key, logic_group=self.logic_group)
# math operation to invoke MathExpression
def __add__(self, other: int | float | bool | Self) -> Self:
return MathExpression(left=self, op=MathExpression.Operator.add, right=other, logic_group=self.logic_group)
def __sub__(self, other: int | float | bool | Self) -> Self:
return MathExpression(left=self, op=MathExpression.Operator.sub, right=other, logic_group=self.logic_group)
def __mul__(self, other: int | float | bool | Self) -> Self:
return MathExpression(left=self, op=MathExpression.Operator.mul, right=other, logic_group=self.logic_group)
def __truediv__(self, other: int | float | bool | Self) -> Self:
return MathExpression(left=self, op=MathExpression.Operator.truediv, right=other, logic_group=self.logic_group)
def __floordiv__(self, other: int | float | bool | Self) -> Self:
return MathExpression(left=self, op=MathExpression.Operator.floordiv, right=other, logic_group=self.logic_group)
def __pow__(self, other: int | float | bool | Self) -> Self:
return MathExpression(left=self, op=MathExpression.Operator.pow, right=other, logic_group=self.logic_group)
def __neg__(self):
return MathExpression(left=self, op=MathExpression.Operator.neg, repr=f'-{self.repr}', logic_group=self.logic_group)
# Comparison operation to invoke ComparisonExpression
def __eq__(self, other: int | float | bool | str | Self) -> Self:
return ComparisonExpression(left=self, op=ComparisonExpression.Operator.eq, right=other, logic_group=self.logic_group)
def __ne__(self, other: int | float | bool | str | Self) -> Self:
return ComparisonExpression(left=self, op=ComparisonExpression.Operator.ne, right=other, logic_group=self.logic_group)
def __gt__(self, other: int | float | bool | Self) -> Self:
return ComparisonExpression(left=self, op=ComparisonExpression.Operator.gt, right=other, logic_group=self.logic_group)
def __ge__(self, other: int | float | bool | Self) -> Self:
return ComparisonExpression(left=self, op=ComparisonExpression.Operator.ge, right=other, logic_group=self.logic_group)
def __lt__(self, other: int | float | bool | Self) -> Self:
return ComparisonExpression(left=self, op=ComparisonExpression.Operator.lt, right=other, logic_group=self.logic_group)
def __le__(self, other: int | float | bool | Self) -> Self:
return ComparisonExpression(left=self, op=ComparisonExpression.Operator.le, right=other, logic_group=self.logic_group)
# Logical operation to invoke LogicalExpression
def __and__(self, other: int | float | bool | Self) -> Self:
return LogicalExpression(left=self, op=LogicalExpression.Operator.and_, right=other, logic_group=self.logic_group)
def __or__(self, other: Self | bool) -> Self:
return LogicalExpression(left=self, op=LogicalExpression.Operator.or_, right=other, logic_group=self.logic_group)
def __invert__(self) -> Self:
return LogicalExpression(left=self, op=LogicalExpression.Operator.not_, repr=f'~{self.repr}', logic_group=self.logic_group)
[docs]
class AttrExpression(_AE, ContextLogicExpression):
[docs]
def __init__(self, attr: str | int, dtype: type = None, repr: str = None, logic_group: LogicGroup = None, edge_condition: Any = True):
super().__init__(attr=attr, repr=repr, logic_group=logic_group)
ContextLogicExpression.__init__(self=self, expression=self._eval, dtype=self.dtype, repr=self.repr, logic_group=self.logic_group)
[docs]
class MathExpression(_ME, ContextLogicExpression):
[docs]
def __init__(self, left: ContextLogicExpression | int | float, op: _ME.Operator, right: ContextLogicExpression | int | float = None, dtype: type = None, repr: str = None, logic_group: LogicGroup = None, edge_condition: Any = True):
super().__init__(left=left, op=op, right=right, dtype=dtype, repr=repr, logic_group=logic_group)
ContextLogicExpression.__init__(self=self, expression=self._eval, dtype=self.dtype, repr=self.repr, logic_group=self.logic_group)
[docs]
class ComparisonExpression(_CE, ContextLogicExpression):
[docs]
def __init__(self, left: ContextLogicExpression | int | float, op: _CE.Operator, right: ContextLogicExpression | int | float = None, dtype: type = None, repr: str = None, logic_group: LogicGroup = None, edge_condition: Any = True):
super().__init__(left=left, op=op, right=right, dtype=dtype, repr=repr, logic_group=logic_group)
ContextLogicExpression.__init__(self=self, expression=self._eval, dtype=self.dtype, repr=self.repr, logic_group=self.logic_group)
[docs]
class LogicalExpression(_LE, ContextLogicExpression):
[docs]
def __init__(self, left: ContextLogicExpression | int | float, op: _LE.Operator, right: ContextLogicExpression | int | float = None, dtype: type = None, repr: str = None, logic_group: LogicGroup = None, edge_condition: Any = True):
super().__init__(left=left, op=op, right=right, dtype=dtype, repr=repr, logic_group=logic_group)
ContextLogicExpression.__init__(self=self, expression=self._eval, dtype=self.dtype, repr=self.repr, logic_group=self.logic_group)