git @ Cat's Eye Technologies Samovar / master src / samovar / database.py
master

Tree @master (Download .tar.gz)

database.py @masterraw · history · blame

from samovar.ast import Assert, Retract


class Database(object):
    """A database is a set of things true at some point in time in a world."""

    def __init__(self, propositions, sorted_search=True):
        """Create a new Database object with the given propositions.

        While the construction and iteration of the underlying set in Python 2 seems to be
        stable, when running under Python 3, we get an unpredictable order when iterating
        over the set.  The `sorted_search` option controls whether we sort the iterable
        before making matches, so that we can accumulate them in a predictable order.
        This is valuable for tests, but can be turned off to improve performance.

        """
        self.sorted_search = sorted_search
        self.contents = set()
        for term in propositions:
            self.contents.add(term)

    def clone(self):
        return Database(self.contents, sorted_search=self.sorted_search)

    def add(self, term):
        self.contents.add(term)

    def remove(self, term):
        self.contents.remove(term)

    def match_all(self, patterns, env):
        """Find all matches for the given patterns in the database, and return a list of unifiers.

        `database` is an iterable of `Term`s.
        `patterns` is a list of `Assert`s and `Retract`s.
        `env` is a dict mapping `Var` names to `Term`s (a previous unifier.)

        """
        if not patterns:
            return [env]
        envs = []
        pattern = patterns[0]

        if isinstance(pattern, Assert):
            for proposition in (sorted(self.contents) if self.sorted_search else self.contents):
                unifier = pattern.term.match(proposition, env, unique_binding=True)
                if unifier is None:
                    continue
                envs.extend(self.match_all(patterns[1:], unifier))

        elif isinstance(pattern, Retract):
            expanded_pattern = pattern.term.subst(env)

            # To test a negative match, we require that there are
            # no free variables remaining in our pattern.  If there are,
            # it will simply not match.  TODO add a flag to turn this
            # check on to support better debugging.

            # free_vars = set()
            # expanded_pattern.collect_variables(free_vars)
            # if free_vars:
            #     # TODO: better exception than this
            #     raise NotImplementedError

            # now we simply check if the term exists in the database.
            # if it does not, we recurse down to the next clause in the pattern.

            if not (expanded_pattern in self.contents):
                envs.extend(self.match_all(patterns[1:], env))

        else:
            raise NotImplementedError

        return envs