Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ spellbind is a reactive programming library that lets you create Variables that
## Installation

```bash
git clone https://github.com/FancyNeuron/spellbind.git
cd spellbind
pip install -e .
pip install spellbind
```

## Quick Start
Expand Down
117 changes: 86 additions & 31 deletions src/spellbind/bool_values.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import operator
from abc import ABC
from typing import TypeVar, Generic, overload, TYPE_CHECKING, TypeAlias
from spellbind.values import Value, OneToOneValue, Constant, SimpleVariable, TwoToOneValue, SelectValue
from typing import TypeVar, Generic, overload, TYPE_CHECKING, TypeAlias, Callable, Iterable

from spellbind.values import Value, OneToOneValue, Constant, SimpleVariable, TwoToOneValue, \
ManyToSameValue, ThreeToOneValue

if TYPE_CHECKING:
from spellbind.float_values import FloatValue # pragma: no cover
Expand All @@ -16,34 +18,45 @@
BoolValueLike: TypeAlias = 'BoolValue | bool'

_S = TypeVar('_S')
_T = TypeVar('_T')
_U = TypeVar('_U')
_V = TypeVar('_V')


BoolLike = bool | Value[bool]
IntLike = int | Value[int]
FloatLike = float | Value[float]
StrLike = str | Value[str]


def _select_function(b: bool, t: _S, f: _S) -> _S:
if b:
return t
return f


class BoolValue(Value[bool], ABC):
@property
def logical_not(self) -> BoolValue:
return NotBoolValue(self)

def __and__(self, other: BoolLike) -> BoolValue:
return AndBoolValues(self, other)
return BoolValue.derive_from_many(all, self, other, is_associative=True)

def __rand__(self, other: bool) -> BoolValue:
return AndBoolValues(other, self)
return BoolValue.derive_from_many(all, other, self, is_associative=True)

def __or__(self, other: BoolLike) -> BoolValue:
return OrBoolValues(self, other)
return BoolValue.derive_from_many(any, self, other, is_associative=True)

def __ror__(self, other: bool) -> BoolValue:
return OrBoolValues(other, self)
return BoolValue.derive_from_many(any, other, self, is_associative=True)

def __xor__(self, other: BoolLike) -> BoolValue:
return XorBoolValues(self, other)
return BoolValue.derive_from_two(operator.xor, self, other)

def __rxor__(self, other: bool) -> BoolValue:
return XorBoolValues(other, self)
return BoolValue.derive_from_two(operator.xor, other, self)

@overload
def select(self, if_true: IntValueLike, if_false: IntValueLike) -> IntValue: ...
Expand All @@ -61,20 +74,50 @@ def select(self, if_true: BoolValue, if_false: BoolValue) -> BoolValue: ...
def select(self, if_true: Value[_S] | _S, if_false: Value[_S] | _S) -> Value[_S]: ...

def select(self, if_true, if_false):
from spellbind.float_values import FloatValue, SelectFloatValue
from spellbind.int_values import IntValue, SelectIntValue
from spellbind.str_values import StrValue, SelectStrValue
from spellbind.float_values import FloatValue
from spellbind.int_values import IntValue
from spellbind.str_values import StrValue

if isinstance(if_true, (FloatValue, float)) and isinstance(if_false, (FloatValue, float)):
return SelectFloatValue(self, if_true, if_false)
return FloatValue.derive_from_three(_select_function, self, if_true, if_false)
elif isinstance(if_true, (StrValue, str)) and isinstance(if_false, (StrValue, str)):
return SelectStrValue(self, if_true, if_false)
return StrValue.derive_from_three(_select_function, self, if_true, if_false)
elif isinstance(if_true, (BoolValue, bool)) and isinstance(if_false, (BoolValue, bool)):
return SelectBoolValue(self, if_true, if_false)
return BoolValue.derive_from_three(_select_function, self, if_true, if_false)
elif isinstance(if_true, (IntValue, int)) and isinstance(if_false, (IntValue, int)):
return SelectIntValue(self, if_true, if_false)
return IntValue.derive_from_three(_select_function, self, if_true, if_false)
else:
return SelectValue(self, if_true, if_false)
return Value.derive_three_value(_select_function, self, if_true, if_false)

