All Downloads are FREE. Search and download functionalities are using the official Maven repository.

Lib.Krakatau.ssa.exceptionset.py Maven / Gradle / Ivy

There is a newer version: 1.1
Show newest version
import collections, itertools
from . import objtypes
from .mixin import ValueType

class CatchSetManager(object):
    def __init__(self, env, chpairs, attributes=None):
        if attributes is not None:
            self.env, self.sets, self.mask = attributes
        else:
            self.env = env
            self.sets = collections.OrderedDict() #make this ordered since OnException relies on it

            sofar = empty = ExceptionSet.EMPTY
            for catchtype, handler in chpairs:
                old = self.sets.get(handler, empty)
                new = ExceptionSet.fromTops(env, catchtype)
                self.sets[handler] = old | (new - sofar)
                sofar = sofar | new
            self.mask = sofar
            self.pruneKeys()
        self._conscheck()

    def newMask(self, mask):
        for k in self.sets:
            self.sets[k] &= mask
        self.mask &= mask
        self._conscheck()

    def pruneKeys(self):
        for handler, catchset in list(self.sets.items()):
            if not catchset:
                del self.sets[handler]

    def copy(self):
        return CatchSetManager(0,0,(self.env, self.sets.copy(), self.mask))

    def replaceKey(self, old, new):
        assert(old in self.sets and new not in self.sets)
        self.sets[new] = self.sets[old]
        del self.sets[old]

    def replaceKeys(self, replace):
        self.sets = collections.OrderedDict((replace.get(key,key), val) for key, val in self.sets.items())

    def _conscheck(self):
        temp = ExceptionSet.EMPTY
        for v in self.sets.values():
            assert(not v & temp)
            temp |= v
        assert(temp == self.mask)

