# $Id: cache_args.py 97f0531f7c21 2008-08-30 mtnyogi $
# coding=utf-8
#
# Copyright 2008 Bruce Frederiksen
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
r'''
'''
import itertools
from pyke import contexts
class selector(object):
def __init__(self):
self.all = [] # [(pat_context, pattern, link)...]
self.full_vars = [] # [(pat_context, pattern, [link])...]
self.partial_vars = [] # [(pat_context, pattern, [link])...]
self.data = {} # {data: [link]}
def add(self, pat_context, pattern, link):
self.all.append((pat_context, pattern, link))
if pattern.is_data(pat_context):
self.data.set_default(pattern.as_data(pat_context), []).append(link)
else:
self.vars # FIX ??
def gen_subset(self, pat_context, pattern):
''' yields links without binding any pattern variables.
'''
if pattern.is_data(pat_context):
for link in self.data[pattern.as_data(pat_context)]: yield link
else:
for p_c, p, links in self.full_vars:
for link in links: yield link
if not isinstance(pattern, contexts.variable):
self.partial_vars # FIX subsets
def gen_match(self, pat_context, pattern):
''' yields links binding pattern variables.
'''
if pattern.is_data(pat_context):
for link in self.data[pattern.as_data(pat_context)]: yield link
elif isinstance(pattern, contexts.variable):
self.all # FIX all
else:
self.partial_vars # FIX matches
self.full_vars # FIX all
class cache_args(object):
def __init__(self):
self.args_list = [] # [(pat_context, (pattern...))...]
self.hashes = {} # (len, (index...)): (other_indices,
# {(arg...): [other_args_from_factn...]})
def reset(self):
self.args_list = []
self.hashes.clear()
def lookup(self, bindings, pat_context, patterns):
""" Binds patterns to successive facts, yielding None for each
successful match. Undoes bindings upon continuation, so that no
bindings remain at StopIteration.
"""
indices = tuple(enum for enum in enumerate(patterns)
if enum[1].is_data(pat_context))
other_indices, other_arg_lists = \
self._get_hashed(len(patterns),
tuple(index[0] for index in indices),
tuple(index[1].as_data(pat_context)
for index in indices))
if other_arg_lists:
for args in other_arg_lists:
mark = bindings.mark(True)
try:
if all(itertools.imap(lambda i, arg:
patterns[i].match_data(bindings,
pat_context,
arg),
other_indices,
args)):
bindings.end_save_all_undo()
yield
else:
bindings.end_save_all_undo()
finally:
bindings.undo_to_mark(mark)
def _get_hashed(self, len, indices, args):
ans = self.hashes.get((len, indices))
if ans is None: ans = self._hash(len, indices)
other_indices, arg_map = ans
return other_indices, arg_map.get(args, ())
def _hash(self, length, indices):
args_hash = {}
new_entry = (tuple(i for i in range(length) if i not in indices),
args_hash)
self.hashes[length, indices] = new_entry
for args in itertools.chain(self.universal_facts,
self.case_specific_facts):
if len(args) == length:
selected_args = tuple(arg for i, arg in enumerate(args)
if i in indices)
args_hash.setdefault(selected_args, []) \
.append(tuple(arg for i, arg in enumerate(args)
if i not in indices))
return new_entry
def add_universal_fact(self, args):
assert args not in self.case_specific_facts, \
"add_universal_fact: fact already present as specific fact"
if args not in self.universal_facts:
self.universal_facts.append(args)
self.add_args(args)
def add_case_specific_fact(self, args):
if args not in self.universal_facts and \
args not in self.case_specific_facts:
self.case_specific_facts.append(args)
self.add_args(args)
for fc_rule, foreach_index in self.fc_rule_refs:
fc_rule.new_fact(args, foreach_index)
def add_args(self, args):
for (length, indices), (other_indices, arg_map) \
in self.hashes.iteritems():
if length == len(args):
selected_args = tuple(arg for i, arg in enumerate(args)
if i in indices)
arg_map.setdefault(selected_args, []) \
.append(tuple(arg for i, arg in enumerate(args)
if i not in indices))
def test():
import doctest
import sys
# FIX sys.exit(doctest.testmod()[0])
if __name__ == "__main__":
test()
|