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

Added stringification for (indefinite and definite) integrals.

Taddeus Kroes 14 лет назад
Родитель
Сommit
a6b3d67158
3 измененных файлов с 50 добавлено и 4 удалено
  1. 31 1
      src/node.py
  2. 1 2
      src/parser.py
  3. 18 1
      tests/test_node.py

+ 31 - 1
src/node.py

@@ -2,6 +2,7 @@
 import os.path
 import os.path
 import sys
 import sys
 import copy
 import copy
+import re
 
 
 sys.path.insert(0, os.path.realpath('external'))
 sys.path.insert(0, os.path.realpath('external'))
 
 
@@ -27,7 +28,7 @@ OP_SUB = 3
 OP_MUL = 4
 OP_MUL = 4
 OP_DIV = 5
 OP_DIV = 5
 OP_POW = 6
 OP_POW = 6
-OP_MOD = 7
+OP_SUBSCRIPT = 7
 
 
 # N-ary (functions)
 # N-ary (functions)
 OP_INT = 8
 OP_INT = 8
@@ -74,6 +75,7 @@ OP_MAP = {
         '*': OP_MUL,
         '*': OP_MUL,
         '/': OP_DIV,
         '/': OP_DIV,
         '^': OP_POW,
         '^': OP_POW,
+        '_': OP_SUBSCRIPT,
         'sin': OP_SIN,
         'sin': OP_SIN,
         'cos': OP_COS,
         'cos': OP_COS,
         'tan': OP_TAN,
         'tan': OP_TAN,
@@ -100,6 +102,7 @@ TOKEN_MAP = {
         OP_MUL: 'TIMES',
         OP_MUL: 'TIMES',
         OP_DIV: 'DIVIDE',
         OP_DIV: 'DIVIDE',
         OP_POW: 'POW',
         OP_POW: 'POW',
+        OP_SUBSCRIPT: 'SUB',
         OP_SQRT: 'FUNCTION',
         OP_SQRT: 'FUNCTION',
         OP_SIN: 'FUNCTION',
         OP_SIN: 'FUNCTION',
         OP_COS: 'FUNCTION',
         OP_COS: 'FUNCTION',
@@ -289,6 +292,33 @@ class ExpressionNode(Node, ExpressionBase):
             if children[1].isdigit():
             if children[1].isdigit():
                 return 'log_%s(%s)' % (children[1], children[0])
                 return 'log_%s(%s)' % (children[1], children[0])
 
 
+        if self.op == OP_INT:
+            # Make sure that any needed parentheses around f(x) are generated,
+            # and append ' dx' to it (result 'f(x) dx')
+            fx, x = self[:2]
+            operand = re.sub(r'(\s*\*)?\s*d$', ' d' + x.value, str(fx * 'd'))
+            op = 'int'
+
+            # Add bounds
+            if len(self) > 2:
+                lbnd, ubnd = self[2:]
+                lbnd = str(ExpressionNode(OP_SUBSCRIPT, lbnd))
+                ubnd = str(ExpressionNode(OP_POW, ubnd))
+                op += lbnd + ubnd
+
+            # int x ^ 2      ->  int x ^ 2 dx
+            # int x + 1      ->  int (x + 1) dx
+            # int_a^b x ^ 2  ->  int_a^b x ^ 2 dx
+            return op + ' ' + operand
+
+        if self.op == OP_INT_INDEF:
+            # [x ^ 2]_a^b
+            F, lbnd, ubnd = self
+            lbnd = str(ExpressionNode(OP_SUBSCRIPT, lbnd))
+            ubnd = str(ExpressionNode(OP_POW, ubnd))
+
+            return '[%s]%s%s' % (F, lbnd, ubnd)
+
     def __str__(self):  # pragma: nocover
     def __str__(self):  # pragma: nocover
         return generate_line(self)
         return generate_line(self)
 
 

+ 1 - 2
src/parser.py

@@ -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', 'SUB'] \
+              'RBRACKET', 'PRIME', 'DERIVATIVE'] \
              + filter(lambda t: t != 'FUNCTION', TOKEN_MAP.values())
              + filter(lambda t: t != 'FUNCTION', TOKEN_MAP.values())
 
 
     # ------------------------------
     # ------------------------------
@@ -628,7 +628,6 @@ class Parser(BisonParser):
     d[ ]*"/"[ ]*"d*"[a-z]"*" { returntoken(DERIVATIVE); }
     d[ ]*"/"[ ]*"d*"[a-z]"*" { returntoken(DERIVATIVE); }
     [0-9]+"."?[0-9]* { returntoken(NUMBER); }
     [0-9]+"."?[0-9]* { returntoken(NUMBER); }
     [a-zA-Z]  { returntoken(IDENTIFIER); }
     [a-zA-Z]  { returntoken(IDENTIFIER); }
-    "_"       { returntoken(SUB); }
     "("       { returntoken(LPAREN); }
     "("       { returntoken(LPAREN); }
     ")"       { returntoken(RPAREN); }
     ")"       { returntoken(RPAREN); }
     "["       { returntoken(LBRACKET); }
     "["       { returntoken(LBRACKET); }

+ 18 - 1
tests/test_node.py

@@ -219,12 +219,29 @@ class TestNode(RulesTestCase):
         self.assertTrue(ln0.contains(a))
         self.assertTrue(ln0.contains(a))
         self.assertFalse(ln1.contains(a))
         self.assertFalse(ln1.contains(a))
 
 
-    def test_construct_function(self):
+    def test_construct_function_derivative(self):
         self.assertEqual(str(tree('der(x ^ 2)')), '[x ^ 2]\'')
         self.assertEqual(str(tree('der(x ^ 2)')), '[x ^ 2]\'')
         self.assertEqual(str(tree('der(der(x ^ 2))')), '[x ^ 2]\'\'')
         self.assertEqual(str(tree('der(der(x ^ 2))')), '[x ^ 2]\'\'')
         self.assertEqual(str(tree('der(x ^ 2, x)')), 'd/dx (x ^ 2)')
         self.assertEqual(str(tree('der(x ^ 2, x)')), 'd/dx (x ^ 2)')
 
 
+    def test_construct_function_logarithm(self):
         self.assertEqual(str(tree('log(x, e)')), 'ln(x)')
         self.assertEqual(str(tree('log(x, e)')), 'ln(x)')
         self.assertEqual(str(tree('log(x, 10)')), 'log(x)')
         self.assertEqual(str(tree('log(x, 10)')), 'log(x)')
         self.assertEqual(str(tree('log(x, 2)')), 'log_2(x)')
         self.assertEqual(str(tree('log(x, 2)')), 'log_2(x)')
         self.assertEqual(str(tree('log(x, g)')), 'log(x, g)')
         self.assertEqual(str(tree('log(x, g)')), 'log(x, g)')
+
+    def test_construct_function_integral(self):
+        self.assertEqual(str(tree('int x ^ 2')), 'int x ^ 2 dx')
+        self.assertEqual(str(tree('int x ^ 2 dx')), 'int x ^ 2 dx')
+        self.assertEqual(str(tree('int x ^ 2 dy')), 'int x ^ 2 dy')
+        self.assertEqual(str(tree('int x ^ 2 dy')), 'int x ^ 2 dy')
+        self.assertEqual(str(tree('int x + 1')), 'int (x + 1) dx')
+
+        self.assertEqual(str(tree('int_a^b x ^ 2')), 'int_a^b x ^ 2 dx')
+        self.assertEqual(str(tree('int_(a-b)^(a+b) x ^ 2')),
+                         'int_(a - b)^(a + b) x ^ 2 dx')
+
+    def test_construct_function_indef(self):
+        self.assertEqual(str(tree('[x ^ 2]_a^b')), '[x ^ 2]_a^b')
+        self.assertEqual(str(tree('[x ^ 2]_(a-b)^(a+b)')),
+                         '[x ^ 2]_(a - b)^(a + b)')