# This file is part of A Boneless Datastructure Language # Copyright (C) 2020 Soni L. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 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 Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . """Classes for use with ABDL's Predicate system. See ``abdl.predicates.Predicate`` and the language reference for details. """ # pylint: disable=too-few-public-methods import warnings import abdl.feature class Predicate: """A predicate checks if an object is accepted in an ABDL expression. """ def accept(self, obj): """Checks if ``obj`` is accepted by this predicate. Args: obj: The object to be accepted. Returns: bool: True if the object is accepted, False otherwise. """ raise NotImplementedError class IsInstance(Predicate): """A ``Predicate`` that accepts objects according to ``isinstance``. Used to implement ``:$foo`` when ``defs['foo']`` is neither a ``Predicate`` nor a ``tuple``. One generally does not need to explicitly create instances of this class, as abdl accepts types wherever instances of this class are accepted. Args: ty (type): The expected type. """ def __init__(self, type_): self.type_ = type_ """The expected type.""" def accept(self, obj): return isinstance(obj, self.type_) class Union(Predicate): """A ``Predicate`` built from multiple predicates. Used to implement ``:$foo`` when ``defs['foo']`` is not a ``Predicate`` but is a ``tuple``. Can be constructed from any iterable. If the iterable yields different results on different runs (e.g. is a generator), the behaviour is undefined. Has semantics equivalent to ``any(p.accept(obj) for p in predicates)``, except p can also be a type or a tuple. One generally does not need to explicitly create instances of this class, as abdl accepts tuples wherever instances of this class are accepted. Args: predicates: An iterable of predicates, types and tuples that yields the same sequence on different iterations. """ def __init__(self, predicates): self.predicates = predicates """The iterable of predicates, types and tuples.""" def accept(self, obj): for predicate in self.predicates: if _to_predicate(predicate).accept(obj): return True return False def _to_predicate(obj): if isinstance(obj, Predicate): return obj if isinstance(obj, tuple): return Union(obj) if isinstance(obj, type) and issubclass(obj, Predicate): warnings.warn("deprecated", abdl.feature.PredicateTypePredicate) # I don't know if anyone relies on the old behaviour of passing the thing directly to isinstance # but this lets the exceptions be raised almost exactly like before return IsInstance(obj)