@classmethod
def derive_from_two(cls, transformer: Callable[[bool, bool], bool],
first: BoolLike, second: BoolLike) -> BoolValue:
return Value.derive_from_two_with_factory(
transformer,
first, second,
create_value=TwoToBoolValue.create,
create_constant=BoolConstant.of,
)

@classmethod
def derive_from_three(cls, transformer: Callable[[_S, _T, _U], bool],
first: _S | Value[_S], second: _T | Value[_T], third: _U | Value[_U]) -> BoolValue:
return Value.derive_from_three_with_factory(
transformer,
first, second, third,
create_value=ThreeToBoolValue.create,
create_constant=BoolConstant.of,
)

@classmethod
def derive_from_many(cls, transformer: Callable[[Iterable[bool]], bool], *values: BoolLike, is_associative: bool = False) -> BoolValue:
return Value.derive_from_many_with_factory(
transformer,
*values,
create_value=ManyBoolToBoolValue.create,
create_constant=BoolConstant.of,
is_associative=is_associative,
)


class OneToBoolValue(OneToOneValue[_S, bool], BoolValue, Generic[_S]):
Expand All @@ -86,32 +129,44 @@ def __init__(self, value: Value[bool]):
super().__init__(operator.not_, value)


class AndBoolValues(TwoToOneValue[bool, bool, bool], BoolValue):
def __init__(self, left: BoolLike, right: BoolLike):
super().__init__(operator.and_, left, right)
class ManyBoolToBoolValue(ManyToSameValue[bool], BoolValue):
@staticmethod
def create(transformer: Callable[[Iterable[bool]], bool], values: Iterable[BoolLike]) -> BoolValue:
return ManyBoolToBoolValue(transformer, *values)


class OrBoolValues(TwoToOneValue[bool, bool, bool], BoolValue):
def __init__(self, left: BoolLike, right: BoolLike):
super().__init__(operator.or_, left, right)
class BoolConstant(BoolValue, Constant[bool]):
@classmethod
def of(cls, value: bool) -> BoolConstant:
if value:
return TRUE
return FALSE

@property
def logical_not(self) -> BoolConstant:
return BoolConstant.of(not self.value)

class XorBoolValues(TwoToOneValue[bool, bool, bool], BoolValue):
def __init__(self, left: BoolLike, right: BoolLike):
super().__init__(operator.xor, left, right)
@property
def constant_value_or_raise(self) -> bool:
return self.value


class BoolConstant(BoolValue, Constant[bool]):
class BoolVariable(SimpleVariable[bool], BoolValue):
pass


class BoolVariable(SimpleVariable[bool], BoolValue):
pass
class ThreeToBoolValue(ThreeToOneValue[_S, _T, _U, bool], BoolValue):
@staticmethod
def create(transformer: Callable[[_S, _T, _U], bool],
first: _S | Value[_S], second: _T | Value[_T], third: _U | Value[_U]) -> BoolValue:
return ThreeToBoolValue(transformer, first, second, third)


class SelectBoolValue(SelectValue[bool], BoolValue):
def __init__(self, condition: BoolLike, if_true: BoolLike, if_false: BoolLike):
super().__init__(condition, if_true, if_false)
class TwoToBoolValue(TwoToOneValue[_S, _T, bool], BoolValue):
@staticmethod
def create(transformer: Callable[[_S, _T], bool],
first: _S | Value[_S], second: _T | Value[_T]) -> BoolValue:
return TwoToBoolValue(transformer, first, second)


TRUE = BoolConstant(True)
Expand Down
Loading