Commit f1e51800 authored by Taddeus Kroes's avatar Taddeus Kroes

Added some new syntaxes for logarithms in the parser and printer.

parent 895c81d7
...@@ -33,27 +33,29 @@ OP_MOD = 7 ...@@ -33,27 +33,29 @@ OP_MOD = 7
OP_INT = 8 OP_INT = 8
OP_COMMA = 9 OP_COMMA = 9
OP_SQRT = 10 OP_SQRT = 10
OP_DERIV = 11 OP_DER = 11
OP_LOG = 12 OP_LOG = 12
OP_LN = 13
# Goniometry # Goniometry
OP_SIN = 14 OP_SIN = 13
OP_COS = 15 OP_COS = 14
OP_TAN = 16 OP_TAN = 15
OP_SOLVE = 17 OP_SOLVE = 16
OP_EQ = 18 OP_EQ = 17
OP_POSSIBILITIES = 19 OP_POSSIBILITIES = 18
OP_HINT = 20 OP_HINT = 19
OP_REWRITE_ALL = 21 OP_REWRITE_ALL = 20
OP_REWRITE = 22 OP_REWRITE = 21
# Special identifiers # Special identifiers
PI = 'pi' PI = 'pi'
E = 'e' E = 'e'
# Default base to use in parsing 'log(...)'
DEFAULT_LOGARITHM_BASE = 10
TYPE_MAP = { TYPE_MAP = {
int: TYPE_INTEGER, int: TYPE_INTEGER,
...@@ -73,10 +75,10 @@ OP_MAP = { ...@@ -73,10 +75,10 @@ OP_MAP = {
'tan': OP_TAN, 'tan': OP_TAN,
'sqrt': OP_SQRT, 'sqrt': OP_SQRT,
'int': OP_INT, 'int': OP_INT,
'der': OP_DERIV, 'der': OP_DER,
'solve': OP_SOLVE, 'solve': OP_SOLVE,
'log': OP_LOG, 'log': OP_LOG,
'ln': OP_LN, 'ln': OP_LOG,
'=': OP_EQ, '=': OP_EQ,
'??': OP_POSSIBILITIES, '??': OP_POSSIBILITIES,
'?': OP_HINT, '?': OP_HINT,
...@@ -96,10 +98,9 @@ TOKEN_MAP = { ...@@ -96,10 +98,9 @@ TOKEN_MAP = {
OP_COS: 'FUNCTION', OP_COS: 'FUNCTION',
OP_TAN: 'FUNCTION', OP_TAN: 'FUNCTION',
OP_INT: 'FUNCTION', OP_INT: 'FUNCTION',
OP_DERIV: 'FUNCTION', OP_DER: 'FUNCTION',
OP_SOLVE: 'FUNCTION', OP_SOLVE: 'FUNCTION',
OP_LOG: 'FUNCTION', OP_LOG: 'FUNCTION',
OP_LN: 'FUNCTION',
OP_EQ: 'EQ', OP_EQ: 'EQ',
OP_POSSIBILITIES: 'POSSIBILITIES', OP_POSSIBILITIES: 'POSSIBILITIES',
OP_HINT: 'HINT', OP_HINT: 'HINT',
...@@ -162,8 +163,8 @@ class ExpressionBase(object): ...@@ -162,8 +163,8 @@ class ExpressionBase(object):
return s_root < o_root or s_exp < o_exp or s_coeff < o_coeff return s_root < o_root or s_exp < o_exp or s_coeff < o_coeff
def is_op(self, op): def is_op(self, *ops):
return not self.is_leaf and self.op == op return not self.is_leaf and self.op in ops
def is_power(self, exponent=None): def is_power(self, exponent=None):
if self.is_leaf or self.op != OP_POW: if self.is_leaf or self.op != OP_POW:
...@@ -240,13 +241,32 @@ class ExpressionNode(Node, ExpressionBase): ...@@ -240,13 +241,32 @@ class ExpressionNode(Node, ExpressionBase):
self.op = OP_MAP[args[0]] self.op = OP_MAP[args[0]]
def construct_function(self, children): def construct_function(self, children):
if self.op == OP_DERIV: if self.op == OP_DER:
f = children[0] f = children[0]
if len(children) < 2: if len(children) < 2:
# der(der(x ^ 2)) -> [x ^ 2]''
if self[0].is_op(OP_DER) and len(self[0]) < 2:
return f + '\''
# der(x ^ 2) -> [x ^ 2]'
return '[' + f + ']\'' return '[' + f + ']\''
return 'd/d' + children[1] + ' (' + f + ')' # der(x ^ 2, x) -> d/dx (x ^ 2)
return 'd/d%s (%s)' % (children[1], f)
if self.op == OP_LOG:
# log(a, e) -> ln(a)
if self[1].is_identifier(E):
return 'ln(%s)' % children[0]
# log(a, 10) -> log(a)
if self[1] == 10:
return 'log(%s)' % children[0]
# log(a, 2) -> log_2(a)
if children[1].isdigit():
return 'log_%s(%s)' % (children[1], children[0])
def __str__(self): # pragma: nocover def __str__(self): # pragma: nocover
return generate_line(self) return generate_line(self)
......
...@@ -15,13 +15,14 @@ from pybison import BisonParser, BisonSyntaxError ...@@ -15,13 +15,14 @@ from pybison import BisonParser, BisonSyntaxError
from graph_drawing.graph import generate_graph from graph_drawing.graph import generate_graph
from node import ExpressionNode as Node, ExpressionLeaf as Leaf, OP_MAP, \ from node import ExpressionNode as Node, ExpressionLeaf as Leaf, OP_MAP, \
OP_DERIV, TOKEN_MAP, TYPE_OPERATOR, OP_COMMA, OP_NEG, OP_MUL, OP_DIV, \ OP_DER, TOKEN_MAP, TYPE_OPERATOR, OP_COMMA, OP_NEG, OP_MUL, OP_DIV, \
Scope, PI OP_LOG, Scope, PI, E, DEFAULT_LOGARITHM_BASE
from rules import RULES from rules import RULES
from strategy import pick_suggestion from strategy import pick_suggestion
from possibilities import filter_duplicates, apply_suggestion from possibilities import filter_duplicates, apply_suggestion
import Queue import Queue
import re
# Check for n-ary operator in child nodes # Check for n-ary operator in child nodes
...@@ -141,8 +142,6 @@ class Parser(BisonParser): ...@@ -141,8 +142,6 @@ class Parser(BisonParser):
self.possibilities = [] self.possibilities = []
import re
# Replace known keywords with escape sequences. # Replace known keywords with escape sequences.
words = list(Parser.words) words = list(Parser.words)
words.insert(10, '\n') words.insert(10, '\n')
...@@ -389,13 +388,24 @@ class Parser(BisonParser): ...@@ -389,13 +388,24 @@ class Parser(BisonParser):
if option in (1, 2): # rule: FUNCTION_LPAREN exp RPAREN | FUNCTION exp if option in (1, 2): # rule: FUNCTION_LPAREN exp RPAREN | FUNCTION exp
op = values[0].split(' ', 1)[0] op = values[0].split(' ', 1)[0]
if op == 'ln':
return Node('log', values[1], Leaf(E))
if values[1].is_op(OP_COMMA): if values[1].is_op(OP_COMMA):
return Node(op, *values[1]) return Node(op, *values[1])
if op == 'log':
return Node('log', values[1], Leaf(DEFAULT_LOGARITHM_BASE))
m = re.match(r'^log_([0-9]+)', op)
if m:
return Node('log', values[1], Leaf(int(m.group(1))))
return Node(op, values[1]) return Node(op, values[1])
if option == 3: # rule: DERIVATIVE exp if option == 3: # rule: DERIVATIVE exp
op = [k for k, v in OP_MAP.iteritems() if v == OP_DERIV][0] op = [k for k, v in OP_MAP.iteritems() if v == OP_DER][0]
# DERIVATIVE looks like 'd/d*x*' -> extract the 'x' # DERIVATIVE looks like 'd/d*x*' -> extract the 'x'
return Node(op, values[1], Leaf(values[0][-2])) return Node(op, values[1], Leaf(values[0][-2]))
...@@ -412,7 +422,7 @@ class Parser(BisonParser): ...@@ -412,7 +422,7 @@ class Parser(BisonParser):
| bracket_derivative APOSTROPH | bracket_derivative APOSTROPH
""" """
op = [k for k, v in OP_MAP.iteritems() if v == OP_DERIV][0] op = [k for k, v in OP_MAP.iteritems() if v == OP_DER][0]
if option == 0: # rule: LBRACKET exp RBRACKET APOSTROPH if option == 0: # rule: LBRACKET exp RBRACKET APOSTROPH
return Node(op, values[1]) return Node(op, values[1])
...@@ -527,6 +537,8 @@ class Parser(BisonParser): ...@@ -527,6 +537,8 @@ class Parser(BisonParser):
"[" { returntoken(LBRACKET); } "[" { returntoken(LBRACKET); }
"]" { returntoken(RBRACKET); } "]" { returntoken(RBRACKET); }
"'" { returntoken(APOSTROPH); } "'" { returntoken(APOSTROPH); }
log_[0-9]+"*(" { returntoken(FUNCTION_LPAREN); }
log_[0-9]+"*" { returntoken(FUNCTION); }
""" + operators + r""" """ + operators + r"""
"raise" { returntoken(RAISE); } "raise" { returntoken(RAISE); }
"graph" { returntoken(GRAPH); } "graph" { returntoken(GRAPH); }
......
...@@ -221,4 +221,10 @@ class TestNode(RulesTestCase): ...@@ -221,4 +221,10 @@ class TestNode(RulesTestCase):
def test_construct_function(self): def test_construct_function(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(x ^ 2, x)')), 'd/dx (x ^ 2)') self.assertEqual(str(tree('der(x ^ 2, x)')), 'd/dx (x ^ 2)')
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)')
...@@ -7,6 +7,7 @@ from tests.parser import ParserWrapper, run_expressions, line, graph ...@@ -7,6 +7,7 @@ from tests.parser import ParserWrapper, run_expressions, line, graph
from tests.rulestestcase import tree from tests.rulestestcase import tree
from src.rules.goniometry import sin, cos 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
class TestParser(unittest.TestCase): class TestParser(unittest.TestCase):
...@@ -75,3 +76,11 @@ class TestParser(unittest.TestCase): ...@@ -75,3 +76,11 @@ class TestParser(unittest.TestCase):
self.assertEqual(tree('d/dx (x ^ 2 + x)'), der(exp + x, x)) self.assertEqual(tree('d/dx (x ^ 2 + x)'), der(exp + x, x))
self.assertEqual(tree('d/d'), d / d) self.assertEqual(tree('d/d'), d / d)
# FIXME: self.assertEqual(tree('d(x ^ 2)/dx'), der(exp, x)) # FIXME: self.assertEqual(tree('d(x ^ 2)/dx'), der(exp, x))
def test_logarithm(self):
x, g = tree('x, g')
self.assertEqual(tree('log(x, e)'), ln(x))
self.assertEqual(tree('log(x, 10)'), log(x))
self.assertEqual(tree('log(x, 2)'), log(x, 2))
self.assertEqual(tree('log(x, g)'), log(x, g))
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