class ExceptionSet(ValueType):
    __slots__ = "env pairs".split()
    def __init__(self, env, pairs): #assumes arguments are in reduced form
        self.env = env
        self.pairs = frozenset([(x,frozenset(y)) for x,y in pairs])
        assert(not pairs or '.null' not in zip(*pairs)[0])
        #We allow env to be None for the empty set so we can construct empty sets easily
        #Any operation resulting in a nonempty set will get its env from the nonempty argument
        assert(self.env or self.empty())

        #make sure set is fully reduced
        parts = []
        for t, holes in pairs:
            parts.append(t)
            parts.extend(holes)
        assert(len(set(parts)) == len(parts))

    @staticmethod #factory
    def fromTops(env, *tops):
        return ExceptionSet(env, [(x, frozenset()) for x in tops])

    def _key(self): return self.pairs
    def empty(self): return not self.pairs
    def __nonzero__(self): return bool(self.pairs)

    def getSingleTType(self): #todo - update SSA printer
        #comSuper doesn't care about order so we can freely pass in nondeterministic order
        return objtypes.commonSupertype(self.env, [(top,0) for (top,holes) in self.pairs])

    def getTopTTs(self): return sorted([(top,0) for (top,holes) in self.pairs])

    def __sub__(self, other):
        assert(type(self) == type(other))
        if self.empty() or other.empty():
            return self
        if self == other:
            return ExceptionSet.EMPTY

        subtest = self.env.isSubclass
        pairs = self.pairs

        for pair2 in other.pairs:
            #Warning, due to a bug in Python, TypeErrors raised inside the gen expr will give an incorect error message
            #TypeError: type object argument after * must be a sequence, not generator
            #This can be worked around by using a list comprehension instead of a genexpr after the *
            pairs = itertools.chain(*[ExceptionSet.diffPair(subtest, pair1, pair2) for pair1 in pairs])
        return ExceptionSet.reduce(self.env, pairs)

    def __or__(self, other):
        assert(type(self) == type(other))
        if other.empty() or self == other:
            return self
        if self.empty():
            return other
        return ExceptionSet.reduce(self.env, self.pairs | other.pairs)

    def __and__(self, other):
        assert(type(self) == type(other))
        new = self - (self - other)
        return new

    def isdisjoint(self, other):
        return (self-other) == self

    def __str__(self):
        parts = [('{} - [{}]'.format(top, ', '.join(sorted(holes))) if holes else top) for top, holes in self.pairs]
        return 'ES[{}]'.format(', '.join(parts))
    __repr__ = __str__

    @staticmethod
    def diffPair(subtest, pair1, pair2): #subtract pair2 from pair1. Returns a list of new pairs
        #todo - find way to make this less ugly
        t1, holes1 = pair1
        t2, holes2 = pair2
        if subtest(t1,t2): #t2 >= t1
            if any(subtest(t1, h) for h in holes2):
                return pair1,
            else:
                newpairs = []
                holes2 = [h for h in holes2 if subtest(h, t1) and not any(subtest(h,h2) for h2 in holes1)]

                for h in holes2:
                    newholes = [h2 for h2 in holes1 if subtest(h2, h)]
                    newpairs.append((h, newholes))
                return newpairs
        elif subtest(t2,t1): #t2 < t1
            if any(subtest(t2, h) for h in holes1):
                return pair1,
            else:
                newpairs = [(t1,ExceptionSet.reduceHoles(subtest, list(holes1)+[t2]))]
                holes2 = [h for h in holes2 if not any(subtest(h,h2) for h2 in holes1)]

                for h in holes2:
                    newholes = [h2 for h2 in holes1 if subtest(h2, h)]
                    newpairs.append((h, newholes))
                return newpairs
        else:
            return pair1,

    @staticmethod
    def mergePair(subtest, pair1, pair2): #merge pair2 into pair1 and return the union
        t1, holes1 = pair1
        t2, holes2 = pair2
        assert(subtest(t2,t1))

        if t2 in holes1:
            holes1 = list(holes1)
            holes1.remove(t2)
            return t1, holes1 + list(holes2)

        #TODO - this can probably be made more efficient
        holes1a = set(h for h in holes1 if not subtest(h, t2))
        holes1b = [h for h in holes1 if h not in holes1a]

        merged_holes = set()
        for h1, h2 in itertools.product(holes1b, holes2):
            if subtest(h2, h1):
                merged_holes.add(h1)
            elif subtest(h1, h2):
                merged_holes.add(h2)
        merged_holes = ExceptionSet.reduceHoles(subtest, merged_holes)
        assert(len(merged_holes) <= len(holes1b) + len(holes2))
        return t1, (list(holes1a) + merged_holes)

    @staticmethod
    def reduceHoles(subtest, holes):
        newholes = []
        for hole in holes:
            for ehole in newholes:
                if subtest(hole, ehole):
                    break
            else:
                newholes = [hole] + [h for h in newholes if not subtest(h, hole)]
        return newholes

    @staticmethod
    def reduce(env, pairs):
        subtest = env.isSubclass
        pairs = [pair for pair in pairs if pair[0] not in pair[1]] #remove all degenerate pairs

        newpairs = []
        while pairs:
            top, holes = pair = pairs.pop()

            #look for an existing top to merge into
            for epair in newpairs[:]:
                etop, eholes = epair
                #new pair can be merged into existing pair
                if subtest(top, etop) and (top in eholes or not any(subtest(top, ehole) for ehole in eholes)):
                    new = ExceptionSet.mergePair(subtest, epair, pair)
                    newpairs, pairs = [new], [p for p in newpairs if p is not epair] + pairs
                    break
                #existing pair can be merged into new pair
                elif subtest(etop, top) and (etop in holes or not any(subtest(etop, hole) for hole in holes)):
                    new = ExceptionSet.mergePair(subtest, pair, epair)
                    newpairs, pairs = [new], [p for p in newpairs if p is not epair] + pairs
                    break
            #pair is incomparable to all existing pairs
            else:
                holes = ExceptionSet.reduceHoles(subtest, holes)
                newpairs.append((top,holes))
        return ExceptionSet(env, newpairs)

ExceptionSet.EMPTY = ExceptionSet(None, [])




© 2015 - 2025 Weber Informatics LLC | Privacy Policy