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

Added notation for absolute value to the parser.

Taddeus Kroes 14 лет назад
Родитель
Сommit
196a602c5b
4 измененных файлов с 59 добавлено и 27 удалено
  1. 26 21
      src/node.py
  2. 10 5
      src/parser.py
  3. 8 1
      src/rules/utils.py
  4. 15 0
      tests/test_parser.py

+ 26 - 21
src/node.py

@@ -21,35 +21,36 @@ TYPE_FLOAT = 8
 
 
 # Unary
 # Unary
 OP_NEG = 1
 OP_NEG = 1
+OP_ABS = 2
 
 
 # Binary
 # Binary
-OP_ADD = 2
-OP_SUB = 3
-OP_MUL = 4
-OP_DIV = 5
-OP_POW = 6
-OP_SUBSCRIPT = 7
+OP_ADD = 3
+OP_SUB = 4
+OP_MUL = 5
+OP_DIV = 6
+OP_POW = 7
+OP_SUBSCRIPT = 8
 
 
 # N-ary (functions)
 # N-ary (functions)
-OP_INT = 8
-OP_INT_INDEF = 9
-OP_COMMA = 10
-OP_SQRT = 11
-OP_DER = 12
-OP_LOG = 13
+OP_INT = 9
+OP_INT_INDEF = 10
+OP_COMMA = 11
+OP_SQRT = 12
+OP_DER = 13
+OP_LOG = 14
 
 
 # Goniometry
 # Goniometry
-OP_SIN = 14
-OP_COS = 15
-OP_TAN = 16
+OP_SIN = 15
+OP_COS = 16
+OP_TAN = 17
 
 
-OP_SOLVE = 17
-OP_EQ = 18
+OP_SOLVE = 18
+OP_EQ = 19
 
 
-OP_POSSIBILITIES = 19
-OP_HINT = 20
-OP_REWRITE_ALL = 21
-OP_REWRITE = 22
+OP_POSSIBILITIES = 20
+OP_HINT = 21
+OP_REWRITE_ALL = 22
+OP_REWRITE = 23
 
 
 # Special identifiers
 # Special identifiers
 PI = 'pi'
 PI = 'pi'
