Parser will no longer combine ADD/SUB/MUL nodes to n-ary nodes.

parent 4c003ee2
...@@ -20,21 +20,22 @@ from pybison import BisonParser, BisonSyntaxError ...@@ -20,21 +20,22 @@ from pybison import BisonParser, BisonSyntaxError
from graph_drawing.graph import generate_graph from graph_drawing.graph import generate_graph
from node import TYPE_OPERATOR, OP_ADD, OP_MUL, OP_SUB from node import TYPE_OPERATOR, OP_ADD, OP_MUL, OP_SUB
from rules import RULES
# Check for n-ary operator in child nodes ## Check for n-ary operator in child nodes
def combine(op, op_type, *nodes): #def combine(op, op_type, *nodes):
# At least return the operator. # # At least return the operator.
res = [op] # res = [op]
#
for n in nodes: # for n in nodes:
# Merge the children for all nodes which have the same operator. # # Merge the children for all nodes which have the same operator.
if n.type == TYPE_OPERATOR and n.op == op_type: # if n.type == TYPE_OPERATOR and n.op == op_type:
res += n.nodes # res += n.nodes
else: # else:
res.append(n) # res.append(n)
#
return res # return res
class Parser(BisonParser): class Parser(BisonParser):
...@@ -72,6 +73,7 @@ class Parser(BisonParser): ...@@ -72,6 +73,7 @@ class Parser(BisonParser):
BisonParser.__init__(self, **kwargs) BisonParser.__init__(self, **kwargs)
self.interactive = kwargs.get('interactive', 0) self.interactive = kwargs.get('interactive', 0)
self.timeout = kwargs.get('timeout', 0) self.timeout = kwargs.get('timeout', 0)
self.possibilities = []
# ------------------------------------------------------------------ # ------------------------------------------------------------------
# override default read method with a version that prompts for input # override default read method with a version that prompts for input
...@@ -144,6 +146,15 @@ class Parser(BisonParser): ...@@ -144,6 +146,15 @@ class Parser(BisonParser):
return data return data
def hook_handler(self, target, option, names, values, retval):
if not retval or retval.type not in RULES:
return retval
for handler in RULES[retval.type]:
self.possibilities.extend(handler(retval))
return retval
# --------------------------------------------------------------- # ---------------------------------------------------------------
# These methods are the python handlers for the bison targets. # These methods are the python handlers for the bison targets.
# (which get called by the bison code each time the corresponding # (which get called by the bison code each time the corresponding
...@@ -247,20 +258,8 @@ class Parser(BisonParser): ...@@ -247,20 +258,8 @@ class Parser(BisonParser):
| exp POW exp | exp POW exp
""" """
if option == 0: # rule: exp PLUS exp if 0 <= option < 5: # rule: exp PLUS exp
return Node(*(combine('+', OP_ADD, values[0], values[2]))) return Node(values[1], values[0], values[2])
if option == 1: # rule: exp MINUS exp
return Node(*(combine('-', OP_SUB, values[0], values[2])))
if option == 2: # rule: exp TIMES exp
return Node(*(combine('*', OP_MUL, values[0], values[2])))
if option == 3: # rule: exp DIVIDE exp
return Node('/', values[0], values[2])
if option == 4: # rule: exp POW exp
return Node('^', values[0], values[2])
raise BisonSyntaxError('Unsupported option %d in target "%s".' raise BisonSyntaxError('Unsupported option %d in target "%s".'
% (option, target)) # pragma: nocover % (option, target)) # pragma: nocover
......
from ..node import ExpressionNode as Node, OP_ADD
from .poly import match_combine_factors#, match_combine_parentheses
RULES = {
OP_ADD: [match_combine_factors],
#OP_MUL: [match_combine_parentheses],
}
from itertools import combinations from itertools import combinations
from node import ExpressionNode as Node, ExpressionLeaf as Leaf from ..node import ExpressionLeaf as Leaf, TYPE_OPERATOR
from possibilities import Possibility as P from ..possibilities import Possibility as P
def match_combine_factors(node): def match_combine_factors(node):
""" """
n + exp + m -> exp + (n + m) n + exp + m -> exp + (n + m)
k0 * v ^ n + exp + k1 * v ^ n -> exp + (k0 + k1) * v ^ n k0 * v ^ n + exp + k1 * v ^ n -> exp + (k0 + k1) * v ^ n
""" """
if node.type != TYPE_OPERATOR:
return []
p = [] p = []
if node.is_nary(): if node.is_nary():
...@@ -59,7 +61,7 @@ def combine_numerics(root, args): ...@@ -59,7 +61,7 @@ def combine_numerics(root, args):
Combine two numeric leaves in an n-ary plus. Combine two numeric leaves in an n-ary plus.
Example: Example:
3 + 4 -> 7 >>> 3 + 4 -> 7
""" """
numerics, others = args numerics, others = args
value = sum([n.value for n in numerics]) value = sum([n.value for n in numerics])
...@@ -94,17 +96,3 @@ def combine_orders(root, args): ...@@ -94,17 +96,3 @@ def combine_orders(root, args):
combined = Node('*', Leaf(coeff), ident) combined = Node('*', Leaf(coeff), ident)
return nary_node('+', others + [combined]) return nary_node('+', others + [combined])
def nary_node(operator, scope):
"""
Create a binary expression tree for an n-ary operator. Takes the operator
and a list of expression nodes as arguments.
"""
return scope[0] if len(scope) == 1 \
else Node(operator, nary_node(operator, scope[:-1]), scope[-1])
rules = {
'+': [match_combine_factors],
}
from ..node import ExpressionNode as Node
def nary_node(operator, scope):
"""
Create a binary expression tree for an n-ary operator. Takes the operator
and a list of expression nodes as arguments.
"""
return scope[0] if len(scope) == 1 \
else Node(operator, nary_node(operator, scope[:-1]), scope[-1])
...@@ -15,7 +15,8 @@ class TestB1Ch8(unittest.TestCase): ...@@ -15,7 +15,8 @@ class TestB1Ch8(unittest.TestCase):
('-5*(-3)^2', N('*', N('-', L(5)), ('-5*(-3)^2', N('*', N('-', L(5)),
N('^', N('-', L(3)), L(2)))), N('^', N('-', L(3)), L(2)))),
('7p-3p', N('-', N('*', L(7), L('p')), N('*', L(3), L('p')))), ('7p-3p', N('-', N('*', L(7), L('p')), N('*', L(3), L('p')))),
('-5a*-6', N('*', N('-', L(5)), L('a'), N('-', L(6)))), ('-5a*-6', N('*', N('*', N('-', L(5)), L('a')),
('3a-8--5-2a', N('-', N('*', L(3), L('a')), L(8), N('-', L(6)))),
N('-', L(5)), N('*', L(2), L('a')))), ('3a-8--5-2a', N('-', N('-', N('-', N('*', L(3), L('a')), L(8)),
N('-', L(5))), N('*', L(2), L('a')))),
]) ])
...@@ -49,15 +49,16 @@ class TestCalc(unittest.TestCase): ...@@ -49,15 +49,16 @@ class TestCalc(unittest.TestCase):
N('+', L(5), L(7)))), N('+', L(5), L(7)))),
('(a+b)(c+d)', N('*', N('+', L('a'), L('b')), ('(a+b)(c+d)', N('*', N('+', L('a'), L('b')),
N('+', L('c'), L('d')))), N('+', L('c'), L('d')))),
('a+b(c+d)', N('+', L('a'), N('*', L('b'), ('a+b(c+d)', N('+', L('a'), N('*', L('b'),
N('+', L('c'), L('d'))))), N('+', L('c'), L('d'))))),
('abcd', N('*', L('a'), L('b'), L('c'), L('d'))), ('abcd', N('*', N('*', N('*', L('a'), L('b')),
('ab(c)d', N('*', L('a'), L('b'), L('c'), L('d'))), L('c')), L('d'))),
#('ab(c)d', N('*', L('a'), N('*', L('b'), ('ab(c)d', N('*', N('*', N('*', L('a'), L('b')),
# N('*', L('c'), L('d'))))), L('c')), L('d'))),
('ab*(c)*d', N('*', L('a'), L('b'), L('c'), L('d'))), ('ab*(c)*d', N('*', N('*', N('*', L('a'), L('b')),
('ab*(c)^d', N('*', L('a'), L('b'), L('c')), L('d'))),
N('^', L('c'), L('d')))), ('ab*(c)^d', N('*', N('*', L('a'), L('b')),
N('^', L('c'), L('d')))),
] ]
run_expressions(Parser, expressions) run_expressions(Parser, expressions)
......
import unittest import unittest
from src.node import ExpressionNode as N, ExpressionLeaf as L from src.node import ExpressionNode as N, ExpressionLeaf as L
from src.rules import match_combine_factors, combine_numerics, \ from src.rules.poly import match_combine_factors, combine_numerics, \
combine_orders, nary_node combine_orders
from src.rules.utils import nary_node
from src.possibilities import Possibility as P from src.possibilities import Possibility as 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