Source code for ggame.logic

# gglogic boolean logic device simulations for ggmath
"""
These digital logic classes are experimental.
"""

from ggame.mathapp import MathApp, _MathDynamic
from abc import ABCMeta, abstractmethod


# decorator for _getvalue or any value handler that may experience recursion
def _recursiontrap(handler):
    def trapmagic(self):
        if not self.ingetvalue:
            self.ingetvalue = True
            self.lastget = handler(self)
            self.ingetvalue = False
            return self.lastget
        else:
            self.ingetvalue = False
            return self.lastget
            
    return trapmagic 


class _BoolDevice(_MathDynamic, metaclass=ABCMeta):

    def __init__(self, mininputqty, *args, **kwargs):
        """
        Required Inputs
        
        * **mininputqty** minimum number of inputs possible
        
        Optional keyword arguments
        
        * **namedinputs** list of input names
        """
        self.In = [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


    @property
    def In(self):
        return self._input
    
    @In.setter
    def In(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):
        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
    
    def _inputState(self, value):
        """
        interprets a value that could be single input or a list of inputs!
        """
        try:
            inputs = [].extend(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
        elif zeros > 0:
            return False
        else: 
            return None
    
    def __call__(self):
        if self.Enable:
            return self._getvalue()
        else:
            return None
    
    def GetInput(self, inputname):
        return self._inputState(self._indict[inputname])

    def SetInput(self, inputname, reference):
        self._indict[inputname] = self.Eval(reference)
    


class _BoolOneInput(_BoolDevice):

    def __init__(self, *args, **kwargs):
        """ 
        No Required Inputs
        """
        super().__init__(1, *args, **kwargs)

    
class _BoolMultiInput(_BoolDevice):

    def __init__(self, *args, **kwargs):
        """ 
        No Required Inputs
        """
        super().__init__(2, *args, **kwargs)


[docs]class BoolNOT(_BoolOneInput): @_recursiontrap def _getvalue(self): inval = self._inputState(self.In[0]) if inval == None: return True # equivalent to an "open" input else: return not inval
[docs]class BoolAND(_BoolMultiInput): @_recursiontrap def _getvalue(self): for v in self._input: if not self._inputState(v): return False return True
[docs]class BoolNOR(_BoolMultiInput): @_recursiontrap def _getvalue(self): for v in self._input: if self._inputState(v): return False return True
[docs]class BoolNAND(_BoolMultiInput): @_recursiontrap def _getvalue(self): for v in self._input: if not self._inputState(v): return True return False
class BoolSRFF(_BoolOneInput): def __init__(self, *args, **kwargs): """ SR Flip Flop Optional keyword arguments * **gateclass** one of BoolNAND or BoolNOR (default) """ 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() def Q_(self): return self.IC2() def Q(self): return self._getvalue() ''' This must be re-implemented to prevent recursion class BoolJKFF(_BoolOneInput): def __init__(self, *args, **kwargs): """ JK Flip Flop Optional keyword arguments """ self.InputNames = ['J','K','CLK'] kwargs['namedinputs'] = self.InputNames super().__init__(*args, **kwargs) self.IC1 = BoolNAND() self.IC2 = BoolNAND() self.ICJ = BoolNAND() self.ICK = BoolNAND() self.IC1.In = self.ICJ, self.IC2 self.IC2.In = self.ICK, self.IC1 # we can only assign ICJ, ICK inputs when this device's inputs are set def SetInput(self, inputname, reference): super().SetInput(inputname, reference) for i in self.InputNames: if self._indict[i]() is None: return self.ICJ.In = self.IC2, self._indict['J'], self._indict['CLK'] self.ICK.In = self.IC1, self._indict['K'], self._indict['CLK'] def _getvalue(self): return self.IC1() def Q_(self): return self.IC2() def Q(self): return self._getvalue() ''' """ class TestDevice(_BoolOneInput): def __init__(self, *args, **kwargs): kwargs['namedinputs'] = ['in1', 'in2'] super().__init__(*args, **kwargs) def out1(self): return self.GetInput('in1') and self.GetInput('in2') def out2(self): return self.GetInput('in1') or self.GetInput('in2') def _getvalue(self): return None """