Просмотр исходного кода

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

Sander Mathijs van Veen 14 лет назад
Родитель
Сommit
72367e731d
7 измененных файлов с 66 добавлено и 58 удалено
  1. 26 27
      src/parser.py
  2. 8 0
      src/rules/__init__.py
  3. 6 18
      src/rules/poly.py
  4. 10 0
      src/rules/utils.py
  5. 4 3
      tests/test_b1_ch8.py
  6. 9 8
      tests/test_calc.py
  7. 3 2
      tests/test_rules.py

+ 26 - 27
src/parser.py

@@ -20,21 +20,22 @@ from pybison import BisonParser, BisonSyntaxError
 from graph_drawing.graph import generate_graph
 
 from node import TYPE_OPERATOR, OP_ADD, OP_MUL, OP_SUB
+from rules import RULES
 
 
-# Check for n-ary operator in child nodes
-def combine(op, op_type, *nodes):
-    # At least return the operator.
-    res = [op]
-
-    for n in nodes:
-        # Merge the children for all nodes which have the same operator.
-        if n.type == TYPE_OPERATOR and n.op == op_type:
-            res += n.nodes
-        else:
-            res.append(n)
-
-    return res
+## Check for n-ary operator in child nodes
+#def combine(op, op_type, *nodes):
+#    # At least return the operator.
+#    res = [op]
+#
+#    for n in nodes:
+#        # Merge the children for all nodes which have the same operator.
+#        if n.type == TYPE_OPERATOR and n.op == op_type:
+#            res += n.nodes
+#        else:
+#            res.append(n)
+#
+#    return res
 
 
 class Parser(BisonParser):
@@ -72,6 +73,7 @@ class Parser(BisonParser):
         BisonParser.__init__(self, **kwargs)
         self.interactive = kwargs.get('interactive', 0)
         self.timeout = kwargs.get('timeout', 0)
+        self.possibilities = []
 
     # ------------------------------------------------------------------
     # override default read method with a version that prompts for input
@@ -144,6 +146,15 @@ class Parser(BisonParser):
 
         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.
     # (which get called by the bison code each time the corresponding
@@ -247,20 +258,8 @@ class Parser(BisonParser):
                | exp POW exp
         """
 
-        if option == 0:  # rule: exp PLUS exp
-            return Node(*(combine('+', OP_ADD, 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])
+        if 0 <= option < 5:  # rule: exp PLUS exp
+            return Node(values[1], values[0], values[2])
 
         raise BisonSyntaxError('Unsupported option %d in target "%s".'
                                % (option, target))  # pragma: nocover

+ 8 - 0
src/rules/__init__.py

@@ -0,0 +1,8 @@
+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],
+        }

+ 6 - 18
src/rules.py → src/rules/poly.py

@@ -1,14 +1,16 @@
 from itertools import combinations
 
-from node import ExpressionNode as Node, ExpressionLeaf as Leaf
-from possibilities import Possibility as P
-
+from ..node import ExpressionLeaf as Leaf, TYPE_OPERATOR
+from ..possibilities import Possibility as P
 
 def match_combine_factors(node):
     """
     n + exp + m -> exp + (n + m)
     k0 * v ^ n + exp + k1 * v ^ n -> exp + (k0 + k1) * v ^ n
     """
+    if node.type != TYPE_OPERATOR:
+        return []
+
     p = []
 
     if node.is_nary():
@@ -59,7 +61,7 @@ def combine_numerics(root, args):
     Combine two numeric leaves in an n-ary plus.
 
     Example:
-    3 + 4 -> 7
+    >>> 3 + 4 -> 7
     """
     numerics, others = args
     value = sum([n.value for n in numerics])
@@ -94,17 +96,3 @@ def combine_orders(root, args):
         combined = Node('*', Leaf(coeff), ident)
 
     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],
-        }

+ 10 - 0
src/rules/utils.py

@@ -0,0 +1,10 @@
+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])

+ 4 - 3
tests/test_b1_ch8.py

@@ -15,7 +15,8 @@ class TestB1Ch8(unittest.TestCase):
             ('-5*(-3)^2', N('*', N('-', L(5)),
                                  N('^', N('-', L(3)), L(2)))),
             ('7p-3p', N('-', N('*', L(7), L('p')), N('*', L(3), L('p')))),
-            ('-5a*-6', N('*', N('-', L(5)), L('a'), N('-', L(6)))),
-            ('3a-8--5-2a', N('-', N('*', L(3), L('a')), L(8),
-                                  N('-', L(5)), N('*', L(2), L('a')))),
+            ('-5a*-6', N('*', N('*', N('-', L(5)), L('a')),
+                              N('-', L(6)))),
+            ('3a-8--5-2a', N('-', N('-', N('-', N('*', L(3), L('a')), L(8)),
+                                  N('-', L(5))), N('*', L(2), L('a')))),
             ])

+ 9 - 8
tests/test_calc.py

@@ -49,15 +49,16 @@ class TestCalc(unittest.TestCase):
                                              N('+', L(5), L(7)))),
                        ('(a+b)(c+d)', N('*', N('+', L('a'), L('b')),
                                              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'))))),
-                       ('abcd',   N('*', L('a'), L('b'), L('c'), L('d'))),
-                       ('ab(c)d',   N('*', L('a'), L('b'), L('c'), L('d'))),
-                       #('ab(c)d',   N('*', L('a'), N('*', L('b'),
-                       #                              N('*', L('c'), L('d'))))),
-                       ('ab*(c)*d',   N('*', L('a'), L('b'), L('c'), L('d'))),
-                       ('ab*(c)^d',   N('*', L('a'), L('b'),
-                                        N('^', L('c'), L('d')))),
+                       ('abcd', N('*', N('*', N('*', L('a'), L('b')),
+                                           L('c')), L('d'))),
+                       ('ab(c)d', N('*', N('*', N('*', L('a'), L('b')),
+                                           L('c')), L('d'))),
+                       ('ab*(c)*d', N('*', N('*', N('*', L('a'), L('b')), 
+                                           L('c')), L('d'))),
+                       ('ab*(c)^d', N('*', N('*', L('a'), L('b')),
+                                           N('^', L('c'), L('d')))),
                       ]
 
         run_expressions(Parser, expressions)

+ 3 - 2
tests/test_rules.py

@@ -1,8 +1,9 @@
 import unittest
 
 from src.node import ExpressionNode as N, ExpressionLeaf as L
-from src.rules import match_combine_factors, combine_numerics, \
-        combine_orders, nary_node
+from src.rules.poly import match_combine_factors, combine_numerics, \
+        combine_orders
+from src.rules.utils import nary_node
 from src.possibilities import Possibility as P