##
## BenchmarkFileParser_BLIF.py
##      - parser class for the BLIF format
##

# $Id: BenchmarkFileParser_BLIF.py,v 1.10 2007/01/18 22:20:59 jarda Exp $


# import of required modules {{{
import sys

from Plex import *

from BenchmarkFileParser import BenchmarkFileParser
from Constants import Constants
from ScannerConstants import ScannerConstants
# }}}


# define all() if necessary {{{
def __all_(iterable):
    # DOC {{{
    # }}}

    # CODE {{{
    for element in iterable:
        if not element:
            return False
    return True
    # }}}


if ((sys.version_info[0] <= 2) and (sys.version_info[1] <= 5)):
    all = __all_
# }}}


# class BenchmarkFileParser_BLIF() {{{
class BenchmarkFileParser_BLIF(BenchmarkFileParser):
    # DOC {{{
    """Parser class for the BLIF format.
    """
    # }}}


    # STATIC VARIABLES {{{
    # gate parsing states {{{
    GPS_EMPTY   = 0
    GPS_READING = 1
    GPS_END     = 2
    # }}}

    # input characters
    INPUT_CHARS = { '1' : 1, '0' : 1, '-' : 1, }

    # output characters
    OUTPUT_CHARS = ('1', '0')

    # gate types {{{
    GT_UNKNOWN  = 0
    GT_NOT      = 1
    GT_BUF      = 2
    GT_AND      = 3
    GT_OR       = 4
    GT_NAND     = 5
    GT_NOR      = 6
    # }}}

    # names of the gate types {{{
    GATE_TYPE_NAME  = {
        GT_UNKNOWN  : 'UNKNOWN',
        GT_NOT      : 'NOT',
        GT_BUF      : 'BUF',
        GT_AND      : 'AND',
        GT_OR       : 'OR',
        GT_NAND     : 'NAND',
        GT_NOR      : 'NOR',
    }
    # }}}

    # benchmark attribute by gate type {{{
    BENCHMARK_ATTRIBUTE_BY_GATE_TYPE = {
        GT_UNKNOWN : Constants.BA_GENERAL_GATES_COUNT,
        GT_NOT     : Constants.BA_NOT_COUNT,
        GT_BUF     : Constants.BA_BUF_COUNT,
        GT_AND     : Constants.BA_AND_COUNT,
        GT_OR      : Constants.BA_OR_COUNT,
        GT_NAND    : Constants.BA_NAND_COUNT,
        GT_NOR     : Constants.BA_NOR_COUNT,
    }
    # }}}
    # }}}


    # METHODS {{{

    def createLexicon(self):
        # DOC {{{
        """Creates lexicon for the parser.
        """
        # }}}

        # CODE {{{
        self.lexicon = Lexicon([
            (Str('#') + Rep(AnyBut('\n')), ScannerConstants.T_COMMENT,),
            (Str('.inputs'), ScannerConstants.T_INPUTS,),
            (Str('.outputs'), ScannerConstants.T_OUTPUTS,),
            (Str('.names'), ScannerConstants.T_NAMES,),
            ScannerConstants.R_NEWLINE,
            ScannerConstants.R_IGNORE_SPACES_AND_TABS,
            ScannerConstants.R_TOKEN,
        ])
        # }}}

    
    def getInitialAttributeValue_GENERAL_GATES_COUNT(self):
        # DOC {{{
        """Returns initial value for GENERAL GATES COUNT benchmark attribute.
        """
        # }}}

        # CODE {{{
        return 0
        # }}}


    def getInitialAttributeValue_BUF_GATES_COUNT(self):
        # DOC {{{
        """Returns initial value for BUF GATES COUNT benchmark attribute.
        """
        # }}}

        # CODE {{{
        return 0
        # }}}


    def getInitialAttributeValue_NOT_GATES_COUNT(self):
        # DOC {{{
        """Returns initial value for NOT GATES COUNT benchmark attribute.
        """
        # }}}

        # CODE {{{
        return 0
        # }}}


    def getInitialAttributeValue_OR_GATES_COUNT(self):
        # DOC {{{
        """Returns initial value for OR GATES COUNT benchmark attribute.
        """
        # }}}

        # CODE {{{
        return 0
        # }}}


    def getInitialAttributeValue_AND_GATES_COUNT(self):
        # DOC {{{
        """Returns initial value for AND GATES COUNT benchmark attribute.
        """
        # }}}

        # CODE {{{
        return 0
        # }}}


    def getInitialAttributeValue_NAND_GATES_COUNT(self):
        # DOC {{{
        """Returns initial value for NAND GATES COUNT benchmark attribute.
        """
        # }}}

        # CODE {{{
        return 0
        # }}}


    def getInitialAttributeValue_NOR_GATES_COUNT(self):
        # DOC {{{
        """Returns initial value for NOR GATES COUNT benchmark attribute.
        """
        # }}}

        # CODE {{{
        return 0
        # }}}


    def accumulateData(self, lineTokensIterator):
        # DOC {{{
        """Custom accumulation method for the format.

        Parameters

            lineTokensIterator -- the line tokens iterator
        """
        # }}}

        # CODE {{{
        # prepare methods that accumulate common attribute lines {{{
        methods = []
        
        for benchmarkAttributeId in self.registeredBenchmarkAttributes:
            attributeName = Constants.INTERNAL_BA_NAME[benchmarkAttributeId]
            methodName = Constants.ACCUMULATE_LINE_PREFIX + attributeName
            if (hasattr(self, methodName) and (getattr(self, methodName) is not None)):
                methods.append(self.createAccumulateLineClosure(benchmarkAttributeId, methodName))
        # }}}

        # initialize auxiliary variables
        state = BenchmarkFileParser_BLIF.GPS_EMPTY
        gateTokensLines = []


        # accumulate the data {{{
        for lineTokens in lineTokensIterator:
            # accumulate common (non-gate) benchmark attributes {{{
            for method in methods:
                method(lineTokens)
            # }}}

            # accumulation of PLA lines
            if (state == BenchmarkFileParser_BLIF.GPS_READING):
                if (self.isGateLine(lineTokens)):
                    gateTokensLines.append(lineTokens)
                else:
                    state = BenchmarkFileParser_BLIF.GPS_END
            else:
                if (lineTokens[0][0] == ScannerConstants.T_NAMES):
                    state = BenchmarkFileParser_BLIF.GPS_READING

            # accumulation of the gate value
            if (state == BenchmarkFileParser_BLIF.GPS_END):
                gateType = self.getGateType(gateTokensLines)

                self.attributeValues[self.BENCHMARK_ATTRIBUTE_BY_GATE_TYPE[gateType]] += 1

                del gateTokensLines[:]

                if (lineTokens[0][0] == ScannerConstants.T_NAMES):
                    state = BenchmarkFileParser_BLIF.GPS_READING
                else:
                    state = BenchmarkFileParser_BLIF.GPS_EMPTY
        # }}}
        # }}}


    def getGateType(self, gateTokensLines):
        # DOC {{{
        """Returns gate type using list of PLA-origin gate token lines.

        Parameters

            gateTokensLines -- the gate tokens lines list

            >>> parser = BenchmarkFileParser_BLIF()
            >>> parser.getGateType([[(0, '1',), (0, '1',),],]) == BenchmarkFileParser_BLIF.GT_BUF
            True
            >>> parser.getGateType([[(0, '1',), (0, '0',),],]) == BenchmarkFileParser_BLIF.GT_NOT
            True
            >>> parser.getGateType([
            ... [(0, '00',), (0, '0',),],
            ... [(0, '01',), (0, '0',),],
            ... [(0, '10',), (0, '0',),],
            ... [(0, '11',), (0, '1',),],
            ... ]) == BenchmarkFileParser_BLIF.GT_AND
            True
            >>> parser.getGateType([
            ... [(0, '00',), (0, '0',),],
            ... [(0, '01',), (0, '1',),],
            ... [(0, '10',), (0, '1',),],
            ... [(0, '11',), (0, '1',),],
            ... ]) == BenchmarkFileParser_BLIF.GT_OR
            True
            >>> parser.getGateType([
            ... [(0, '00',), (0, '1',),],
            ... [(0, '01',), (0, '0',),],
            ... [(0, '10',), (0, '0',),],
            ... [(0, '11',), (0, '0',),],
            ... ]) == BenchmarkFileParser_BLIF.GT_NOR
            True
            >>> parser.getGateType([
            ... [(0, '00',), (0, '1',),],
            ... [(0, '01',), (0, '1',),],
            ... [(0, '10',), (0, '1',),],
            ... [(0, '11',), (0, '0',),],
            ... ]) == BenchmarkFileParser_BLIF.GT_NAND
            True
        """
        # }}}

        # CODE {{{
        if (len(gateTokensLines) == 0):
            return BenchmarkFileParser_BLIF.GT_UNKNOWN

        if (len(gateTokensLines[0][0][1]) == 1):
            if (all((self.isGateLine_NOT(tokenLine) for tokenLine in gateTokensLines))):
                return BenchmarkFileParser_BLIF.GT_NOT
            else:
                return BenchmarkFileParser_BLIF.GT_BUF

        if (all((self.isGateLine_AND(tokenLine) for tokenLine in gateTokensLines))):
            return BenchmarkFileParser_BLIF.GT_AND

        if (all((self.isGateLine_OR(tokenLine) for tokenLine in gateTokensLines))):
            return BenchmarkFileParser_BLIF.GT_OR

        if (all((self.isGateLine_NOR(tokenLine) for tokenLine in gateTokensLines))):
            return BenchmarkFileParser_BLIF.GT_NOR

        if (all((self.isGateLine_NAND(tokenLine) for tokenLine in gateTokensLines))):
            return BenchmarkFileParser_BLIF.GT_NAND


        return BenchmarkFileParser_BLIF.GT_UNKNOWN
        # }}}


    def isGateLine_NOT(self, lineTokens):
        # DOC {{{
        """Tells whether the gate line can express NOT gate.

        Parameters

            lineTokens -- tokens from one line of the benchmark file
        """
        # }}}

        # CODE {{{
        return (((lineTokens[0][1] == '0') and (lineTokens[1][1] == '1')) or
                ((lineTokens[0][1] == '1') and (lineTokens[1][1] == '0')))
        # }}}


    def isGateLine_OR(self, lineTokens):
        # DOC {{{
        """Tells whether the gate line can express OR gate.

        Parameters

            lineTokens -- tokens from one line of the benchmark file
        """
        # }}}

        # CODE {{{
        return (((lineTokens[1][1] == '1') and ('1' in lineTokens[0][1])) or
                ((lineTokens[1][1] == '0') and ('1' not in lineTokens[0][1])))
        # }}}


    def isGateLine_AND(self, lineTokens):
        # DOC {{{
        """Tells whether the gate line can express AND gate.

        Parameters

            lineTokens -- tokens from one line of the benchmark file
        """
        # }}}

        # CODE {{{
        return (((lineTokens[1][1] == '1') and all(c == '1' for c in lineTokens[0][1])) or
                ((lineTokens[1][1] == '0') and ('0' in lineTokens[0][1])))
        # }}}


    def isGateLine_NOR(self, lineTokens):
        # DOC {{{
        """Tells whether the gate line can express NOR gate.

        Parameters

            lineTokens -- tokens from one line of the benchmark file
        """
        # }}}

        # CODE {{{
        return (((lineTokens[1][1] == '0') and ('1' in lineTokens[0][1])) or
                ((lineTokens[1][1] == '1') and all(c == '0' for c in lineTokens[0][1])))
        # }}}


    def isGateLine_NAND(self, lineTokens):
        # DOC {{{
        """Tells whether the gate line can express NAND gate.

        Parameters

            lineTokens -- tokens from one line of the benchmark file
        """
        # }}}

        # CODE {{{
        return (((lineTokens[1][1] == '0') and all(c == '1' for c in lineTokens[0][1])) or
                ((lineTokens[1][1] == '1') and ('0' in lineTokens[0][1])))
        # }}}


    def isGateLine(self, lineTokens):
        # DOC {{{
        """Tells whether the line can express a gate (whether it is a PLA line).

        Parameters

            lineTokens -- tokens from one line of the benchmark file
        """
        # }}}

        # CODE {{{
        if (len(lineTokens) != 2):
            return 0

        if (lineTokens[0][0] != ScannerConstants.T_TOKEN):
            return 0

        if (len([1 for c in lineTokens[0][1] if (c not in BenchmarkFileParser_BLIF.INPUT_CHARS)])):
            return 0

        if (len([1 for c in lineTokens[1][1] if (c not in BenchmarkFileParser_BLIF.OUTPUT_CHARS)])):
            return 0

        return 1
        # }}}

    
    def getInitialAttributeValue_INPUTS_COUNT(self):
        # DOC {{{
        """Returns initial value for INPUTS COUNT benchmark attribute.
        """
        # }}}

        # CODE {{{
        return 0
        # }}}

        
    def accumulateLine_INPUTS_COUNT(self, lineTokens, getValueMethod, setValueMethod):
        # DOC {{{
        """Accumulator method that updates value of INPUTS COUNT benchmark attribute.

        Parameters

            lineTokens -- tokens from one line of the benchmark file

            getValueMethod -- method that returns the current value of the
                benchmark attribute
                
            setValueMethod -- method that sets value of the benchmark attribute        
        """
        # }}}

        # CODE {{{
        if (lineTokens[0][0] == ScannerConstants.T_INPUTS):
            setValueMethod(getValueMethod() + len(lineTokens) - 1)
        # }}}

    
    def getInitialAttributeValue_OUTPUTS_COUNT(self):
        # DOC {{{
        """Returns initial value for OUTPUTS COUNT benchmark attribute.
        """
        # }}}

        # CODE {{{
        return 0
        # }}}

        
    def accumulateLine_OUTPUTS_COUNT(self, lineTokens, getValueMethod, setValueMethod):
        # DOC {{{
        """Accumulator method that updates value of OUTPUTS COUNT benchmark attribute.

        Parameters

            lineTokens -- tokens from one line of the benchmark file

            getValueMethod -- method that returns the current value of the
                benchmark attribute
                
            setValueMethod -- method that sets value of the benchmark attribute        
        """
        # }}}

        # CODE {{{
        if (lineTokens[0][0] == ScannerConstants.T_OUTPUTS):
            setValueMethod(getValueMethod() + len(lineTokens) - 1)
        # }}}


    # }}}
# }}}


def _test():
    # DOC {{{
    """Runs doctest on the module.
    """
    # }}}

    # CODE {{{
    import doctest
    doctest.testmod()
    # }}}


# when the script is the main script, run the test {{{
if __name__ == "__main__":
    _test()
# }}}
