Commit 17c6fae8 authored by Taddeus Kroes's avatar Taddeus Kroes

Added logical operators.

parent 685f3ea3
...@@ -28,34 +28,36 @@ OP_MUL = 5 ...@@ -28,34 +28,36 @@ OP_MUL = 5
OP_DIV = 6 OP_DIV = 6
OP_POW = 7 OP_POW = 7
OP_SUBSCRIPT = 8 OP_SUBSCRIPT = 8
OP_AND = 9
OP_OR = 10
# N-ary (functions) # N-ary (functions)
OP_INT = 9 OP_INT = 11
OP_INT_INDEF = 10 OP_INT_INDEF = 12
OP_COMMA = 11 OP_COMMA = 13
OP_SQRT = 12 OP_SQRT = 14
OP_DER = 13 OP_DER = 15
OP_LOG = 14 OP_LOG = 16
# Goniometry # Goniometry
OP_SIN = 15 OP_SIN = 17
OP_COS = 16 OP_COS = 18
OP_TAN = 17 OP_TAN = 19
OP_SOLVE = 18 OP_SOLVE = 20
OP_EQ = 19 OP_EQ = 21
OP_POSSIBILITIES = 20 OP_POSSIBILITIES = 22
OP_HINT = 21 OP_HINT = 23
OP_REWRITE_ALL = 22 OP_REWRITE_ALL = 24
OP_REWRITE = 23 OP_REWRITE = 25
# Special identifiers # Special identifiers
PI = 'pi' PI = 'pi'
E = 'e' E = 'e'
INFINITY = 'oo' INFINITY = 'oo'
SPECIAL_TOKENS = [PI, INFINITY] SPECIAL_TOKENS = [PI, E, INFINITY]
# Default base to use in parsing 'log(...)' # Default base to use in parsing 'log(...)'
DEFAULT_LOGARITHM_BASE = 10 DEFAULT_LOGARITHM_BASE = 10
...@@ -75,6 +77,8 @@ OP_MAP = { ...@@ -75,6 +77,8 @@ OP_MAP = {
'/': OP_DIV, '/': OP_DIV,
'^': OP_POW, '^': OP_POW,
'_': OP_SUBSCRIPT, '_': OP_SUBSCRIPT,
'^^': OP_AND,
'vv': OP_OR,
'sin': OP_SIN, 'sin': OP_SIN,
'cos': OP_COS, 'cos': OP_COS,
'tan': OP_TAN, 'tan': OP_TAN,
...@@ -103,6 +107,8 @@ TOKEN_MAP = { ...@@ -103,6 +107,8 @@ TOKEN_MAP = {
OP_DIV: 'DIVIDE', OP_DIV: 'DIVIDE',
OP_POW: 'POW', OP_POW: 'POW',
OP_SUBSCRIPT: 'SUB', OP_SUBSCRIPT: 'SUB',
OP_AND: 'AND',
OP_OR: 'OR',
OP_SQRT: 'FUNCTION', OP_SQRT: 'FUNCTION',
OP_SIN: 'FUNCTION', OP_SIN: 'FUNCTION',
OP_COS: 'FUNCTION', OP_COS: 'FUNCTION',
...@@ -226,6 +232,12 @@ class ExpressionBase(object): ...@@ -226,6 +232,12 @@ class ExpressionBase(object):
def __pos__(self): def __pos__(self):
return self.reduce_negation() return self.reduce_negation()
def __and__(self, other):
return ExpressionNode(OP_AND, self, to_expression(other))
def __or__(self, other):
return ExpressionNode(OP_OR, self, to_expression(other))
def reduce_negation(self, n=1): def reduce_negation(self, n=1):
"""Remove n negation flags from the node.""" """Remove n negation flags from the node."""
assert self.negated assert self.negated
......
...@@ -87,10 +87,12 @@ class Parser(BisonParser): ...@@ -87,10 +87,12 @@ class Parser(BisonParser):
precedences = ( precedences = (
('left', ('COMMA', )), ('left', ('COMMA', )),
('right', ('INTEGRAL', 'DERIVATIVE')), ('right', ('INTEGRAL', 'DERIVATIVE')),
('left', ('OR', )),
('left', ('AND', )),
('left', ('EQ', )),
('left', ('MINUS', 'PLUS')), ('left', ('MINUS', 'PLUS')),
('left', ('TIMES', 'DIVIDE')), ('left', ('TIMES', 'DIVIDE')),
('right', ('FUNCTION', )), ('right', ('FUNCTION', )),
('left', ('EQ', )),
('left', ('NEG', )), ('left', ('NEG', )),
('right', ('POW', )), ('right', ('POW', )),
('left', ('SUB', )), ('left', ('SUB', )),
...@@ -225,6 +227,9 @@ class Parser(BisonParser): ...@@ -225,6 +227,9 @@ class Parser(BisonParser):
for i, keyword in enumerate(words): for i, keyword in enumerate(words):
data = data.replace(chr(i), keyword) data = data.replace(chr(i), keyword)
# Fix TIMES operator next to OR
data = re.sub(r'\*?vv\*?', 'vv', data)
if self.verbose and data_before != data: # pragma: nocover if self.verbose and data_before != data: # pragma: nocover
print 'hook_read_after() modified the input data:' print 'hook_read_after() modified the input data:'
print 'before:', repr(data_before) print 'before:', repr(data_before)
...@@ -527,14 +532,16 @@ class Parser(BisonParser): ...@@ -527,14 +532,16 @@ class Parser(BisonParser):
| exp TIMES exp | exp TIMES exp
| exp DIVIDE exp | exp DIVIDE exp
| exp EQ exp | exp EQ exp
| exp AND exp
| exp OR exp
| exp MINUS exp | exp MINUS exp
| power | power
""" """
if 0 <= option < 4: # rule: exp {PLUS,TIMES,DIVIDE,EQ} exp if 0 <= option <= 5: # rule: exp {PLUS,TIMES,DIVIDE,EQ,AND,OR} exp
return Node(values[1], values[0], values[2]) return Node(values[1], values[0], values[2])
if option == 4: # rule: exp MINUS exp if option == 6: # rule: exp MINUS exp
node = values[2] node = values[2]
# Add negation to the left-most child # Add negation to the left-most child
...@@ -549,7 +556,7 @@ class Parser(BisonParser): ...@@ -549,7 +556,7 @@ class Parser(BisonParser):
return Node(OP_ADD, values[0], values[2]) return Node(OP_ADD, values[0], values[2])
if option == 5: # rule: power if option == 7: # rule: power
return Node(OP_POW, *values[0]) return Node(OP_POW, *values[0])
raise BisonSyntaxError('Unsupported option %d in target "%s".' raise BisonSyntaxError('Unsupported option %d in target "%s".'
...@@ -573,8 +580,9 @@ class Parser(BisonParser): ...@@ -573,8 +580,9 @@ class Parser(BisonParser):
functions = [] functions = []
for token in SPECIAL_TOKENS: for token in SPECIAL_TOKENS:
operators += '"%s"%s{ returntoken(IDENTIFIER); }\n' \ if len(token) > 1:
% (token, ' ' * (8 - len(token))) operators += '"%s"%s{ returntoken(IDENTIFIER); }\n' \
% (token, ' ' * (8 - len(token)))
for op_str, op in OP_MAP.iteritems(): for op_str, op in OP_MAP.iteritems():
if TOKEN_MAP[op] == 'FUNCTION': if TOKEN_MAP[op] == 'FUNCTION':
......
...@@ -38,6 +38,14 @@ class TestParser(unittest.TestCase): ...@@ -38,6 +38,14 @@ class TestParser(unittest.TestCase):
self.assertNotEqual(possibilities1, possibilities2) self.assertNotEqual(possibilities1, possibilities2)
def test_binary(self):
a, b, c = tree('a, b, c')
self.assertEqual(tree('a ^^ b'), a & b)
self.assertEqual(tree('a vv b'), a | b)
self.assertEqual(tree('a vv b vv c'), (a | b) | c)
self.assertEqual(tree('a vv b ^^ c'), a | (b & c))
def test_preprocessor(self): def test_preprocessor(self):
self.assertEqual(tree('ab'), tree('a * b')) self.assertEqual(tree('ab'), tree('a * b'))
self.assertEqual(tree('abc'), tree('a * b * c')) self.assertEqual(tree('abc'), tree('a * b * c'))
......
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