Skip to content

Commit c35a88c

Browse files
committed
[fixes] Fixed last crucial cases
1 parent 5854791 commit c35a88c

File tree

5 files changed

+202
-65
lines changed

5 files changed

+202
-65
lines changed

libs/element.py

Lines changed: 133 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,20 @@ def __init__(self, expression, func=None):
5959
:param expression: mathematical expression as string
6060
"""
6161
# Validate on expression and raise exception if not true
62-
expression = expression.replace(" ", "")
62+
63+
# if " " in expression:
64+
# raise ExpressionFormatException("Expression should not de with spaces")
65+
66+
# expression = expression.replace(" ", "")
67+
6368
if not expression:
6469
raise NoExpressionException("The expression was not passed")
6570

71+
# if expression.startswith("--") or expression.startswith("=") or expression.startswith("+"):
72+
# raise ExpressionFormatException("The expression bad format")
73+
# if expression.endswith("-"):
74+
# raise ExpressionFormatException("The expression bad format")
75+
6676
# TODO: comment
6777
self._mathematical_functions = {
6878
name: val for name, val in getmembers(math) if type(val).__name__ == "builtin_function_or_method"
@@ -76,16 +86,16 @@ def __init__(self, expression, func=None):
7686

7787
self._func = None
7888
if func:
89+
func = func.strip()
7990
if func not in self._mathematical_functions:
80-
raise UnsupportedMathematicalFunctionException("We do not support '{}' function".format(self._func))
91+
raise UnsupportedMathematicalFunctionException("We do not support '{}' function".format(func))
8192
self._func = self._mathematical_functions.get(func)
8293

8394
self._expression = []
8495

8596
bracket_level = 0
8697
item = []
8798
last_mathematical_action = None
88-
bracket_closed = False
8999
bracket_content = []
90100
self._comparison_operation = False
91101
self._multivalue = False
@@ -116,19 +126,32 @@ def __init__(self, expression, func=None):
116126
else:
117127
raise ExpressionFormatException("After comparison operation expression or number are expected")
118128

119-
# Look for multivalue expression
120-
if "(" not in expression and ")" not in expression and "," in expression:
121-
if not self._func:
122-
raise ExpressionFormatException("Commas allowed only in function calls.")
123-
parts = expression.split(",")
124-
self._expression = [Element(i) for i in parts]
125-
self._multivalue = True
129+
# Look for commas in expression
130+
start_index = 0
131+
multivalue_items = []
132+
for i, c in enumerate(expression):
133+
# increase bracket level
134+
if c == "(":
135+
bracket_level += 1
136+
# decrease bracket level
137+
elif c == ")":
138+
bracket_level -= 1
139+
elif c == "," and bracket_level == 0:
140+
self._multivalue = True
141+
multivalue_items.append(Element(expression[start_index:i]))
142+
start_index = i + 1
143+
if self._multivalue:
144+
self._expression = multivalue_items
145+
self._expression.append(Element(expression[start_index:]))
126146
return
127147

128148
# Validate format expression and raise exception if it is not valid
129149
item = []
150+
bracket_closed = False
130151
for i in expression:
131-
if bracket_closed:
152+
if bracket_closed and bracket_level == 0:
153+
if i == " ":
154+
continue
132155
if i not in self.MATH_ACTIONS and i != ")":
133156
raise ExpressionFormatException("After bracket closed 'math sign' or "
134157
"another bracket close are expected")
@@ -163,10 +186,11 @@ def __init__(self, expression, func=None):
163186
else:
164187
if i in self.MATH_ACTIONS:
165188
if item:
166-
item = "".join(item)
167-
if item in self._mathematical_constants:
168-
item = self._mathematical_constants[item]
169-
self._expression.append(float(item))
189+
item = "".join(item).strip()
190+
if item:
191+
if item in self._mathematical_constants:
192+
item = self._mathematical_constants[item]
193+
self._expression.append(float(item))
170194
item = []
171195

172196
# Handle double mathematical operation
@@ -209,6 +233,8 @@ def _calculate_boolean_expression(self):
209233
boolean_value = True
210234
for i, v in enumerate(self._expression):
211235
if isinstance(v, str):
236+
if i <= 0:
237+
raise ExpressionFormatException("Comparison could be at the first position")
212238
if v == ">=":
213239
if not self._expression[i - 1] >= self._expression[i + 1]:
214240
boolean_value = False
@@ -243,17 +269,16 @@ def _calculate_mathematical_expression(self):
243269
first_negative = True
244270
del self._expression[0]
245271

246-
# Calculate power mathematical operation
247-
self._expression.reverse()
248-
while True:
249-
try:
250-
index = self._expression.index("^")
251-
self._expression.pop(index)
252-
power = self._expression.pop(index - 1)
253-
self._expression[index - 1] **= power
254-
except ValueError:
255-
break
256-
self._expression.reverse()
272+
i = len(self._expression) - 1
273+
while i >= 0:
274+
el = self._expression[i]
275+
if el == "^":
276+
self._expression.pop(i)
277+
power = self._expression.pop(i)
278+
if power == "-":
279+
power = -self._expression.pop(i)
280+
self._expression[i - 1] **= power
281+
i -= 1
257282

258283
# Calculate high priority mathematical operations
259284
new_expression = []
@@ -288,8 +313,8 @@ def _calculate_mathematical_expression(self):
288313
if isinstance(i, str):
289314
if i in ("+", "-",):
290315
operation = i
291-
else:
292-
raise UnsupportedMathematicalOperationException("We do not support '{}' operation".format(i))
316+
# else:
317+
# raise UnsupportedMathematicalOperationException("We do not support '{}' operation".format(i))
293318
elif operation:
294319
if operation == "+":
295320
value += i
@@ -312,35 +337,94 @@ def value(self):
312337
"""
313338

