Source code for ggame.logic
"""
These MathApp-based digital logic classes are experimental.
"""
from abc import ABCMeta, abstractmethod
from ggame.mathapp import _MathDynamic
# decorator for _getvalue or any value handler that may experience recursion
def _recursiontrap(handler):
def trapmagic(self):
"""
An attempt to catch recursion (doesn't work).
"""
if not self.ingetvalue:
self.ingetvalue = True
self.lastget = handler(self)
self.ingetvalue = False
return self.lastget
self.ingetvalue = False
return self.lastget
return trapmagic
class _BoolDevice(_MathDynamic, metaclass=ABCMeta):
"""
Base class for boolean objects.
:Required Arguments:
:param int mininputqty: The minimum number of inputs possible.
:Optional Keyword Arguments:
* **namedinputs** (*list[str]*) List of input names.
"""
def __init__(self, mininputqty, **kwargs):
self.inp = [None] * mininputqty
self.enable = True
namedinputs = kwargs.get("namedinputs", [])
self._indict = {name: self.eval(None) for name in namedinputs}
self.ingetvalue = False
self.lastget = False
self.resetval = False
self.firsttime = True
self._enable = None
self._input = None
super().__init__()
@property
def inp(self):
"""
Report the list of input references.
"""
return self._input
@inp.setter
def inp(self, val):
try:
self._input = [self.eval(v) for v in list(val)]
except TypeError:
self._input = [self.eval(val)]
# Enable attribute controls the "tri-state" of output
@property
def enable(self):
"""
Report the enable state of the object.
"""
return self._enable
@enable.setter
def enable(self, val):
self._enable = self.eval(val)
@abstractmethod
@_recursiontrap # MUST use with any implementation that may recurse!
def _getvalue(self):
return None
@staticmethod
def _inputState(value):
"""
interprets a value that could be single input or a list of inputs!
"""
try:
inputs = [] + value
except TypeError:
inputs = [value]
scalars = [v() for v in inputs]
ones = scalars.count(True) + scalars.count(1)
zeros = scalars.count(False) + scalars.count(0)
if ones > 0 and zeros > 0:
raise ValueError("Conflicting inputs found")
if ones > 0:
return True
if zeros > 0:
return False
return None
def __call__(self):
if self.enable:
return self._getvalue()
return None
def getinput(self, inputname):
"""
Retrieve input by name.
:param str inputname: Name to look up.
"""
return self._inputState(self._indict[inputname])
def setinput(self, inputname, reference):
"""
Set an input connection.
:param str inputname: Name to assign.
:param function reference: Callable object or function connected to input.
"""
self._indict[inputname] = self.eval(reference)
class _BoolOneInput(_BoolDevice):
"""
Base class for one-input boolean objects. No required inputs.
"""
def __init__(self, *args, **kwargs):
super().__init__(1, *args, **kwargs)
@abstractmethod
@_recursiontrap # MUST use with any implementation that may recurse!
def _getvalue(self):
return None
class _BoolMultiInput(_BoolDevice):
"""
Base class for multiple-input boolean objects. No required inputs.
"""
def __init__(self, *args, **kwargs):
super().__init__(2, *args, **kwargs)
@abstractmethod
@_recursiontrap # MUST use with any implementation that may recurse!
def _getvalue(self):
return None
[docs]
class BoolNOT(_BoolOneInput):
"""
Logical NOT boolean gate.
"""
@_recursiontrap
def _getvalue(self):
inval = self._inputState(self.inp[0])
if inval is None:
return True # equivalent to an "open" input
return not inval
[docs]
class BoolAND(_BoolMultiInput):
"""
Logical AND boolean gate. Multiple inputs.
"""
@_recursiontrap
def _getvalue(self):
for v in self._input:
if not self._inputState(v):
return False
return True
[docs]
class BoolNOR(_BoolMultiInput):
"""
Logical NOR boolean gate. Multiple inputs.
"""
@_recursiontrap
def _getvalue(self):
for v in self._input:
if self._inputState(v):
return False
return True
[docs]
class BoolNAND(_BoolMultiInput):
"""
Logical NAND boolean gate. Multiple inputs.
"""
@_recursiontrap
def _getvalue(self):
for v in self._input:
if not self._inputState(v):
return True
return False
class BoolSRFF(_BoolOneInput):
"""
Logical Set/Reset (SR) FlipFlop.
:Optional Keyword Arguments:
:param class gateclass: One of BoolNAND or BoolNOR (default).
"""
def __init__(self, *args, **kwargs):
kwargs["namedinputs"] = ["R", "S"]
super().__init__(*args, **kwargs)
gate = kwargs.get("gateclass", BoolNOR)
self.ic1 = gate()
self.ic2 = gate()
# we can only assign IC1 and IC2 inputs when this device's inputs are set
def setinput(self, inputname, reference):
super().setinput(inputname, reference)
if inputname == "R":
self.ic1.In = reference, self.ic2
elif inputname == "S":
self.ic2.In = reference, self.ic1
def _getvalue(self):
return self.ic1()
# pylint: disable=invalid-name
def q_(self):
"""
Report value of Q_ output.
"""
return self.ic2()
# pylint: disable=invalid-name
def q(self):
"""
Report value of Q output.
"""
return self._getvalue()