### Navigation

- index
- modules |
- FAdo 1.2 documentation »
- Module code »

# Source code for common

    # -*- coding: utf-8 -*-
    """**Common definitions for FAdo files**

    .. *Authors:* Rogério Reis & Nelma Moreira

    .. *This is part of FAdo project*   http://fado.dcc.fc.up.pt.

    .. *Copyright:* 1999-2014 Rogério Reis & Nelma Moreira {rvr,nam}@dcc.fc.up.pt

    .. *Contributions by:*
       - Marco Almeida
       - Hugo Gouveia

    .. This program is free software; you can redistribute it and/or
       modify it under the terms of the GNU General Public License as published
       by the Free Software Foundation; either version 2 of the License, or
       (at your option) any later version.

       This program is distributed in the hope that it will be useful,
       but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
       or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       for more details.

       You should have received a copy of the GNU General Public License along
       with this program; if not, write to the Free Software Foundation, Inc.,
       675 Mass Ave, Cambridge, MA 02139, USA."""

    import os
    import random
    from copy import deepcopy
    from abc import abstractmethod
    import functools
    import tempfile
    import subprocess

    FAdoVersion = "1.2"

    class fnhException(Exception):
        pass

    class NImplemented(fnhException):
        pass

    class NonPlanar(fnhException):
        pass

    class VertexNotInGraph(fnhException):
        pass

    class FAException(fnhException):
        pass

    class DFAerror(fnhException):
        pass

    class TFASignal(fnhException):
        pass

    class PDAerror(fnhException):
        pass

    class CFGerror(fnhException):
        pass

    class FAdoError(Exception):
        pass

    class FAdoGeneralError(FAdoError):
        def __init__(self, msg):
            self.msg = msg

        def __str__(self):
            return "FAdo: " + self.msg

    class VersoError(FAdoGeneralError):
        pass

    class TFAAccept(TFASignal):
        pass

    class TFAReject(TFASignal):
        pass

    class TFARejectLoop(TFAReject):
        pass

    class TFARejectBlocked(TFAReject):
        pass

    class TFARejectNonFinal(TFAReject):
        pass

    class CFGgrammarError(CFGerror):
        def __init__(self, rule):
            self.rule = rule

        def __str__(self):
            return "Error in rule %s" % self.rule

    class CFGterminalError(CFGerror):
        def __init__(self, size):
            self.size = size

        def __str__(self):
            return "To many alphabetic symbols: %s" % self.size

    class DFAnoInitial(DFAerror):
        def __str__(self):
            return "No initial state defined"

    class DuplicateName(DFAerror):
        def __init__(self, number):
            self.number = number

        def __str__(self):
            return "State  number %s repeated" % self.number

    class FAdoSyntacticError(FAdoError):
        pass

    class DFASyntaticError(DFAerror):
        def __init__(self, line):
            self.line = line

    class DFAstateUnknown(DFAerror):
        def __init__(self, stidx):
            self.stidx = stidx

        def __str__(self):
            return "State  %s unknown" % self.stidx

    class DFAnotNFA(DFAerror):
        def __init__(self, msg):
            self.message = msg

        def __str__(self):
            return "Not a DFA %s" % self.message

    class DFAepsilonRedefinition(DFAerror):
        pass

    class DFAsymbolUnknown(DFAerror):
        def __init__(self, sym):
            self.symbol = sym

        def __str__(self):
            return "Symbol %s is unknown" % self.symbol

    class GraphError(FAdoGeneralError):
        def __str__(self):
            return "Graph Error"

    class DFAstopped(DFAerror):
        pass

    class DFAFileError(DFAerror):
        def __init(self, name):
            self.filename = name

        def __str__(self):
            return "Error in file: %s" % self.filename

    class DFAFound(DFAerror):
        def __init__(self, word):
            self.word = word[:]

        def __str__(self):
            return "Found: $s" % self.word

    class DFAEmptyDFA(DFAerror):
        def __str__(self):
            return "Dfa is empty"

    class DFAequivalent(DFAerror):
        def __str__(self):
            return "Dfa are equivalent"

    class DFAnotComplete(DFAerror):
        def __str__(self):
            return "Dfa is not complete"

    class DFAnotMinimal(DFAerror):
        def __str__(self):
            return "Dfa is not minimal"

    class DFAinputError(DFAerror):
        def __init__(self, word):
            self.word = word

        def __str__(self):
            return "Input error: %s" % self.word

    class DFAdifferentSigma(DFAerror):
        def __str__(self):
            return "Dfas with different alphabets"

    class DFAEmptySigma(DFAerror):
        def __str__(self):
            return "Dfa alphabet is empty"

    class DFAmarkedError(DFAerror):
        def __init__(self, sym):
            self.sym = sym

        def __str__(self):
            return "Symbol not marked %s" % str(self.sym)

    class TRError(FAException):
        def __str__(self):
            return "Transducer Error"

    class regexpInvalid(DFAerror):
        def __init__(self, word):
            self.word = word
            self.message = 'Error in regexp %s' % word

        def __str__(self):
            return "%s" % self.message

    class regexpInvalidSymbols(DFAerror):
        def __init__(self):
            self.message = 'Symbols in regexp do not match alphabet'

        def __str__(self):
            return "%s" % self.message

    class notAcyclic(DFAerror):
        def __str__(self):
            return "Automaton is not acyclic "

    class IllegalBias(FAdoGeneralError):
        def __str__(self):
            return "Bias with illegal value "

    class CodesError(FAdoGeneralError):
        pass

    class CodingTheoryError(Exception):
        def __init__(self, msg):
            self.msg = msg

        def __str__(self):
            return "FAdo: coding theory error. Message: " + self.msg

    class PropertyNotSatisfied(CodesError):
        def __str__(self):
            return "Property not satisfied"

    class GraphError(fnhException):
        def __init__(self, message):
            self.message = message

        def __str__(self):
            return "%s" % self.message

    class TestsError(DFAerror):
        def __init__(self, message):
            return "%s" % self.message

    class PDAsymbolUnknown(PDAerror):
        def __init__(self, symb):
            self.symb = symb

        def __str__(self):
            return "Unknown stack symbol %s" % self.symb

    class NotSP(DFAerror):
        def __str__(self):
            return "DFA is not Serial-Paralel."

    EmptySet = "@empty_set"
    Epsilon = "@epsilon"
    DeadName = "@DeaD"

    DEBUG = False

    TYPE_DISJ = "disj"
    TYPE_CONC = "conc"
    TYPE_STAR = "star"
    TYPE_SYMB = "sym"
    TYPE_EWRD = "ewrd"
    TYPE_ESET = "eset"

    def debug(string, level=0):
        print "%s%s" % ("".join(["\t" for _ in xrange(level)]), string)

    class SPLabel(object):
        """Label class for Serial-Paralel test algorithm

        .. seealso::
            Moreira & Reis, Fundamenta Informatica, 'Series-Paralel automata and short regular expressions',  n.91 3-4,
            pag 611-629"""

        def __init__(self, val=None):
            if not val:
                val = []
            self.value = val

        def __repr__(self):
            if type(self.value) is type(lbl()):
                return 'spl: ref %s' % self.lastref()
            else:
                return 'spl: val %s' % str(self.value)

        def val(self):
            if type(self.value) is type(lbl()):
                return self.value.val()
            else:
                return self.value

        def ref(self):
            return lbl(self)

        def assign(self, val):
            self.lastref().value = val

        def lastref(self):
            if type(self.value) is type(lbl()):
                return self.value.lastref()
            else:
                return self

        def copy(self):
            return lbl(deepcopy(self.val()))

    class lbl(object):
        def __init__(self, val=None):
            if not val:
                val = []
            self.value = val

        def __repr__(self):
            if type(self.value) is type(lbl()):
                return 'lbl: ref %s' % self.lastref()
            else:
                return 'lbl: val %s' % str(self.value)

        def val(self):
            if type(self.value) is type(lbl()):
                return self.value.val()
            else:
                return self.value

        def ref(self):
            return lbl(self)

        def assign(self, val):
            self.lastref().value = val

        def lastref(self):
            if type(self.value) is type(lbl()):
                return self.value.lastref()
            else:
                return self

        def copy(self):
            return lbl(deepcopy(self.val()))

    class memoized(object):
        """Decorator that caches a function's return value each time it is called.

        If called later with the same arguments, the cached value is returned, and not re-evaluated."""
        def __init__(self, func):
            self.func = func
            self.cache = {}

        def __call__(self, *args):
            try:
                return self.cache[args]
            except KeyError:
                value = self.func(*args)
                self.cache[args] = value
                return value
            except TypeError:
                # uncachable -- for instance, passing a list as an argument.
                # Better to not cache than to blow up entirely.
                return self.func(*args)

        def __repr__(self):
            """Return the function's docstring."""
            return self.func.__doc__

        # noinspection PyUnusedLocal
        def __get__(self, obj, objtype):
            """Support instance methods."""
            return functools.partial(self.__call__, obj)

    def memoize(cls, method_name):
        """Memoizes a given method result on instances of given class.

        Given method should have no side effects. Results are stored as instance attributes --- given parameters are
        disregarded.

        :param cls:
        :param method_name:

        .. note: original method is stored as <cls>.memoize_<method_name>_original

        .. note: values are stored as <instance>.memoized_<method_name>

        .. attention: all instances in all threads will be affected"""
        saved_name = "memoize_" + method_name + "_original"
        if hasattr(cls, saved_name):
            return False
        attr_name = "memoized_" + method_name
        method = getattr(cls, method_name)
        setattr(cls, saved_name, method)
        if not hasattr(cls, "memoized_instances"):
            cls.memoized_instances = {}
        inst_list = []
        cls.memoized_instances[method_name] = inst_list

        def memo(self, *param):
            try:
                return getattr(self, attr_name)
            except AttributeError:
                value = method(self, *param)
                setattr(self, attr_name, value)
                inst_list.append(self)
                return value

        memo.__name__ = method_name
        setattr(cls, method_name, memo)
        return True

    def dememoize(cls, method_name):
        """Restore method of given class from memoized state. Stored attributes will be removed."""
        saved_name = "memoize_" + method_name + "_original"
        if not hasattr(cls, saved_name):
            return False
        method = getattr(cls, saved_name)
        delattr(cls, saved_name)
        setattr(cls, method_name, method)
        for instance in cls.memoized_instances[method_name]:
            delattr(instance, "memoized_" + method_name)
        del cls.memoized_instances[method_name]
        if not cls.memoized_instances:
            del cls.memoized_instances
        return True

    try:
        from itertools import product as cartesianProduct
    except ImportError:
        def cartesianProduct(x, y):
            return [(a, b) for a in x for b in y]

    def uSet(s):
        """returns the first element of a set

        :param set s: the set
        :return: the first element of s
        :rtype: x"""
        return list(s)[0]

    def tmpFileName():
        i = os.getpid()
        r = random.randint(0, 1000000)
        return "/var/tmp/F%d-%d" % (i, r)

    def forceIterable(x):
        """Forces a non iterable object into a list, otherwise returns itself

        :param list x: the object
        :return: object as a list
        :rtype: list"""
        if not getattr(x, '__iter__', False):
            return list([x])
        else:
            return x

    def binomial(n,k):
        """ Exactly what it seems

        :param int n: n
        :param int k: k
        :rtype: int"""
        return reduce(lambda acc, m: acc * (n - k + m) / m, range(1, k + 1), 1)

    def delFromList(l, l1):
        """Delete every element of l1 from l

        :type l: list
        :type l1:list

        .. versionadded: 0.9.8"""
        for i in l1:
            l.remove(i)

    def suffixes(word):
        """Returns the list of propper suffixes of a word

        :param word: the word
        :type word: str
        :rtype: list

        .. versionadded: 0.9.8"""
        return [word[i:] for i in range(1, len(word))]

    def graphvizTranslate(s):
        """Translate epsilons for graphviz

        :param s: symbol
        :type s: str
        :rtype: str"""
        if s == Epsilon:
            return "&epsilon;"
        else:
            return s

    import warnings

    def deprecated(func):
        """This is a decorator which can be used to mark functions as deprecated. It will result in a warning being emmitted
        when the function is used."""
        def newFunc(*args, **kwargs):
            warnings.warn("Call to deprecated function %s." % func.__name__,
                          category=DeprecationWarning)
            return func(*args, **kwargs)
        newFunc.__name__ = func.__name__
        newFunc.__doc__ = func.__doc__
        newFunc.__dict__.update(func.__dict__)
        return newFunc

    class Drawable(object):
        """Any FAdo object that is drawable"""
        def display(self, fileName=None, size="30,20"):
            """ Display automata using dot

            :param size: size of representation
            :param fileName: filename to use for the graphic representation (default a os tmpfile
            :return: name of the file created

            .. versionchanged:: 1.0.4"""
            if fileName is not None:
                fnameGV = fileName + ".gv"
                fnamePDF = fileName + ".pdf"
            else:
                f = tempfile.NamedTemporaryFile(suffix=".gv")
                f.close()
                fnameGV = f.name
                fname, _ = os.path.splitext(fnameGV)
                fnamePDF = fname + ".pdf"
            foo = open(fnameGV, "w")
            foo.write(self.dotFormat(size))
            foo.close()
            callstr = "dot -Tpdf %s -o %s" % (fnameGV, fnamePDF)
            result = subprocess.call(callstr, shell=True)
            if result:
                print "Need graphviz to visualize objects"
                return
            #os.system("dot -Tpdf %s -o %s" % (fnameGV, fnamePDF))
            if os.name == 'nt':
                os.system("start %s" % fnamePDF)
            else:
                os.system("open %s" % fnamePDF)
            return fnamePDF

        def makePNG(self, fileName=None, size="30,20"):
            """ Produce png file to display

            :param str fileName: file name, if None will be a tmpfile
            :param size: size for graphviz
            :return: name of the file created

            .. versionadded:: 1.0.4"""
            if fileName is not None:
                fnameGV = fileName + ".gv"
                fnamePNG = fileName + ".png"
            else:
                f = tempfile.NamedTemporaryFile(suffix=".gv")
                f.close()
                fnameGV = f.name
                fname, _ = os.path.splitext(fnameGV)
                fnamePNG = fname + ".png"
            foo = open(fnameGV, "w")
            foo.write(self.dotFormat(size))
            foo.close()
            callstr = "dot -Tpng %s -o %s" % (fnameGV, fnamePNG)
            result = subprocess.call(callstr, shell=True)
            if result:
                print "Need graphviz to visualize objects"
                return
            return fnamePNG

        @abstractmethod
        def dotFormat(self, size):
            """Some dot representation

            :param size: size parameter for dotviz
            :type size: str
            :rtype: str"""
            pass

    class Word(object):
        """Class to implement generic words as iterables with pretty-print

        Basically a unified way to deal with words with caracters of of sizes different of one with no much fuss"""
        def __init__(self, it=[]):
            self.Sigma = set()
            self.data = []
            self.simple = True
            for c in it:
                self.append(c)

        def __repr__(self):
            return "Word:%s" % self.__str__()

        def __str__(self):
            if self.simple:
                return "".join(self.data)
            else:
                return str(self.data)

        def __len__(self):
            return len(self.data)

        def __contains__(self, item):
            return item in self.data

        def __getitem__(self, item):
            return self.data[item]

        def append(self, value):
            if len(value) != 1:
                self.simple = False
            self.Sigma.add(value)
            self.data.append(value)

        def dup(self):
            return deepcopy(self)

        def __add__(self, other):
            if not isinstance(other, Word):
                raise FAdoSyntacticError()
            else:
                new = self.dup()
                for c in other:
                    new.append(c)
                return new

### Navigation

- index
- modules |
- FAdo 1.2 documentation »
- Module code »