314339
# Validate mathematical operations and calculate nested expressions
315-
last_operation = None
340+
# i = len(self._expression) - 1
341+
# last_operation = None
342+
# print(">>", self._expression)
343+
# while i >= 0:
344+
# el = self._expression[i]
345+
# if isinstance(el, Element):
346+
# self._expression[i] = el.value()
347+
# last_operation = None
348+
# elif isinstance(el, str):
349+
316350
for i, v in enumerate(self._expression):
317351
if isinstance(v, Element):
318352
self._expression[i] = v.value()
319353
if isinstance(v, str):
320-
if v not in ("<=", ">=", "==", "!=", "<>", "**", "//"):
354+
if v.startswith("-"):
321355
if len(v) > 1:
322356
if len(v) % 2 == 0:
323357
self._expression[i] = "+"
324358
else:
325359
self._expression[i] = "-"
360+
if v.startswith("+"):
361+
self._expression[i] = "+"
326362

327-
if last_operation and v in ("+", "-",):
328-
if last_operation == "+" and v == "-":
329-
self._expression[i] = "-"
330-
del self._expression[i - 1]
331-
elif last_operation == "-" and v == "+":
332-
self._expression[i] = "-"
333-
del self._expression[i]
334-
elif last_operation and v in self.MATH_ACTIONS:
335-
raise DoubleOperationException("'{so}' operation follows '{fo}'".format(
336-
so=last_operation,
337-
fo=v
338-
))
339-
340-
if v in self.MATH_ACTIONS:
341-
last_operation = v
342-
else:
363+
expression = []
364+
last_operation = None
365+
sign = None
366+
for i, v in enumerate(self._expression):
367+
if isinstance(v, str):
368+
if last_operation:
369+
if last_operation in ("+", "-",):
370+
if v in ("+", "-"):
371+
if last_operation == v:
372+
last_operation = "+"
373+
else:
374+
last_operation = "-"
375+
else:
376+
raise DoubleOperationException("'{so}' operation follows '{fo}'".format(
377+
so=last_operation,
378+
fo=v
379+
))
380+
else:
381+
if v not in ("+", "-"):
382+
raise DoubleOperationException("'{so}' operation follows '{fo}'".format(
383+
so=last_operation,
384+
fo=v
385+
))
386+
if sign:
387+
if sign == v:
388+
sign = "+"
389+
else:
390+
sign = "-"
391+
else:
392+
sign = v
393+
else:
394+
last_operation = v
395+
continue
396+
397+
if last_operation:
398+
expression.append(last_operation)
343399
last_operation = None
400+
if sign == "-":
401+
v = -v
402+
sign = None
403+
expression.append(v)
404+
405+
if last_operation or sign:
406+
raise ExpressionFormatException("Expression finishes with mathematical operation.")
407+
408+
self._expression = expression
409+
410+
# for i, v in enumerate(self._expression):
411+
# if isinstance(v, str):
412+
# if last_operation and v in ("+", "-",):
413+
# if last_operation == "+" and v == "-":
414+
# self._expression[i] = "-"
415+
# elif last_operation == "-" and v == "+":
416+
# self._expression[i] = "-"
417+
# del self._expression[i - 1]
418+
# elif last_operation and v in self.MATH_ACTIONS:
419+
# raise DoubleOperationException("'{so}' operation follows '{fo}'".format(
420+
# so=last_operation,
421+
# fo=v
422+
# ))
423+
424+
# if v in self.MATH_ACTIONS:
425+
# last_operation = v
426+
# else:
427+
# last_operation = None
344428

345429
# Evaluate comparison expression
346430
if self._comparison_operation:
@@ -351,9 +435,7 @@ def value(self):
351435
try:
352436
return self._func(*self._expression)
353437
except TypeError:
354-
raise ExpressionFormatException("Expected 2 arguments, got 3: '{}'".format(self._func))
438+
raise ExpressionFormatException("Expected 2 arguments: '{}'".format(self._func))
355439

