Commit e4b29aa3 authored by Sander Mathijs van Veen's avatar Sander Mathijs van Veen Committed by Taddeus Kroes

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

parent 1e478565
......@@ -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