__docformat__ = 'restructuredtext' class Pattern: """ A pattern matches a string much like a regex. Our patterns have the additional properties of being unionable and intersectable. """ def __init__(self, positive_match, *args): """ `args` is a series of strings the pattern should match. If `positive_match` is False, then `args` is a series of strings that should *not* be matched. """ self.items = set(args) self.invert = not positive_match def match(self, str): """ Determine if this Pattern matches `str` """ retval = str in self.items if self.invert: retval = not retval return retval def nonempty(self): """ Returns True if this pattern will *ever* match *any* string. """ if self.invert: return True if len(self.items) > 0: return True return False def singular(self): """ Returns True if this pattern will match *exactly one* string """ return not self.invert and len(self.items) == 1 def complement(self): """ Return a Pattern that matches anything this pattern *doesn't* match. """ return Pattern(self.invert, *self.items) def union(self, other): """ Return a Pattern that matches anything this pattern *or* the pattern `other` would match. """ invert = True if self.invert and other.invert: items = self.items & other.items elif not (self.invert or other.invert): items = self.items | other.items invert = False else: if self.invert: items = self.items - other.items else: items = other.items - self.items return Pattern(not invert, *items) def intersect(self, other): """ Return a Pattern that matches anything this pattern *and* the pattern `other` would match. """ return self.complement().union(other.complement()).complement() def __str__(self): if len(self.items) == 0: if self.invert: return "*" else: return "(-)" if self.invert: return "!(%s)" % "|".join(self.items) else: return "(%s)" % "|".join(self.items) def __repr__(self): return str(self) def __hash__(self): return hash(str(self)) def __eq__(self, other): if not other.__class__ == Pattern: return False return self.items == other.items and self.invert == other.invert