Commit a6b3d671 authored by Taddeus Kroes's avatar Taddeus Kroes

Added stringification for (indefinite and definite) integrals.

parent 6b257767
......@@ -2,6 +2,7 @@
import os.path
import sys
import copy
import re
sys.path.insert(0, os.path.realpath('external'))
......@@ -27,7 +28,7 @@ OP_SUB = 3
OP_MUL = 4
OP_DIV = 5
OP_POW = 6
OP_MOD = 7
OP_SUBSCRIPT = 7
# N-ary (functions)
OP_INT = 8
......@@ -74,6 +75,7 @@ OP_MAP = {
'*': OP_MUL,
'/': OP_DIV,
'^': OP_POW,
'_': OP_SUBSCRIPT,
'sin': OP_SIN,
'cos': OP_COS,
'tan': OP_TAN,
......@@ -100,6 +102,7 @@ TOKEN_MAP = {
OP_MUL: 'TIMES',
OP_DIV: 'DIVIDE',
OP_POW: 'POW',
OP_SUBSCRIPT: 'SUB',
OP_SQRT: 'FUNCTION',
OP_SIN: 'FUNCTION',
OP_COS: 'FUNCTION',
......@@ -289,6 +292,33 @@ class ExpressionNode(Node, ExpressionBase):
if children[1].isdigit():
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
return generate_line(self)
......
......@@ -78,7 +78,7 @@ class Parser(BisonParser):
# of tokens of the lex script.
tokens = ['NUMBER', 'IDENTIFIER', 'NEWLINE', 'QUIT', 'RAISE', 'GRAPH',
'LPAREN', 'RPAREN', 'FUNCTION', 'FUNCTION_LPAREN', 'LBRACKET',
'RBRACKET', 'PRIME', 'DERIVATIVE', 'SUB'] \
'RBRACKET', 'PRIME', 'DERIVATIVE'] \
+ filter(lambda t: t != 'FUNCTION', TOKEN_MAP.values())
# ------------------------------
......@@ -628,7 +628,6 @@ class Parser(BisonParser):
d[ ]*"/"[ ]*"d*"[a-z]"*" { returntoken(DERIVATIVE); }
[0-9]+"."?[0-9]* { returntoken(NUMBER); }
[a-zA-Z] { returntoken(IDENTIFIER); }
"_" { returntoken(SUB); }
"(" { returntoken(LPAREN); }
")" { returntoken(RPAREN); }
"[" { returntoken(LBRACKET); }
......
......@@ -219,12 +219,29 @@ class TestNode(RulesTestCase):
self.assertTrue(ln0.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(der(x ^ 2))')), '[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, 10)')), 'log(x)')
self.assertEqual(str(tree('log(x, 2)')), 'log_2(x)')
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)')
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