@@ -94,6 +95,7 @@ OP_MAP = {
 OP_VALUE_MAP = dict([(v, k) for k, v in OP_MAP.iteritems()])
 OP_VALUE_MAP = dict([(v, k) for k, v in OP_MAP.iteritems()])
 OP_MAP['ln'] = OP_LOG
 OP_MAP['ln'] = OP_LOG
 OP_VALUE_MAP[OP_INT_INDEF] = 'indef'
 OP_VALUE_MAP[OP_INT_INDEF] = 'indef'
+OP_VALUE_MAP[OP_ABS] = 'abs'
 
 
 TOKEN_MAP = {
 TOKEN_MAP = {
         OP_COMMA: 'COMMA',
         OP_COMMA: 'COMMA',
@@ -319,6 +321,9 @@ class ExpressionNode(Node, ExpressionBase):
 
 
             return '[%s]%s%s' % (F, lbnd, ubnd)
             return '[%s]%s%s' % (F, lbnd, ubnd)
 
 
+        if self.op == OP_ABS:
+            return '|%s|' % children[0]
+
     def __str__(self):  # pragma: nocover
     def __str__(self):  # pragma: nocover
         return generate_line(self)
         return generate_line(self)
 
 

+ 10 - 5
src/parser.py

@@ -18,7 +18,7 @@ from node import ExpressionBase, ExpressionNode as Node, \
         ExpressionLeaf as Leaf, OP_MAP, OP_DER, TOKEN_MAP, TYPE_OPERATOR, \
         ExpressionLeaf as Leaf, OP_MAP, OP_DER, TOKEN_MAP, TYPE_OPERATOR, \
         OP_COMMA, OP_NEG, OP_MUL, OP_DIV, OP_POW, OP_LOG, OP_ADD, Scope, E, \
         OP_COMMA, OP_NEG, OP_MUL, OP_DIV, OP_POW, OP_LOG, OP_ADD, Scope, E, \
         DEFAULT_LOGARITHM_BASE, OP_VALUE_MAP, SPECIAL_TOKENS, OP_INT, \
         DEFAULT_LOGARITHM_BASE, OP_VALUE_MAP, SPECIAL_TOKENS, OP_INT, \
-        OP_INT_INDEF
+        OP_INT_INDEF, OP_ABS
 from rules import RULES
 from rules import RULES
 from rules.utils import find_variable
 from rules.utils import find_variable
 from strategy import pick_suggestion
 from strategy import pick_suggestion
@@ -78,7 +78,7 @@ class Parser(BisonParser):
     # of tokens of the lex script.
     # of tokens of the lex script.
     tokens = ['NUMBER', 'IDENTIFIER', 'NEWLINE', 'QUIT', 'RAISE', 'GRAPH',
     tokens = ['NUMBER', 'IDENTIFIER', 'NEWLINE', 'QUIT', 'RAISE', 'GRAPH',
               'LPAREN', 'RPAREN', 'FUNCTION', 'FUNCTION_LPAREN', 'LBRACKET',
               'LPAREN', 'RPAREN', 'FUNCTION', 'FUNCTION_LPAREN', 'LBRACKET',
-              'RBRACKET', 'PRIME', 'DERIVATIVE'] \
+              'RBRACKET', 'PIPE', 'PRIME', 'DERIVATIVE'] \
              + filter(lambda t: t != 'FUNCTION', TOKEN_MAP.values())
              + filter(lambda t: t != 'FUNCTION', TOKEN_MAP.values())
 
 
     # ------------------------------
     # ------------------------------
@@ -180,16 +180,16 @@ class Parser(BisonParser):
         #   - "4a" with "4*a".
         #   - "4a" with "4*a".
         #   - "a4" with "a^4".
         #   - "a4" with "a^4".
 
 
-        pattern = ('(?:(\))\s*(\(|\[)'                        # )(  -> ) * (
+        pattern = ('(?:(\))\s*([([])'                         # )(  -> ) * (
                                                               # )[  -> ) * [
                                                               # )[  -> ) * [
-                + '|([\x00-\x09\x0b-\x19a-z0-9])\s*(\(|\[)'   # a(  -> a * (
+                + '|([\x00-\x09\x0b-\x19a-z0-9])\s*([([])'    # a(  -> a * (
                                                               # a[  -> a * [
                                                               # a[  -> a * [
                 + '|(\))\s*([\x00-\x09\x0b-\x19a-z0-9])'      # )a  -> ) * a
                 + '|(\))\s*([\x00-\x09\x0b-\x19a-z0-9])'      # )a  -> ) * a
                 + '|([\x00-\x09\x0b-\x19a-z])\s*'
                 + '|([\x00-\x09\x0b-\x19a-z])\s*'
                   + '([\x00-\x09\x0b-\x19a-z])'               # ab  -> a * b
                   + '([\x00-\x09\x0b-\x19a-z])'               # ab  -> a * b
                 + '|([0-9])\s*([\x00-\x09\x0b-\x19a-z])'      # 4a  -> 4 * a
                 + '|([0-9])\s*([\x00-\x09\x0b-\x19a-z])'      # 4a  -> 4 * a
                 + '|([\x00-\x09\x0b-\x19a-z])([0-9])'         # a4  -> a ^ 4
                 + '|([\x00-\x09\x0b-\x19a-z])([0-9])'         # a4  -> a ^ 4
-                + '|([\x00-\x09\x0b-\x19a-z0-9])(\s+[0-9]))'  # a 4 -> a * 4,
+                + '|([\x00-\x09\x0b-\x19a-z0-9])(\s+[0-9]))'  # a 4 -> a * 4
                                                               # 4 4 -> 4 * 4
                                                               # 4 4 -> 4 * 4
                 )
                 )
 
 
@@ -399,6 +399,7 @@ class Parser(BisonParser):
               | INTEGRAL exp
               | INTEGRAL exp
               | integral_bounds TIMES exp %prec INTEGRAL
               | integral_bounds TIMES exp %prec INTEGRAL
               | LBRACKET exp RBRACKET lbnd ubnd
               | LBRACKET exp RBRACKET lbnd ubnd
+              | PIPE exp PIPE
         """
         """
 
 
         if option == 0:  # rule: NEG exp
         if option == 0:  # rule: NEG exp
@@ -464,6 +465,9 @@ class Parser(BisonParser):
         if option == 7:  # rule: LBRACKET exp RBRACKET lbnd ubnd
         if option == 7:  # rule: LBRACKET exp RBRACKET lbnd ubnd
             return Node(OP_INT_INDEF, values[1], values[3], values[4])
             return Node(OP_INT_INDEF, values[1], values[3], values[4])
 
 
+        if option == 8:  # rule: PIPE exp PIPE
+            return Node(OP_ABS, values[1])
+
         raise BisonSyntaxError('Unsupported option %d in target "%s".'
         raise BisonSyntaxError('Unsupported option %d in target "%s".'
                                % (option, target))  # pragma: nocover
                                % (option, target))  # pragma: nocover
 
 
@@ -633,6 +637,7 @@ class Parser(BisonParser):
     "["       { returntoken(LBRACKET); }
     "["       { returntoken(LBRACKET); }
     "]"       { returntoken(RBRACKET); }
     "]"       { returntoken(RBRACKET); }
     "'"       { returntoken(PRIME); }
     "'"       { returntoken(PRIME); }
+    "|"       { returntoken(PIPE); }
     log_([0-9]+|[a-zA-Z])"*(" { returntoken(FUNCTION_LPAREN); }
     log_([0-9]+|[a-zA-Z])"*(" { returntoken(FUNCTION_LPAREN); }
     log_([0-9]+|[a-zA-Z])"*" { returntoken(FUNCTION); }
     log_([0-9]+|[a-zA-Z])"*" { returntoken(FUNCTION); }
     """ + operators + r"""
     """ + operators + r"""

+ 8 - 1
src/rules/utils.py

@@ -1,5 +1,5 @@
 from ..node import ExpressionNode as N, ExpressionLeaf as L, OP_MUL, OP_DIV, \
 from ..node import ExpressionNode as N, ExpressionLeaf as L, OP_MUL, OP_DIV, \
-        INFINITY
+        INFINITY, OP_ABS
 
 
 
 
 def greatest_common_divisor(a, b):
 def greatest_common_divisor(a, b):
@@ -133,3 +133,10 @@ def infinity():
     Return an infinity leaf node.
     Return an infinity leaf node.
     """
     """
     return L(INFINITY)
     return L(INFINITY)
+
+
+def absolute(exp):
+    """
+    Put an 'absolute value' operator on top of the given expression.
+    """
+    return N(OP_ABS, exp)

+ 15 - 0
tests/test_parser.py

@@ -10,6 +10,7 @@ from src.rules.goniometry import sin, cos
 from src.rules.derivatives import der
 from src.rules.derivatives import der
 from src.rules.logarithmic import log, ln
 from src.rules.logarithmic import log, ln
 from src.rules.integrals import integral, indef
 from src.rules.integrals import integral, indef
+from src.rules.utils import absolute
 
 
 
 
 class TestParser(unittest.TestCase):
 class TestParser(unittest.TestCase):
@@ -51,6 +52,14 @@ class TestParser(unittest.TestCase):
         self.assertEqual(tree('2(a + b)'), tree('2 * (a + b)'))
         self.assertEqual(tree('2(a + b)'), tree('2 * (a + b)'))
         self.assertEqual(tree('(a + b)2'), tree('(a + b) * 2'))
         self.assertEqual(tree('(a + b)2'), tree('(a + b) * 2'))
 
 
+        self.assertEqual(tree('(a)(b)'), tree('(a) * (b)'))
+        self.assertEqual(tree('(a)[b]\''), tree('(a) * [b]\''))
+
+        # FIXME: self.assertEqual(tree('(a)|b|'), tree('(a) * |b|'))
+        # FIXME: self.assertEqual(tree('|a|(b)'), tree('|a| * (b)'))
+        # FIXME: self.assertEqual(tree('a|b|'), tree('a * |b|'))
+        # FIXME: self.assertEqual(tree('|a|b'), tree('|a| * b'))
+
     def test_moved_negation(self):
     def test_moved_negation(self):
         a, b = tree('a,b')
         a, b = tree('a,b')
 
 
@@ -128,3 +137,9 @@ class TestParser(unittest.TestCase):
         x2, a, b = tree('x ^ 2, a, b')
         x2, a, b = tree('x ^ 2, a, b')
 
 
         self.assertEqual(tree('[x ^ 2]_a^b'), indef(x2, a, b))
         self.assertEqual(tree('[x ^ 2]_a^b'), indef(x2, a, b))
+
+    def test_absolute_value(self):
+        x = tree('x')
+
+        self.assertEqual(tree('|x|'), absolute(x))
+        self.assertEqual(tree('|x2|'), absolute(x ** 2))