356-
try:
357-
return self._calculate_mathematical_expression()
358-
except TypeError:
359-
raise UnsupportedMathematicalOperationException("We do not support '{}' operation".format(i))
440+
# print(self._expression)
441+
return self._calculate_mathematical_expression()

pycalc

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,9 @@ if __name__ == '__main__':
1515
print(expression.value())
1616
except BaseExpressionException as exc:
1717
print("ERROR: {}".format(str(exc)))
18-
# except ValueError as exc:
19-
# print("ERROR: {}".format(str(exc)))
20-
# except TypeError as exc:
21-
# print("ERROR: {}".format(str(exc)))
18+
except ValueError as exc:
19+
print("ERROR: {}".format(str(exc)))
20+
except TypeError as exc:
21+
print("ERROR: {}".format(str(exc)))
2222
# expression = Element(expression=args.EXPRESSION)
2323
# print(expression.value())
24-
# print(expression.booleen_value())

pycalc_checker.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from termcolor import colored
66

77

8-
PYCALC_UTIL_NAME = "pycalc"
8+
PYCALC_UTIL_NAME = "./pycalc"
99
RETURN_CODE = 0
1010

1111

tests/test_negatives.py

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
from unittest import TestCase
22

33
from libs.element import Element, NoExpressionException, ExpressionFormatException, BracketsAreNotBalanced, \
4-
DoubleOperationException, ExpressionFormatException, UnsupportedMathematicalOperationException
4+
DoubleOperationException, ExpressionFormatException, UnsupportedMathematicalOperationException, \
5+
UnsupportedMathematicalFunctionException
56

67

78
class TestNegativesElementSimple(TestCase):
@@ -53,10 +54,49 @@ def test_unsupported_comparison_operation(self):
5354

5455
def test_unsupported_operation(self):
5556
with self.assertRaises(DoubleOperationException):
56-
expression = Element(expression="1*/5*3")
57+
expression = Element(expression="1+/5*3")
5758
expression.value()
5859

59-
# def test_unsupported_trigonometric_operation(self):
60-
# with self.assertRaises(UnsupportedMathematicalOperationException):
61-
# expression = Element(expression="erf(10)")
62-
# expression.value()
60+
def test_unsupported_trigonometric_operation(self):
61+
with self.assertRaises(UnsupportedMathematicalFunctionException):
62+
expression = Element(expression="iii(10)")
63+
expression.value()
64+
65+
def test_not_mathematical_constant(self):
66+
with self.assertRaises(ExpressionFormatException):
67+
expression = Element(expression="li")
68+
expression.value()
69+
70+
def test_exponentiation(self):
71+
with self.assertRaises(UnsupportedMathematicalOperationException):
72+
expression = Element(expression="2**6")
73+
expression.value()
74+
75+
def test_expected_arguments(self):
76+
with self.assertRaises(ExpressionFormatException):
77+
expression = Element(expression="pow(2,5,6)")
78+
expression.value()
79+
80+
def test_comma_without_func(self):
81+
with self.assertRaises(ExpressionFormatException):
82+
expression = Element(expression="2+3,4")
83+
expression.value()
84+
85+
# def test_calculate_mathematical_expression(self):
86+
# # with self.assertRaises(UnsupportedMathematicalOperationException):
87+
# expression = Element(expression="")
88+
# expression.value()
89+
def test_bad_expression(self):
90+
with self.assertRaises(ExpressionFormatException):
91+
expression = Element(expression="--+1-")
92+
expression.value()
93+
94+
def test_expression_bad(self):
95+
with self.assertRaises(ExpressionFormatException):
96+
expression = Element(expression="2-")
97+
expression.value()
98+
99+
def test_expression_with_space(self):
100+
with self.assertRaises(ExpressionFormatException):
101+
expression = Element(expression="2- 3+")
102+
expression.value()

tests/test_simple.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,26 @@ def test_exponentiation(self):
4141
expression = Element(expression="2*3//2")
4242
self.assertEqual(expression.value(), 3)
4343

44-
45-
4644
def test_str(self):
4745
expression = Element(expression="2+3*((5-1)-2)")
4846
self.assertTrue(str(expression), 8)
4947

48+
def test_mathematical_constant(self):
49+
expression = Element(expression="pi")
50+
self.assertEqual(expression.value(), 3.141592653589793)
51+
52+
def test_mathematical_power(self):
53+
expression = Element(expression="2^3")
54+
self.assertEqual(expression.value(), 8)
5055

56+
def test_two_mathematical_constant(self):
57+
expression = Element(expression="pi*e")
58+
self.assertEqual(expression.value(), 8.539734222673566)
59+
60+
def test_unary_operation(self):
61+
expression = Element(expression="2*4-----3+++4--3")
62+
self.assertEqual(expression.value(), 12)
63+
64+
def test_various_unary_operation(self):
65+
expression = Element(expression="2*4++4-+++4---3++2----1")
66+
self.assertEqual(expression.value(), 8)

0 commit comments

Comments
 (0)