Pick the best possibiltity using the shortest path with breadth first traversal.

parent 91a26452
......@@ -22,7 +22,7 @@ from node import ExpressionNode as Node, \
from rules.utils import find_variable
from rules.precedences import IMPLICIT_RULES
from strategy import find_possibilities
from possibilities import apply_suggestion
from possibilities import apply_suggestion, pick_best_possibility
import Queue
import re
......@@ -258,7 +258,7 @@ class Parser(BisonParser):
if self.interactive:
if self.possibilities:
print self.possibilities[0]
print pick_best_possibility(self, self.possibilities)
else:
print 'No further reduction is possible.'
......@@ -268,13 +268,16 @@ class Parser(BisonParser):
for i, p in enumerate(self.possibilities):
print '%d %s' % (i, p)
def rewrite(self, index=0, verbose=False, check_implicit=True):
def rewrite(self, index=None, verbose=False, check_implicit=True):
self.find_possibilities()
if not self.possibilities:
return
suggestion = self.possibilities[index]
if index is None:
suggestion = pick_best_possibility(self, self.possibilities)
else:
suggestion = self.possibilities[index]
if self.verbose:
print 'EXPLICIT:', suggestion
......@@ -327,10 +330,10 @@ class Parser(BisonParser):
return self.root_node
def rewrite_all(self, verbose=False):
def rewrite_all(self, index=None, verbose=False):
i = 0
while self.rewrite(verbose=verbose):
while self.rewrite(index=index, verbose=verbose):
i += 1
if i > 100:
......
from node import TYPE_OPERATOR
from node import TYPE_OPERATOR, OP_NEG
# Each rule will append its hint message to the following dictionary. The
......@@ -7,6 +7,12 @@ from node import TYPE_OPERATOR
# message. The string will be processed using string.format().
MESSAGES = {}
# Dictionary used to map a stringified expression tree to the index of the
# best / shortest possibility.
BEST_POSSIBILITY_CACHE = {}
MAX_TREE_DEPTH = 20
class Possibility(object):
def __init__(self, root, handler, args=()):
......@@ -39,6 +45,36 @@ class Possibility(object):
and self.args == other.args
def find_possibilities(node, parent_op=None):
possibilities = []
handlers = []
from rules import RULES
if not node.is_leaf:
# Traverse through child nodes first using postorder traversal
for child in node:
possibilities += find_possibilities(child, node.op)
# Add operator-specific handlers. Prevent duplicate possibilities in
# n-ary nodes by only executing the handlers on the outermost node of
# related nodes with the same operator
#if node.op in RULES and (node.op != parent_op \
# or node.op not in NARY_OPERATORS):
# handlers += RULES[node.op]
if node.op in RULES:
handlers += RULES[node.op]
if node.negated:
handlers += RULES[OP_NEG]
# Run handlers
for handler in handlers:
possibilities += handler(node)
return possibilities
def find_parent_node(root, child):
nodes = [root]
......@@ -59,25 +95,97 @@ def find_parent_node(root, child):
def apply_suggestion(root, suggestion):
# TODO: clone the root node before modifying. After deep copying the root
# node, the subtree_map cannot be used since the hash() of each node in the
# deep copied root node has changed.
#root = root.clone()
# Apply the suggestion on the sub tree root with the given arguments.
subtree = suggestion.handler(suggestion.root, suggestion.args)
parent_node = find_parent_node(root, suggestion.root)
# There is either a parent node or the subtree is the root node.
# FIXME: FAIL: test_diagnostic_test_application in tests/test_b1_ch08.py
#try:
# assert bool(parent_node) != (subtree == root)
#except:
# print 'parent_node: %s' % (str(parent_node))
# print 'subtree: %s == %s' % (str(subtree), str(root))
# raise
# Find the parent node of the sub tree and, if it has a parent node,
# substitute the new sub tree into the parent's list of child nodes.
parent_node = find_parent_node(root, suggestion.root)
if parent_node:
parent_node.substitute(suggestion.root, subtree)
return root
# Apparently the whole tree is updated. Return the new tree instead of the
# old root node
return subtree
def pick_best_possibility(parser, possibilities):
root = parser.root_node
#print 'pick_best_possibility for:', root
# Get the final expression
parser.set_root_node(root.clone())
result = parser.rewrite_all(index=0)
def traverse_breadth_first(node, result, depth=0):
if depth > MAX_TREE_DEPTH:
return
node_expr = str(node)
if node_expr in BEST_POSSIBILITY_CACHE:
return BEST_POSSIBILITY_CACHE[node_expr]
# If the nodes match, there is no rewrite step needed. If the nodes to
# not converge at all, the step_index will be None as well (since there
# still is no rewrite step to do).
step_index = None
#print ' ' * depth + 'node:', node, 'result:', result, 'equal:', \
# node.equals(result)
if not node.equals(result):
children = []
possibilities = find_possibilities(node)
#print ' ' * depth + (' ' * depth + '\n').join(map(str,
# enumerate(possibilities)))
for p, possibility in enumerate(possibilities):
#print ' ' * depth + 'possibility:', possibility, 'on:', node
# Clone the root node because it will be used in multiple
# substitutions
child = apply_suggestion(node.clone(), possibility)
#print ' ' * depth + 'child:', child
if child.equals(result):
step_index = p
break
children.append(child)
# If the final expression is not found in the direct children,
# start searching in the children of the children.
if step_index is None:
for c, child in enumerate(children):
child_step = traverse_breadth_first(child, result, depth + 1)
#print ' ' * depth + 'child_step:', child_step
if child_step is not None:
step_index = c
break
BEST_POSSIBILITY_CACHE[node_expr] = step_index
return step_index
#print '--- start traversal ---'
step_index = traverse_breadth_first(root, result)
#print 'step_index:', step_index
#print '--- cache: ---'
#print BEST_POSSIBILITY_CACHE
# Reset the parser's original state
parser.set_root_node(root)
parser.find_possibilities()
return parser.possibilities[step_index]
......@@ -87,16 +87,10 @@ def depth_possibilities(node, depth=0, parent_op=None):
def find_possibilities(node):
"""
Find all possibilities inside a node and return them in a list.
Find all possibilities inside a node and return them as a list.
"""
possibilities = depth_possibilities(node)
#import copy
#old_possibilities = copy.deepcopy(possibilities)
possibilities.sort(compare_possibilities)
#get_handler = lambda (p, d): str(p.handler)
#if old_possibilities != possibilities:
# print 'before:', '\n '.join(map(get_handler, old_possibilities))
# print 'after:', '\n '.join(map(get_handler, possibilities))
return [p for p, depth in possibilities]
......
from parser import Parser
from possibilities import apply_suggestion
from possibilities import apply_suggestion, BEST_POSSIBILITY_CACHE
from strategy import find_possibilities
from tests.parser import ParserWrapper
......@@ -16,7 +16,20 @@ def validate(exp, result):
if node.equals(result):
return True
for p in find_possibilities(node):
node_expr = str(node)
if node_expr in BEST_POSSIBILITY_CACHE:
possibility_index = BEST_POSSIBILITY_CACHE[node_expr]
# If there is no possible rewrite step, bail out
if possibility_index is None:
return False
possibilities = [possibility_index]
else:
possibilities = find_possibilities(node)
for p in possibilities:
# Clone the root node because it will be used in multiple
# substitutions
child = apply_suggestion(node.clone(), p)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment