diff options
Diffstat (limited to 'abdl/predicates.py')
-rw-r--r-- | abdl/predicates.py | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/abdl/predicates.py b/abdl/predicates.py new file mode 100644 index 0000000..4df9cdf --- /dev/null +++ b/abdl/predicates.py @@ -0,0 +1,94 @@ +# 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 <https://www.gnu.org/licenses/>. + +"""Classes for use with ABDL's Predicate system. + +See ``abdl.predicates.Predicate`` and the language reference for details. +""" + +# pylint: disable=too-few-public-methods + +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) + # 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) |