From 1a3ce42990d80996b731508d3316cdac5397e03a Mon Sep 17 00:00:00 2001 From: Stacy Date: Sat, 20 Oct 2018 16:45:22 +0300 Subject: [PATCH 01/49] Elementary calc --- calc.py | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 calc.py diff --git a/calc.py b/calc.py new file mode 100644 index 0000000..470981e --- /dev/null +++ b/calc.py @@ -0,0 +1,63 @@ +binary_operations = { + "+": 0, + "-": 0, + "*": 1, + "/": 1, + "//": 1, + "%": 1, + "^": 2, +} + + +def calc(expression): + stack = [] + expression = to_postfix(expression) + for i in expression: + if i.isnumeric(): + stack.append(i) + else: + b = int(stack.pop()) + a = int(stack.pop()) + if i == '+': + stack.append(a + b) + if i == '-': + stack.append(a - b) + if i == '*': + stack.append(a * b) + if i == '/': + stack.append(a / b) + if i == '//': + stack.append(a // b) + if i == '%': + stack.append(a % b) + if i == '^': + stack.append(a ** b) + return stack.pop() + + +def to_postfix(expression): + res = [] + stack = [] + for i in expression.split(): + if i.isnumeric(): + res.append(i) + elif i == '(': + stack.append(i) + elif i == ')': + while stack[-1] != '(': + res.append(stack.pop()) + stack.pop() + elif i in binary_operations: + if stack and stack[-1] in binary_operations and binary_operations[stack[-1]] >= binary_operations[i]: + while stack and binary_operations[stack[-1]] >= binary_operations[i]: + res.append(stack.pop()) + stack.append(i) + else: + stack.append(i) + for i in reversed(stack): + res.append(i) + return res + + +a = "2 ^ 3 + 2" +print(calc(a)) From 2f46ae18996ec47b276eacff2d51867ccf92d66e Mon Sep 17 00:00:00 2001 From: Stacy Date: Sun, 21 Oct 2018 12:51:22 +0300 Subject: [PATCH 02/49] Add regex and insert '*' --- calc.py | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/calc.py b/calc.py index 470981e..3089ba4 100644 --- a/calc.py +++ b/calc.py @@ -1,3 +1,6 @@ +import re + + binary_operations = { "+": 0, "-": 0, @@ -9,13 +12,42 @@ } +ops_list = dict(log=1, log10=1, abs=1, sqrt=1, sin=1, asin=1, cos=1, acos=1, hypot=1, tan=1, atan=1, atan2=1, ceil=1, + copysign=1, fabs=1, factorial=1, floor=1, fmod=1, frexp=1, ldexp=1, fsum=1, isfinite=1, isinf=1, + isnan=1, modf=1, trunc=1, exp=1, expm1=1, log1p=1, log2=1, pow=1, degrees=1, radians=1, cosh=1, sinh=1, + tanh=1, acosh=1, asinh=1, atanh=1, erf=1, erfc=1, gamma=1, lgamma=1, pi=1, e=1, inv=0, gcd=1, isclose=1, + isdexp=1, tau=1, inf=1, nan=1, + ) + + +def correct_expression(expression): + re_expr = re.findall('log10|log2|log1p|expm1|atan2|\//|\d+\.\d+|\d+|\W|\w+',expression) + i = 0 + _len = lambda x: len(x) + for i in range(_len(re_expr)): + if re_expr[i].isdigit() and re_expr[i + 1] == '(': + re_expr.insert(i + 1, '*') + print('a') + elif re_expr[i].isdigit() and re_expr[i + 1] in ops_list: + re_expr.insert(i + 1, '*') + print('q') + print(re_expr[i+2]) + elif re_expr[i] == ')' and re_expr[i + 1][0].isdigit(): + re_expr.insert(i + 1, '*') + i += 1 + print('b') + if i + 2 >= _len(re_expr): + break + return re_expr + + def calc(expression): stack = [] expression = to_postfix(expression) for i in expression: if i.isnumeric(): stack.append(i) - else: + elif i in binary_operations: b = int(stack.pop()) a = int(stack.pop()) if i == '+': @@ -38,7 +70,7 @@ def calc(expression): def to_postfix(expression): res = [] stack = [] - for i in expression.split(): + for i in correct_expression(expression): if i.isnumeric(): res.append(i) elif i == '(': @@ -59,5 +91,5 @@ def to_postfix(expression): return res -a = "2 ^ 3 + 2" +a = "2^3+2" print(calc(a)) From 2b327a72751d6df93cac1011ffea81fa39143c2d Mon Sep 17 00:00:00 2001 From: Stacy Date: Tue, 30 Oct 2018 21:27:17 +0300 Subject: [PATCH 03/49] step of calc functions --- calc.py | 120 ++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 94 insertions(+), 26 deletions(-) diff --git a/calc.py b/calc.py index 3089ba4..0a32f4b 100644 --- a/calc.py +++ b/calc.py @@ -13,13 +13,16 @@ ops_list = dict(log=1, log10=1, abs=1, sqrt=1, sin=1, asin=1, cos=1, acos=1, hypot=1, tan=1, atan=1, atan2=1, ceil=1, - copysign=1, fabs=1, factorial=1, floor=1, fmod=1, frexp=1, ldexp=1, fsum=1, isfinite=1, isinf=1, - isnan=1, modf=1, trunc=1, exp=1, expm1=1, log1p=1, log2=1, pow=1, degrees=1, radians=1, cosh=1, sinh=1, - tanh=1, acosh=1, asinh=1, atanh=1, erf=1, erfc=1, gamma=1, lgamma=1, pi=1, e=1, inv=0, gcd=1, isclose=1, + copysign=2, fabs=1, factorial=1, floor=1, fmod=2, frexp=1, ldexp=2, fsum=1, isfinite=1, isinf=1, + isnan=1, modf=1, trunc=1, exp=1, expm1=1, log1p=1, log2=1, pow=2, degrees=1, radians=1, cosh=1, sinh=1, + tanh=1, acosh=1, asinh=1, atanh=1, erf=1, erfc=1, gamma=1, lgamma=1, pi=1, e=1, inv=0, gcd=2, isclose=5, isdexp=1, tau=1, inf=1, nan=1, ) + + + def correct_expression(expression): re_expr = re.findall('log10|log2|log1p|expm1|atan2|\//|\d+\.\d+|\d+|\W|\w+',expression) i = 0 @@ -41,55 +44,120 @@ def correct_expression(expression): return re_expr +def ops_arg(expression): + print('ops:', id(expression)) + ops = expression.pop(0) + res = [] + arg = [] + point = 1 + while expression: + if expression[0] == ',': + res.append(arg.copy()) + arg.clear() + elif expression[0] == ')': + point -= 1 + if point == 0: + expression.remove(expression[0]) + res.append(arg) + return ops, res + else: + arg.append(expression[0]) + elif expression[0] in ops_list: + point += 1 + arg.append(expression[0]) + else: + arg.append(expression[0]) + expression.remove(expression[0]) + + +def is_float(val): + try: + float(val) + return True + except ValueError: + return False + def calc(expression): stack = [] - expression = to_postfix(expression) - for i in expression: - if i.isnumeric(): - stack.append(i) + print('calc:', id(expression)) + while expression: + i = expression[0] + if is_float(i): + stack.append(float(i)) + expression.remove((i)) elif i in binary_operations: - b = int(stack.pop()) - a = int(stack.pop()) - if i == '+': - stack.append(a + b) - if i == '-': - stack.append(a - b) - if i == '*': - stack.append(a * b) - if i == '/': - stack.append(a / b) - if i == '//': - stack.append(a // b) - if i == '%': - stack.append(a % b) - if i == '^': - stack.append(a ** b) + if len(stack)>1 and isinstance(stack[-1],(int,float)) and isinstance(stack[-2],(int,float)): + b = stack.pop() + a = stack.pop() + if i == '+': + stack.append(a + b) + if i == '-': + stack.append(a - b) + if i == '*': + stack.append(a * b) + if i == '/': + stack.append(a / b) + if i == '//': + stack.append(a // b) + if i == '%': + stack.append(a % b) + if i == '^': + stack.append(a ** b) + else: + stack.append(i) + expression.remove((i)) + if i in ops_list: + arg = [] + ops, arg0 = ops_arg(expression) + while arg0: + arg.append(calc(arg0.pop())) + if ops == 'pow': + stack.append(pow(*arg)) + elif ops == 'abs': + stack.append(abs(*arg)) + print(stack) return stack.pop() def to_postfix(expression): res = [] stack = [] + ops_bracket = [] for i in correct_expression(expression): if i.isnumeric(): res.append(i) + elif i in ops_list: + res.append(i) elif i == '(': + if res[-1] in ops_list: + ops_bracket.append(i) stack.append(i) elif i == ')': while stack[-1] != '(': res.append(stack.pop()) stack.pop() + if ops_bracket: + ops_bracket.pop() + res.append(i) elif i in binary_operations: if stack and stack[-1] in binary_operations and binary_operations[stack[-1]] >= binary_operations[i]: - while stack and binary_operations[stack[-1]] >= binary_operations[i]: + while stack and stack[-1] in binary_operations and binary_operations[stack[-1]] >= binary_operations[i]: res.append(stack.pop()) stack.append(i) else: stack.append(i) + elif i == ',': + while stack[-1] != '(': + res.append(stack.pop()) + res.append(i) + for i in reversed(stack): res.append(i) return res + #a = calc(to_postfix('abs(2)+3*2')) +print('res', calc(to_postfix('abs(-2)+3*2'))) +#print(calc(to_postfix('pow(abs(2+1-1),abs(56-53)+1)+3*(3-4)'))) +#print(calc(to_postfix('pow(abs(2+1-1),abs(56-53)+1)+3*(3-4)'))) + -a = "2^3+2" -print(calc(a)) From a4fb07ea62d0a4015fa0b45f5cd747494175771a Mon Sep 17 00:00:00 2001 From: Stacy Date: Tue, 30 Oct 2018 22:22:04 +0300 Subject: [PATCH 04/49] non-full calc functions --- calc.py | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/calc.py b/calc.py index 0a32f4b..d3acc8b 100644 --- a/calc.py +++ b/calc.py @@ -26,9 +26,12 @@ def correct_expression(expression): re_expr = re.findall('log10|log2|log1p|expm1|atan2|\//|\d+\.\d+|\d+|\W|\w+',expression) i = 0 + if re_expr[0] == '-' and is_float(re_expr[1]): + re_expr[1] *= -1 + re_expr.pop(0) _len = lambda x: len(x) for i in range(_len(re_expr)): - if re_expr[i].isdigit() and re_expr[i + 1] == '(': + if is_float(re_expr[i]) and re_expr[i + 1] == '(': re_expr.insert(i + 1, '*') print('a') elif re_expr[i].isdigit() and re_expr[i + 1] in ops_list: @@ -39,6 +42,10 @@ def correct_expression(expression): re_expr.insert(i + 1, '*') i += 1 print('b') + elif re_expr[i] == '(' and re_expr[i+1] == '-' and is_float(re_expr[i+2]): + re_expr[i+2] *= -1 + re_expr.pop(i+1) + if i + 2 >= _len(re_expr): break return re_expr @@ -84,24 +91,24 @@ def calc(expression): i = expression[0] if is_float(i): stack.append(float(i)) - expression.remove((i)) + expression.remove(i) elif i in binary_operations: if len(stack)>1 and isinstance(stack[-1],(int,float)) and isinstance(stack[-2],(int,float)): b = stack.pop() a = stack.pop() if i == '+': stack.append(a + b) - if i == '-': + elif i == '-': stack.append(a - b) - if i == '*': + elif i == '*': stack.append(a * b) - if i == '/': + elif i == '/': stack.append(a / b) - if i == '//': + elif i == '//': stack.append(a // b) - if i == '%': + elif i == '%': stack.append(a % b) - if i == '^': + elif i == '^': stack.append(a ** b) else: stack.append(i) @@ -124,7 +131,7 @@ def to_postfix(expression): stack = [] ops_bracket = [] for i in correct_expression(expression): - if i.isnumeric(): + if is_float(i): res.append(i) elif i in ops_list: res.append(i) @@ -156,8 +163,8 @@ def to_postfix(expression): return res #a = calc(to_postfix('abs(2)+3*2')) -print('res', calc(to_postfix('abs(-2)+3*2'))) -#print(calc(to_postfix('pow(abs(2+1-1),abs(56-53)+1)+3*(3-4)'))) +#print('res', calc(to_postfix('abs(-2)+3*2'))) +print(calc(to_postfix('pow(abs(2+1-1),abs(56-53)+1)+3*(3-4)'))) #print(calc(to_postfix('pow(abs(2+1-1),abs(56-53)+1)+3*(3-4)'))) From 22ad3d719bd1259792aaa6e04fa1c707ee281e41 Mon Sep 17 00:00:00 2001 From: Stacy Date: Sat, 3 Nov 2018 21:31:59 +0300 Subject: [PATCH 05/49] calc with comparison --- calc.py | 75 ++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 61 insertions(+), 14 deletions(-) diff --git a/calc.py b/calc.py index d3acc8b..ca6f819 100644 --- a/calc.py +++ b/calc.py @@ -1,5 +1,5 @@ import re - +import math binary_operations = { "+": 0, @@ -15,16 +15,32 @@ ops_list = dict(log=1, log10=1, abs=1, sqrt=1, sin=1, asin=1, cos=1, acos=1, hypot=1, tan=1, atan=1, atan2=1, ceil=1, copysign=2, fabs=1, factorial=1, floor=1, fmod=2, frexp=1, ldexp=2, fsum=1, isfinite=1, isinf=1, isnan=1, modf=1, trunc=1, exp=1, expm1=1, log1p=1, log2=1, pow=2, degrees=1, radians=1, cosh=1, sinh=1, - tanh=1, acosh=1, asinh=1, atanh=1, erf=1, erfc=1, gamma=1, lgamma=1, pi=1, e=1, inv=0, gcd=2, isclose=5, - isdexp=1, tau=1, inf=1, nan=1, + tanh=1, acosh=1, asinh=1, atanh=1, erf=1, erfc=1, gamma=1, lgamma=1, inv=0, gcd=2, isclose=5, + isdexp=1, ) +constants = { + "pi", + "e", + "tau", + "inf", + "nan", +} + +comparison_operators = { + "<", + "<=", + "==", + "!=", + ">=", + ">", +} def correct_expression(expression): - re_expr = re.findall('log10|log2|log1p|expm1|atan2|\//|\d+\.\d+|\d+|\W|\w+',expression) + re_expr = re.findall('<=|==|!=|>=|log10|log2|log1p|expm1|atan2|\//|\d+\.\d+|\d+|\W|\w+',expression) i = 0 if re_expr[0] == '-' and is_float(re_expr[1]): re_expr[1] *= -1 @@ -43,7 +59,7 @@ def correct_expression(expression): i += 1 print('b') elif re_expr[i] == '(' and re_expr[i+1] == '-' and is_float(re_expr[i+2]): - re_expr[i+2] *= -1 + re_expr[i+2] =re_expr[i+1] + re_expr[i+2] re_expr.pop(i+1) if i + 2 >= _len(re_expr): @@ -51,8 +67,7 @@ def correct_expression(expression): return re_expr -def ops_arg(expression): - print('ops:', id(expression)) +def get_arguments(expression): ops = expression.pop(0) res = [] arg = [] @@ -84,14 +99,43 @@ def is_float(val): except ValueError: return False + def calc(expression): stack = [] - print('calc:', id(expression)) while expression: i = expression[0] if is_float(i): stack.append(float(i)) expression.remove(i) + elif i in comparison_operators: + operator = expression.pop(0) + a = stack.pop() + b = calc(expression) + if operator == '<': + res = a < b + elif operator == '<=': + res = a <= b + elif operator == '==': + res = a == b + elif operator == '!=': + res = a != b + elif operator == '>=': + res = a >= b + elif operator == '>': + res = a>b + stack.append(res) + elif i in constants: + if i == "pi": + stack.append(math.pi) + elif i == "e": + stack.append(math.e) + elif i == "tau": + stack.append(math.tau) + elif i == "inf": + stack.append(math.inf) + elif i == "nan": + stack.append(math.nan) + expression.remove(i) elif i in binary_operations: if len(stack)>1 and isinstance(stack[-1],(int,float)) and isinstance(stack[-2],(int,float)): b = stack.pop() @@ -115,14 +159,14 @@ def calc(expression): expression.remove((i)) if i in ops_list: arg = [] - ops, arg0 = ops_arg(expression) + ops, arg0 = get_arguments(expression) while arg0: arg.append(calc(arg0.pop())) if ops == 'pow': stack.append(pow(*arg)) elif ops == 'abs': stack.append(abs(*arg)) - print(stack) + print(stack) return stack.pop() @@ -131,9 +175,11 @@ def to_postfix(expression): stack = [] ops_bracket = [] for i in correct_expression(expression): - if is_float(i): + if is_float(i) or i in ops_list or i in constants: res.append(i) - elif i in ops_list: + elif i in comparison_operators: + while stack: + res.append(stack.pop()) res.append(i) elif i == '(': if res[-1] in ops_list: @@ -163,8 +209,9 @@ def to_postfix(expression): return res #a = calc(to_postfix('abs(2)+3*2')) -#print('res', calc(to_postfix('abs(-2)+3*2'))) +print('res', calc(to_postfix('abs(-2)+3*2'))) +print(calc(to_postfix('pow(abs(3),abs(56-53)+1)+3*(3-4)+pi'))) print(calc(to_postfix('pow(abs(2+1-1),abs(56-53)+1)+3*(3-4)'))) -#print(calc(to_postfix('pow(abs(2+1-1),abs(56-53)+1)+3*(3-4)'))) +print(calc(to_postfix("2+3-5+6<=6"))) From 176ab6c8e67a68166aa880b1989d53baed9d6941 Mon Sep 17 00:00:00 2001 From: Stacy Date: Sun, 4 Nov 2018 16:06:58 +0300 Subject: [PATCH 06/49] Add test for bad brackets --- test_calc.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 test_calc.py diff --git a/test_calc.py b/test_calc.py new file mode 100644 index 0000000..b546e82 --- /dev/null +++ b/test_calc.py @@ -0,0 +1,13 @@ +import unittest, calc + + +class TestCalc(unittest.TestCase): + + def test_correct_expression(self): + expect = ['sin', '(', '30', ')', '+', '0.25', '-' , '(', '-1', ')' ] + actual = calc.correct_expression('sin(30)+.25-(-1)') + self.assertEqual(actual, expect) + + def test_bad_brackets(self): + with self.assertRaises(calc.CalcError): + calc.correct_expression('8-3(5-1))') From d8237bdd351e00a30ff34cb069ab590caa63d62b Mon Sep 17 00:00:00 2001 From: Stacy Date: Sun, 4 Nov 2018 23:11:59 +0300 Subject: [PATCH 07/49] Test all correct_expression functions --- test_calc.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/test_calc.py b/test_calc.py index b546e82..1062213 100644 --- a/test_calc.py +++ b/test_calc.py @@ -4,10 +4,22 @@ class TestCalc(unittest.TestCase): def test_correct_expression(self): - expect = ['sin', '(', '30', ')', '+', '0.25', '-' , '(', '-1', ')' ] - actual = calc.correct_expression('sin(30)+.25-(-1)') - self.assertEqual(actual, expect) + expect = ['-1', '*', 'sin', '(', '30', ')', '+', '0.25', '-', '(', '-1', '*', 'pow', '(', '2', ',', '2', ')', + ')'] + actual = calc.correct_expression('-sin(30)+.25-(-pow(2, 2))') + self.assertEqual(expect, actual) - def test_bad_brackets(self): - with self.assertRaises(calc.CalcError): - calc.correct_expression('8-3(5-1))') + def test_insert_multiplication(self): + expect = '3*log10(2)*5+2*(2+3)*(7+1)*log1p(8)' + actual = calc.insert_multiplication('3log10(2)5+2(2+3)(7+1)log1p(8)') + self.assertEqual(expect, actual) + + def test_correct_negative_value(self): + expect = '-1sin(40+5)+3(-1pi)' + actual = calc.match_negative_value('-sin(40+5)+3(-pi)') + self.assertEqual(expect, actual) + + def test_zeroless_number(self): + expect = '5+0.25*0.3abs(-0.75)' + actual = calc.zeroless('5+.25*.3abs(-.75)') + self.assertEqual(expect, actual) From f5a8e1d41ffcacac666e18fd9f95ffd816f5077a Mon Sep 17 00:00:00 2001 From: Stacy Date: Sun, 4 Nov 2018 23:14:54 +0300 Subject: [PATCH 08/49] New correct_expression functions with regex --- calc.py | 82 ++++++++++++++++++++++++++------------------------------- 1 file changed, 37 insertions(+), 45 deletions(-) diff --git a/calc.py b/calc.py index ca6f819..505803d 100644 --- a/calc.py +++ b/calc.py @@ -1,6 +1,11 @@ import re import math + +class CalcError(Exception): + pass + + binary_operations = { "+": 0, "-": 0, @@ -11,14 +16,12 @@ "^": 2, } - -ops_list = dict(log=1, log10=1, abs=1, sqrt=1, sin=1, asin=1, cos=1, acos=1, hypot=1, tan=1, atan=1, atan2=1, ceil=1, - copysign=2, fabs=1, factorial=1, floor=1, fmod=2, frexp=1, ldexp=2, fsum=1, isfinite=1, isinf=1, - isnan=1, modf=1, trunc=1, exp=1, expm1=1, log1p=1, log2=1, pow=2, degrees=1, radians=1, cosh=1, sinh=1, - tanh=1, acosh=1, asinh=1, atanh=1, erf=1, erfc=1, gamma=1, lgamma=1, inv=0, gcd=2, isclose=5, - isdexp=1, - ) - +ops_list = {'abs', 'pow', 'round', 'log', 'log10', 'sqrt', 'sin', 'asin', 'cos', 'acos', 'hypot', 'tan', 'atan', + 'copysign', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'ldexp', 'fsum', 'isfinite', 'isinf', + 'isnan', 'modf', 'trunc', 'exp', 'expm1', 'log1p', 'log2', 'degrees', 'radians', 'cosh', 'sinh', + 'tanh', 'acosh', 'asinh', 'atanh', 'erf', 'erfc', 'gamma', 'lgamma', 'inv', 'gcd', 'isclose', + 'isdexp', 'atan2', 'ceil', + } constants = { "pi", @@ -28,7 +31,6 @@ "nan", } - comparison_operators = { "<", "<=", @@ -39,31 +41,28 @@ } +def zeroless(expression): + match = re.split(r'(?<=\W)(?=\.\d)', expression) + return '0'.join(match) + + +def match_negative_value(expression): + match = re.split(r'(?<=^-)(?=[a-z])|(?<=\(-)(?=[a-z])', expression) + return '1'.join(match) + + +def insert_multiplication(expression): + regex = r'(?<=\))(?=\w+)|(?<=\))(?=\()|(?<=[^a-z][^a-z]\d)(?=\()|(?<=^\d)(?=\()|(?<=\d)(?=e|[a-z][a-z])' + match = re.split(regex, expression) + return '*'.join(match) + + def correct_expression(expression): - re_expr = re.findall('<=|==|!=|>=|log10|log2|log1p|expm1|atan2|\//|\d+\.\d+|\d+|\W|\w+',expression) - i = 0 - if re_expr[0] == '-' and is_float(re_expr[1]): - re_expr[1] *= -1 - re_expr.pop(0) - _len = lambda x: len(x) - for i in range(_len(re_expr)): - if is_float(re_expr[i]) and re_expr[i + 1] == '(': - re_expr.insert(i + 1, '*') - print('a') - elif re_expr[i].isdigit() and re_expr[i + 1] in ops_list: - re_expr.insert(i + 1, '*') - print('q') - print(re_expr[i+2]) - elif re_expr[i] == ')' and re_expr[i + 1][0].isdigit(): - re_expr.insert(i + 1, '*') - i += 1 - print('b') - elif re_expr[i] == '(' and re_expr[i+1] == '-' and is_float(re_expr[i+2]): - re_expr[i+2] =re_expr[i+1] + re_expr[i+2] - re_expr.pop(i+1) - - if i + 2 >= _len(re_expr): - break + expression = insert_multiplication(match_negative_value(zeroless(expression))) + regex = r'(<=|==|!=|>=|log10|log2|log1p|expm1|atan2|^-\d+.\d+|^-\d+|(?<=\()-\d+.' \ + '\d+|(?<=\()-\d+|\//|\d+\.\d+|\d+|\W|\w+)' + re_expr = re.split(regex, expression) + re_expr = [x for x in re_expr if x and x != ' '] return re_expr @@ -122,7 +121,7 @@ def calc(expression): elif operator == '>=': res = a >= b elif operator == '>': - res = a>b + res = a > b stack.append(res) elif i in constants: if i == "pi": @@ -137,7 +136,7 @@ def calc(expression): stack.append(math.nan) expression.remove(i) elif i in binary_operations: - if len(stack)>1 and isinstance(stack[-1],(int,float)) and isinstance(stack[-2],(int,float)): + if len(stack) > 1 and isinstance(stack[-1], (int, float)) and isinstance(stack[-2], (int, float)): b = stack.pop() a = stack.pop() if i == '+': @@ -161,11 +160,12 @@ def calc(expression): arg = [] ops, arg0 = get_arguments(expression) while arg0: - arg.append(calc(arg0.pop())) + arg.append(calc(arg0.pop())) if ops == 'pow': stack.append(pow(*arg)) elif ops == 'abs': stack.append(abs(*arg)) + print(stack) print(stack) return stack.pop() @@ -182,7 +182,7 @@ def to_postfix(expression): res.append(stack.pop()) res.append(i) elif i == '(': - if res[-1] in ops_list: + if res and res[-1] in ops_list: ops_bracket.append(i) stack.append(i) elif i == ')': @@ -207,11 +207,3 @@ def to_postfix(expression): for i in reversed(stack): res.append(i) return res - - #a = calc(to_postfix('abs(2)+3*2')) -print('res', calc(to_postfix('abs(-2)+3*2'))) -print(calc(to_postfix('pow(abs(3),abs(56-53)+1)+3*(3-4)+pi'))) -print(calc(to_postfix('pow(abs(2+1-1),abs(56-53)+1)+3*(3-4)'))) -print(calc(to_postfix("2+3-5+6<=6"))) - - From ab52a097cbc35e3bfcedd6cefe9fe6442523703a Mon Sep 17 00:00:00 2001 From: Stacy Date: Wed, 7 Nov 2018 15:55:22 +0300 Subject: [PATCH 09/49] Setup for pycalc --- __init__.py | 0 setup.py | 16 ++++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 __init__.py create mode 100644 setup.py diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..90e23e8 --- /dev/null +++ b/setup.py @@ -0,0 +1,16 @@ +from setuptools import setup, Extension + +setup( + name='pycalc', + version='1.0.0', + description='Pure-python command-line calculator.', + author='Anastasiya Holubeva', + author_email='anastasyago@yandex.ru', + url='https://github.com/hfvh/Pycalc', + packages=['pycalc'], + entry_points={ + 'console_scripts': [ + 'pycalc=pycalc.calc:main', + ], + }, +) From aad8776876defaf24cc1bc9e1c3e11e7d651fc02 Mon Sep 17 00:00:00 2001 From: Stacy Date: Wed, 7 Nov 2018 15:56:39 +0300 Subject: [PATCH 10/49] Tests for pycalc functions --- test_calc.py | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 3 deletions(-) diff --git a/test_calc.py b/test_calc.py index 1062213..810bc1e 100644 --- a/test_calc.py +++ b/test_calc.py @@ -1,4 +1,5 @@ -import unittest, calc +import unittest +from pycalc import calc class TestCalc(unittest.TestCase): @@ -19,7 +20,98 @@ def test_correct_negative_value(self): actual = calc.match_negative_value('-sin(40+5)+3(-pi)') self.assertEqual(expect, actual) - def test_zeroless_number(self): + def test_with_zero_number(self): expect = '5+0.25*0.3abs(-0.75)' - actual = calc.zeroless('5+.25*.3abs(-.75)') + actual = calc.fix_missing_zero('5+.25*.3abs(-.75)') self.assertEqual(expect, actual) + + def test_invalid_brackets(self): + with self.assertRaises(calc.CalcError): + calc.correct_expression('sin()+3') + calc.to_postfix('sin30(5-1)') + calc.to_postfix('(') + calc.to_postfix('1+2/3+5)-6') + + def test_invalid_input(self): + with self.assertRaises(calc.CalcError): + calc.to_postfix('2+3+sin(45)+pov(2, 6)') + calc.to_postfix('1+2*3-4@') + calc.to_postfix('1-#/2') + calc.to_postfix('3*6>>5+7') + calc.to_postfix('./7+2') + calc.to_postfix('.7*(sin())') + calc.to_postfix('23+3+') + calc.to_postfix('abs(-)') + + def test_invalid_operators(self): + with self.assertRaises(calc.CalcError): + calc.to_postfix('1+2-*6-3') + calc.to_postfix('cos(+30)/2+3') + + def test_reversed_polish_notation(self): + expect = ['3', '5', '2', '2', '^', '*', '3', '1', '-', '/', '+', 'abs', '-2', ')', '-', 'pow', '1', '1', '+', + ',', '1', '2', '*', '1', '+', ')', '+'] + actual = calc.to_postfix('3+5*2^2/(3-1)-abs(-2)+pow(1+1, 1*2+1)') + self.assertEqual(expect, actual) + + def test_comparison(self): + expect = True + actual = calc.calc_iteration(calc.to_postfix('2+3*5>=10+12/2')) + self.assertEqual(expect, actual) + + def test_addition(self): + expect = 8.05 + actual = calc.calc_iteration(calc.to_postfix('3.5+.5+2.75+1.3')) + self.assertEqual(expect, actual) + + def test_subtraction(self): + expect = 3 + actual = calc.calc_iteration(calc.to_postfix('25-22')) + self.assertEqual(expect, actual) + + def test_multiplication(self): + expect = 5 + actual = calc.calc_iteration(calc.to_postfix('2.5*2')) + self.assertEqual(expect, actual) + + def test_division(self): + expect = 6 + actual = calc.calc_iteration(calc.to_postfix('24/4')) + self.assertEqual(expect, actual) + + def test_division_by_zero(self): + with self.assertRaises(calc.CalcError): + calc.calc_iteration(calc.to_postfix('3 / 0')) + + def test_modulus(self): + expect = 7 + actual = calc.calc_iteration(calc.to_postfix('29%11')) + self.assertEqual(expect, actual) + + def test_modulo_by_zero(self): + with self.assertRaises(calc.CalcError): + calc.calc_iteration(calc.to_postfix('3 % 0')) + + def test_floor_division(self): + expect = 4 + actual = calc.calc_iteration(calc.to_postfix('9//2')) + self.assertEqual(expect, actual) + + def test_floor_division_by_zero(self): + with self.assertRaises(calc.CalcError): + calc.calc_iteration(calc.to_postfix('3 // 0')) + + def test_power(self): + expect = 8 + actual = calc.calc_iteration(calc.to_postfix('2^3')) + self.assertEqual(expect, actual) + + def test_round(self): + expect = 8 + actual = calc.calc_iteration(calc.to_postfix('round(7.51)')) + self.assertEqual(expect, actual) + + def test_abs(self): + expect = 6 + actual = calc.calc_iteration(calc.to_postfix('abs(-6)')) + self.assertEqual(expect, actual) \ No newline at end of file From 43d480d351acfaff8430e59de326c9ef9d0f6df2 Mon Sep 17 00:00:00 2001 From: Stacy Date: Wed, 7 Nov 2018 15:57:38 +0300 Subject: [PATCH 11/49] Calc with some documentation --- pycalc/calc.py | 300 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 300 insertions(+) create mode 100644 pycalc/calc.py diff --git a/pycalc/calc.py b/pycalc/calc.py new file mode 100644 index 0000000..c36682e --- /dev/null +++ b/pycalc/calc.py @@ -0,0 +1,300 @@ +#!/usr/bin/env python3 +import re +import math +import argparse + + +class CalcError(Exception): + """Calculation exception class""" + pass + + +binary_operations = { + "+": 0, + "-": 0, + "*": 1, + "/": 1, + "//": 1, + "%": 1, + "^": 2, +} + +ops_list = {'abs', 'pow', 'round', 'log', 'log10', 'sqrt', 'sin', 'asin', 'cos', 'acos', 'hypot', 'tan', 'atan', + 'copysign', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'ldexp', 'fsum', 'isfinite', 'isinf', + 'isnan', 'modf', 'trunc', 'exp', 'expm1', 'log1p', 'log2', 'degrees', 'radians', 'cosh', 'sinh', + 'tanh', 'acosh', 'asinh', 'atanh', 'erf', 'erfc', 'gamma', 'lgamma', 'inv', 'gcd', 'isclose', + 'isdexp', 'atan2', 'ceil', + } + +constants = { + "pi", + "e", + "tau", + "inf", + "nan", +} + +comparison_operators = { + "<", + "<=", + "==", + "!=", + ">=", + ">", +} + + +def fix_missing_zero(expression): + """Inserts a zero in the number of the construction .number: .3 -> 0.3 + Args: + expression: input string with math expression + Returns: + The return fixed string + """ + match = re.split(r'(?<=\W)(?=\.\d)|(?<=^)(?=\.\d)', expression) + return '0'.join(match) + + +def match_negative_value(expression): + """Fix missing -1, if math function looks like -sin + Args: + expression: input string with math expression + Returns: + The return string with correct negative value + """ + match = re.split(r'(?<=^-)(?=[a-z])|(?<=\(-)(?=[a-z])', expression) + return '1'.join(match) + + +def insert_multiplication(expression): + """Fix missing multiplication + Args: + expression: input string with math expression + Returns: + The return string with correct multiplication + """ + regex = r'(?<=\))(?=\w+)|(?<=\))(?=\()|(?<=[^a-z][^a-z]\d)(?=\()|(?<=^\d)(?=\()|(?<=\d)(?=e|[a-z][a-z])' + match = re.split(regex, expression) + return '*'.join(match) + + +def correct_expression(expression): + """Split expression by tokens + Args: + expression: input string with math expression + Returns: + The return list of tokens + """ + if '()' in expression: + raise CalcError('ERROR: invalid bracket expression') + expression = insert_multiplication(match_negative_value(fix_missing_zero(expression))) + regex = r'(<=|==|!=|>=|log10|log2|log1p|expm1|atan2|^-\d+.\d+|^-\d+|(?<=\()-\d+.' \ + '\d+|(?<=\()-\d+|\//|\d+\.\d+|\d+|\W|\w+)' + re_expr = re.split(regex, expression) + re_expr = [x for x in re_expr if x and x != ' '] + return re_expr + + +def get_arguments(expression): + """Get arguments for function + Args: + expression: input string start with math function + Returns: + The return list of arguments + """ + ops = expression.pop(0) + res = [] + arg = [] + point = 1 + while expression: + if expression[0] == ',': + res.append(arg.copy()) + arg.clear() + elif expression[0] == ')': + point -= 1 + if point == 0: + expression.remove(expression[0]) + res.append(arg) + return ops, res + else: + arg.append(expression[0]) + elif expression[0] in ops_list: + point += 1 + arg.append(expression[0]) + else: + arg.append(expression[0]) + expression.remove(expression[0]) + + +def is_float(value): + """Check is value number + Args: + value: expression token + Returns: + The return True, if value is number and False if not + """ + try: + float(value) + return True + except ValueError: + return False + + +def calc_iteration(expression): + """Calculate math expression + Args: + expression: input string with math expression + Returns: + The return result of calculation + """ + stack = [] + while expression: + i = expression[0] + if is_float(i): + stack.append(float(i)) + expression.remove(i) + elif i in comparison_operators: + operator = expression.pop(0) + a = stack.pop() + b = calc_iteration(expression) + if operator == '<': + res = a < b + elif operator == '<=': + res = a <= b + elif operator == '==': + res = a == b + elif operator == '!=': + res = a != b + elif operator == '>=': + res = a >= b + elif operator == '>': + res = a > b + stack.append(res) + elif i in constants: + stack.append(getattr(math, i)) + expression.remove(i) + elif i in binary_operations: + if len(stack) > 1 and isinstance(stack[-1], (int, float)) and isinstance(stack[-2], (int, float)): + b = stack.pop() + a = stack.pop() + if i == '+': + stack.append(a + b) + elif i == '-': + stack.append(a - b) + elif i == '*': + stack.append(a * b) + elif i == '/': + try: + stack.append(a / b) + except ZeroDivisionError: + raise CalcError('ERROR: division by zero') + elif i == '//': + try: + stack.append(a // b) + except ZeroDivisionError: + raise CalcError('ERROR: floor division by zero') + elif i == '%': + try: + stack.append(a % b) + except ZeroDivisionError: + raise CalcError('ERROR: modulus by zero') + elif i == '^': + stack.append(a ** b) + else: + stack.append(i) + expression.remove((i)) + if i in ops_list: + arg = [] + ops, arg0 = get_arguments(expression) + while arg0: + arg.append(calc_iteration(arg0.pop())) + try: + if ops == 'round': + stack.append(round(*arg)) + elif ops == 'abs': + stack.append(abs(*arg)) + else: + stack.append(getattr(math, ops)(*reversed(arg))) + except ValueError: + raise CalcError('ERROR: invalid argument for function {0}'.format(ops)) + except TypeError: + raise CalcError('ERROR: invalid number of arguments for function {0}'.format(ops)) + return stack.pop() + + +def to_postfix(expression): + """Convert infix notation to postfix notation + Args: + expression: input string with math expression + Returns: + The return expression in postfix notation + """ + res = [] + stack = [] + ops_bracket = [] + expression = correct_expression(expression) + if expression[0] in binary_operations: + raise CalcError('ERROR: invalid operator "{0}"'.format(expression[0])) + for item in range(len(expression)): + i = expression[item] + if is_float(i) or i in ops_list or i in constants: + res.append(i) + elif i in comparison_operators: + while stack: + res.append(stack.pop()) + res.append(i) + elif i == '(': + if expression[item + 1] in binary_operations: + raise CalcError('ERROR: invalid operator "{0}"'.format(expression[item + 1])) + if res and res[-1] in ops_list: + ops_bracket.append(i) + stack.append(i) + elif i == ')': + if '(' in stack: + while stack[-1] != '(': + res.append(stack.pop()) + else: + raise CalcError('ERROR: invalid bracket expression') + stack.pop() + if ops_bracket: + ops_bracket.pop() + res.append(i) + elif i in binary_operations: + if item + 1 >= len(expression) or expression[item + 1] in binary_operations: + raise CalcError('ERROR: invalid operator "{0}"'.format(expression[:item + 1])) + if stack and stack[-1] in binary_operations and binary_operations[stack[-1]] >= binary_operations[i]: + while stack and stack[-1] in binary_operations and binary_operations[stack[-1]] >= binary_operations[i]: + res.append(stack.pop()) + stack.append(i) + else: + stack.append(i) + elif i == ',': + while stack[-1] != '(': + res.append(stack.pop()) + res.append(i) + else: + raise CalcError('ERROR: input invalid token "{0}"'.format(i)) + for i in reversed(stack): + res.append(i) + if '(' in res: + raise CalcError('ERROR: invalid bracket expression') + return res + + +def evaluate(expression): + return calc_iteration(to_postfix(expression)) + + +def main(): + """Calc main function""" + parser = argparse.ArgumentParser(description='Pure-python command-line calculator.') + parser.add_argument("EXPRESSION", help="expression string to evaluate") + args = parser.parse_args() + try: + print(evaluate(args.EXPRESSION)) + except CalcError as exception: + print(exception) + + +if __name__ == '__main__': + main() From a23c84612c3bd2508aa590e8450a202cfd01a9bd Mon Sep 17 00:00:00 2001 From: Stacy Date: Wed, 7 Nov 2018 16:00:44 +0300 Subject: [PATCH 12/49] Remove redundant files --- __init__.py | 0 calc.py | 209 ---------------------------------------------------- 2 files changed, 209 deletions(-) delete mode 100644 __init__.py delete mode 100644 calc.py diff --git a/__init__.py b/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/calc.py b/calc.py deleted file mode 100644 index 505803d..0000000 --- a/calc.py +++ /dev/null @@ -1,209 +0,0 @@ -import re -import math - - -class CalcError(Exception): - pass - - -binary_operations = { - "+": 0, - "-": 0, - "*": 1, - "/": 1, - "//": 1, - "%": 1, - "^": 2, -} - -ops_list = {'abs', 'pow', 'round', 'log', 'log10', 'sqrt', 'sin', 'asin', 'cos', 'acos', 'hypot', 'tan', 'atan', - 'copysign', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'ldexp', 'fsum', 'isfinite', 'isinf', - 'isnan', 'modf', 'trunc', 'exp', 'expm1', 'log1p', 'log2', 'degrees', 'radians', 'cosh', 'sinh', - 'tanh', 'acosh', 'asinh', 'atanh', 'erf', 'erfc', 'gamma', 'lgamma', 'inv', 'gcd', 'isclose', - 'isdexp', 'atan2', 'ceil', - } - -constants = { - "pi", - "e", - "tau", - "inf", - "nan", -} - -comparison_operators = { - "<", - "<=", - "==", - "!=", - ">=", - ">", -} - - -def zeroless(expression): - match = re.split(r'(?<=\W)(?=\.\d)', expression) - return '0'.join(match) - - -def match_negative_value(expression): - match = re.split(r'(?<=^-)(?=[a-z])|(?<=\(-)(?=[a-z])', expression) - return '1'.join(match) - - -def insert_multiplication(expression): - regex = r'(?<=\))(?=\w+)|(?<=\))(?=\()|(?<=[^a-z][^a-z]\d)(?=\()|(?<=^\d)(?=\()|(?<=\d)(?=e|[a-z][a-z])' - match = re.split(regex, expression) - return '*'.join(match) - - -def correct_expression(expression): - expression = insert_multiplication(match_negative_value(zeroless(expression))) - regex = r'(<=|==|!=|>=|log10|log2|log1p|expm1|atan2|^-\d+.\d+|^-\d+|(?<=\()-\d+.' \ - '\d+|(?<=\()-\d+|\//|\d+\.\d+|\d+|\W|\w+)' - re_expr = re.split(regex, expression) - re_expr = [x for x in re_expr if x and x != ' '] - return re_expr - - -def get_arguments(expression): - ops = expression.pop(0) - res = [] - arg = [] - point = 1 - while expression: - if expression[0] == ',': - res.append(arg.copy()) - arg.clear() - elif expression[0] == ')': - point -= 1 - if point == 0: - expression.remove(expression[0]) - res.append(arg) - return ops, res - else: - arg.append(expression[0]) - elif expression[0] in ops_list: - point += 1 - arg.append(expression[0]) - else: - arg.append(expression[0]) - expression.remove(expression[0]) - - -def is_float(val): - try: - float(val) - return True - except ValueError: - return False - - -def calc(expression): - stack = [] - while expression: - i = expression[0] - if is_float(i): - stack.append(float(i)) - expression.remove(i) - elif i in comparison_operators: - operator = expression.pop(0) - a = stack.pop() - b = calc(expression) - if operator == '<': - res = a < b - elif operator == '<=': - res = a <= b - elif operator == '==': - res = a == b - elif operator == '!=': - res = a != b - elif operator == '>=': - res = a >= b - elif operator == '>': - res = a > b - stack.append(res) - elif i in constants: - if i == "pi": - stack.append(math.pi) - elif i == "e": - stack.append(math.e) - elif i == "tau": - stack.append(math.tau) - elif i == "inf": - stack.append(math.inf) - elif i == "nan": - stack.append(math.nan) - expression.remove(i) - elif i in binary_operations: - if len(stack) > 1 and isinstance(stack[-1], (int, float)) and isinstance(stack[-2], (int, float)): - b = stack.pop() - a = stack.pop() - if i == '+': - stack.append(a + b) - elif i == '-': - stack.append(a - b) - elif i == '*': - stack.append(a * b) - elif i == '/': - stack.append(a / b) - elif i == '//': - stack.append(a // b) - elif i == '%': - stack.append(a % b) - elif i == '^': - stack.append(a ** b) - else: - stack.append(i) - expression.remove((i)) - if i in ops_list: - arg = [] - ops, arg0 = get_arguments(expression) - while arg0: - arg.append(calc(arg0.pop())) - if ops == 'pow': - stack.append(pow(*arg)) - elif ops == 'abs': - stack.append(abs(*arg)) - print(stack) - print(stack) - return stack.pop() - - -def to_postfix(expression): - res = [] - stack = [] - ops_bracket = [] - for i in correct_expression(expression): - if is_float(i) or i in ops_list or i in constants: - res.append(i) - elif i in comparison_operators: - while stack: - res.append(stack.pop()) - res.append(i) - elif i == '(': - if res and res[-1] in ops_list: - ops_bracket.append(i) - stack.append(i) - elif i == ')': - while stack[-1] != '(': - res.append(stack.pop()) - stack.pop() - if ops_bracket: - ops_bracket.pop() - res.append(i) - elif i in binary_operations: - if stack and stack[-1] in binary_operations and binary_operations[stack[-1]] >= binary_operations[i]: - while stack and stack[-1] in binary_operations and binary_operations[stack[-1]] >= binary_operations[i]: - res.append(stack.pop()) - stack.append(i) - else: - stack.append(i) - elif i == ',': - while stack[-1] != '(': - res.append(stack.pop()) - res.append(i) - - for i in reversed(stack): - res.append(i) - return res From df4969f26bffd503f8dd9c966be308687744cba7 Mon Sep 17 00:00:00 2001 From: Stacy Date: Wed, 7 Nov 2018 16:09:09 +0300 Subject: [PATCH 13/49] Add init to pycalc --- pycalc/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 pycalc/__init__.py diff --git a/pycalc/__init__.py b/pycalc/__init__.py new file mode 100644 index 0000000..e69de29 From c6c4c920815af659c18cda5c2654ec4965437856 Mon Sep 17 00:00:00 2001 From: Stacy Date: Wed, 7 Nov 2018 16:16:28 +0300 Subject: [PATCH 14/49] Add pycalc to final_task --- {pycalc => final_task/pycalc}/__init__.py | 0 {pycalc => final_task/pycalc}/calc.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {pycalc => final_task/pycalc}/__init__.py (100%) rename {pycalc => final_task/pycalc}/calc.py (100%) diff --git a/pycalc/__init__.py b/final_task/pycalc/__init__.py similarity index 100% rename from pycalc/__init__.py rename to final_task/pycalc/__init__.py diff --git a/pycalc/calc.py b/final_task/pycalc/calc.py similarity index 100% rename from pycalc/calc.py rename to final_task/pycalc/calc.py From 75fbac9da72bb51e5e2b8fec47cd9cad8e02c47f Mon Sep 17 00:00:00 2001 From: Stacy Date: Wed, 7 Nov 2018 16:29:05 +0300 Subject: [PATCH 15/49] Add setup.py to final_task --- final_task/setup.py | 16 ++++++++++++++++ setup.py | 16 ---------------- 2 files changed, 16 insertions(+), 16 deletions(-) delete mode 100644 setup.py diff --git a/final_task/setup.py b/final_task/setup.py index e69de29..90e23e8 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -0,0 +1,16 @@ +from setuptools import setup, Extension + +setup( + name='pycalc', + version='1.0.0', + description='Pure-python command-line calculator.', + author='Anastasiya Holubeva', + author_email='anastasyago@yandex.ru', + url='https://github.com/hfvh/Pycalc', + packages=['pycalc'], + entry_points={ + 'console_scripts': [ + 'pycalc=pycalc.calc:main', + ], + }, +) diff --git a/setup.py b/setup.py deleted file mode 100644 index 90e23e8..0000000 --- a/setup.py +++ /dev/null @@ -1,16 +0,0 @@ -from setuptools import setup, Extension - -setup( - name='pycalc', - version='1.0.0', - description='Pure-python command-line calculator.', - author='Anastasiya Holubeva', - author_email='anastasyago@yandex.ru', - url='https://github.com/hfvh/Pycalc', - packages=['pycalc'], - entry_points={ - 'console_scripts': [ - 'pycalc=pycalc.calc:main', - ], - }, -) From 558e088014b0ad019ea5ea25deff4f5e35484c93 Mon Sep 17 00:00:00 2001 From: Stacy Date: Wed, 7 Nov 2018 17:03:49 +0300 Subject: [PATCH 16/49] Fix regex --- final_task/pycalc/calc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/final_task/pycalc/calc.py b/final_task/pycalc/calc.py index c36682e..8c43b15 100644 --- a/final_task/pycalc/calc.py +++ b/final_task/pycalc/calc.py @@ -89,7 +89,7 @@ def correct_expression(expression): raise CalcError('ERROR: invalid bracket expression') expression = insert_multiplication(match_negative_value(fix_missing_zero(expression))) regex = r'(<=|==|!=|>=|log10|log2|log1p|expm1|atan2|^-\d+.\d+|^-\d+|(?<=\()-\d+.' \ - '\d+|(?<=\()-\d+|\//|\d+\.\d+|\d+|\W|\w+)' + r'\d+|(?<=\()-\d+|\//|\d+\.\d+|\d+|\W|\w+)' re_expr = re.split(regex, expression) re_expr = [x for x in re_expr if x and x != ' '] return re_expr From 4edbcc6a5dd940f490b5fdf71ebedc931bd29d0d Mon Sep 17 00:00:00 2001 From: Stacy Date: Wed, 7 Nov 2018 17:21:48 +0300 Subject: [PATCH 17/49] deb --- final_task/pycalc/calc.py | 1 + 1 file changed, 1 insertion(+) diff --git a/final_task/pycalc/calc.py b/final_task/pycalc/calc.py index 8c43b15..401a92f 100644 --- a/final_task/pycalc/calc.py +++ b/final_task/pycalc/calc.py @@ -51,6 +51,7 @@ def fix_missing_zero(expression): Returns: The return fixed string """ + print(expression) match = re.split(r'(?<=\W)(?=\.\d)|(?<=^)(?=\.\d)', expression) return '0'.join(match) From dfcd5f06d9a36004daffb31bc9380b71ff7248fa Mon Sep 17 00:00:00 2001 From: Stacy Date: Wed, 7 Nov 2018 19:42:45 +0300 Subject: [PATCH 18/49] Fix regular expression to python 3.6 --- final_task/pycalc/calc.py | 54 ++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/final_task/pycalc/calc.py b/final_task/pycalc/calc.py index 401a92f..1123c72 100644 --- a/final_task/pycalc/calc.py +++ b/final_task/pycalc/calc.py @@ -44,6 +44,22 @@ class CalcError(Exception): } +def insert(regex, expression, token): + """Inserts token in expression + Args: + regex: pattern to find position + expression: input string with math expression + token: token to insert + Returns: + The return fixed string + """ + find = re.search(regex, expression) + while find: + position = re.search(regex, expression).end() + expression = token.join([expression[:position], expression[position:]]) + find = re.search(regex, expression) + return expression + def fix_missing_zero(expression): """Inserts a zero in the number of the construction .number: .3 -> 0.3 Args: @@ -51,9 +67,16 @@ def fix_missing_zero(expression): Returns: The return fixed string """ - print(expression) - match = re.split(r'(?<=\W)(?=\.\d)|(?<=^)(?=\.\d)', expression) - return '0'.join(match) + token = '0' + regex = r'(?<=\W)(?=\.\d)|(?<=^)(?=\.\d)' + find = re.search(regex, expression) + if not find: + res = expression + else: + res = insert(regex, expression, token) + # match = re.split(regex, expression) + # res = '0'.join(match) + return res def match_negative_value(expression): @@ -63,8 +86,16 @@ def match_negative_value(expression): Returns: The return string with correct negative value """ - match = re.split(r'(?<=^-)(?=[a-z])|(?<=\(-)(?=[a-z])', expression) - return '1'.join(match) + token = '1' + regex = r'(?<=^-)(?=[a-z])|(?<=\(-)(?=[a-z])' + find = re.search(regex, expression) + if not find: + res = expression + else: + res = insert(regex, expression, token) + # match = re.split(regex, expression) + # res = '1'.join(match) + return res def insert_multiplication(expression): @@ -74,9 +105,16 @@ def insert_multiplication(expression): Returns: The return string with correct multiplication """ + token = '*' regex = r'(?<=\))(?=\w+)|(?<=\))(?=\()|(?<=[^a-z][^a-z]\d)(?=\()|(?<=^\d)(?=\()|(?<=\d)(?=e|[a-z][a-z])' - match = re.split(regex, expression) - return '*'.join(match) + find = re.search(regex, expression) + if not find: + res = expression + else: + res = insert(regex, expression, token) + # match = re.split(regex, expression) + # res = '*'.join(match) + return res def correct_expression(expression): @@ -299,3 +337,5 @@ def main(): if __name__ == '__main__': main() + + From 7e11b0a2d8b9088b0ac56296c673b232fb1db6d5 Mon Sep 17 00:00:00 2001 From: Stacy Date: Wed, 7 Nov 2018 21:08:43 +0300 Subject: [PATCH 19/49] Fix priority --- final_task/pycalc/calc.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/final_task/pycalc/calc.py b/final_task/pycalc/calc.py index 1123c72..560a9db 100644 --- a/final_task/pycalc/calc.py +++ b/final_task/pycalc/calc.py @@ -44,6 +44,16 @@ class CalcError(Exception): } +def fix_multi_operations(expression): + mul_operators = re.search(r'\+\+|\-\-|\+\-|-\+', expression) + while mul_operators: + expression = expression.replace('++', '+') + expression = expression.replace('--', '+') + expression = expression.replace('+-', '-') + expression = expression.replace('-+', '-') + mul_operators = re.search(r'\+\+|\-\-|\+\-|-\+', expression) + return expression + def insert(regex, expression, token): """Inserts token in expression Args: @@ -60,6 +70,7 @@ def insert(regex, expression, token): find = re.search(regex, expression) return expression + def fix_missing_zero(expression): """Inserts a zero in the number of the construction .number: .3 -> 0.3 Args: @@ -126,7 +137,7 @@ def correct_expression(expression): """ if '()' in expression: raise CalcError('ERROR: invalid bracket expression') - expression = insert_multiplication(match_negative_value(fix_missing_zero(expression))) + expression = insert_multiplication(match_negative_value(fix_missing_zero(fix_multi_operations(expression)))) regex = r'(<=|==|!=|>=|log10|log2|log1p|expm1|atan2|^-\d+.\d+|^-\d+|(?<=\()-\d+.' \ r'\d+|(?<=\()-\d+|\//|\d+\.\d+|\d+|\W|\w+)' re_expr = re.split(regex, expression) @@ -301,7 +312,7 @@ def to_postfix(expression): elif i in binary_operations: if item + 1 >= len(expression) or expression[item + 1] in binary_operations: raise CalcError('ERROR: invalid operator "{0}"'.format(expression[:item + 1])) - if stack and stack[-1] in binary_operations and binary_operations[stack[-1]] >= binary_operations[i]: + if stack and stack[-1] in binary_operations and binary_operations[stack[-1]] > binary_operations[i]: while stack and stack[-1] in binary_operations and binary_operations[stack[-1]] >= binary_operations[i]: res.append(stack.pop()) stack.append(i) @@ -339,3 +350,4 @@ def main(): main() + From 3cadcfd4527be175ffd5cc51f55e833eb9f07e22 Mon Sep 17 00:00:00 2001 From: Stacy Date: Wed, 7 Nov 2018 21:45:39 +0300 Subject: [PATCH 20/49] Fix regex 2 --- final_task/pycalc/calc.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/final_task/pycalc/calc.py b/final_task/pycalc/calc.py index 560a9db..907cac8 100644 --- a/final_task/pycalc/calc.py +++ b/final_task/pycalc/calc.py @@ -98,7 +98,7 @@ def match_negative_value(expression): The return string with correct negative value """ token = '1' - regex = r'(?<=^-)(?=[a-z])|(?<=\(-)(?=[a-z])' + regex = r'(?<=^-)(?=[a-z])|(?<=\(-)(?=[a-z])|(?<=\W\-)(?=[a-z])' find = re.search(regex, expression) if not find: res = expression @@ -138,8 +138,8 @@ def correct_expression(expression): if '()' in expression: raise CalcError('ERROR: invalid bracket expression') expression = insert_multiplication(match_negative_value(fix_missing_zero(fix_multi_operations(expression)))) - regex = r'(<=|==|!=|>=|log10|log2|log1p|expm1|atan2|^-\d+.\d+|^-\d+|(?<=\()-\d+.' \ - r'\d+|(?<=\()-\d+|\//|\d+\.\d+|\d+|\W|\w+)' + regex = r'(<=|==|!=|>=|log10|log2|log1p|expm1|atan2|^-\d+.\d+|^-\d+|(?<=\W\W)\-\d+\.\d+|(?<=\W\W)\-\d+|(?<=\()-\d+. \ + \d+|(?<=\()-\d+|\//|\/|\d+\.\d+|\d+|\W|\w+)' re_expr = re.split(regex, expression) re_expr = [x for x in re_expr if x and x != ' '] return re_expr @@ -310,8 +310,6 @@ def to_postfix(expression): ops_bracket.pop() res.append(i) elif i in binary_operations: - if item + 1 >= len(expression) or expression[item + 1] in binary_operations: - raise CalcError('ERROR: invalid operator "{0}"'.format(expression[:item + 1])) if stack and stack[-1] in binary_operations and binary_operations[stack[-1]] > binary_operations[i]: while stack and stack[-1] in binary_operations and binary_operations[stack[-1]] >= binary_operations[i]: res.append(stack.pop()) From 5890c0e04a60e9f7f5d6dbbe5807a8d209e4758b Mon Sep 17 00:00:00 2001 From: Stacy Date: Wed, 7 Nov 2018 22:16:40 +0300 Subject: [PATCH 21/49] Fix regex 3 --- final_task/pycalc/calc.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/final_task/pycalc/calc.py b/final_task/pycalc/calc.py index 907cac8..210d620 100644 --- a/final_task/pycalc/calc.py +++ b/final_task/pycalc/calc.py @@ -54,6 +54,7 @@ def fix_multi_operations(expression): mul_operators = re.search(r'\+\+|\-\-|\+\-|-\+', expression) return expression + def insert(regex, expression, token): """Inserts token in expression Args: @@ -138,8 +139,8 @@ def correct_expression(expression): if '()' in expression: raise CalcError('ERROR: invalid bracket expression') expression = insert_multiplication(match_negative_value(fix_missing_zero(fix_multi_operations(expression)))) - regex = r'(<=|==|!=|>=|log10|log2|log1p|expm1|atan2|^-\d+.\d+|^-\d+|(?<=\W\W)\-\d+\.\d+|(?<=\W\W)\-\d+|(?<=\()-\d+. \ - \d+|(?<=\()-\d+|\//|\/|\d+\.\d+|\d+|\W|\w+)' + regex = re.compile(r'(<=|==|!=|>=|log10|log2|log1p|expm1|atan2|^-\d+.\d+|^-\d+|(?<=\W\W)\-\d+\.\d+|(?<=\W\W)\-\d+|' + r'(?<=\()\-\d+.\d+|(?<=\()\-\d+|\//|\/|\d+\.\d+|\d+|\W|\w+)') re_expr = re.split(regex, expression) re_expr = [x for x in re_expr if x and x != ' '] return re_expr @@ -347,5 +348,3 @@ def main(): if __name__ == '__main__': main() - - From 448f4c50384b2a62c0b8da9286feb643eb64f041 Mon Sep 17 00:00:00 2001 From: Stacy Date: Wed, 7 Nov 2018 23:15:11 +0300 Subject: [PATCH 22/49] Fix regex 4 --- final_task/pycalc/calc.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/final_task/pycalc/calc.py b/final_task/pycalc/calc.py index 210d620..87b5515 100644 --- a/final_task/pycalc/calc.py +++ b/final_task/pycalc/calc.py @@ -139,8 +139,9 @@ def correct_expression(expression): if '()' in expression: raise CalcError('ERROR: invalid bracket expression') expression = insert_multiplication(match_negative_value(fix_missing_zero(fix_multi_operations(expression)))) - regex = re.compile(r'(<=|==|!=|>=|log10|log2|log1p|expm1|atan2|^-\d+.\d+|^-\d+|(?<=\W\W)\-\d+\.\d+|(?<=\W\W)\-\d+|' - r'(?<=\()\-\d+.\d+|(?<=\()\-\d+|\//|\/|\d+\.\d+|\d+|\W|\w+)') + regex = re.compile(r'(<=|==|!=|>=|log10|log2|log1p|expm1|atan2|^-\d+.\d+|^-\d+|(?<=\W\W)\-\d+\.\d+|(?<=\W\W)\-\d+|' + r'(?<=\()\-\d+.\d+|(?<=\()\-\d+|(?<=[a-z]\W)\-\d+.\d+|(?<=[a-z]\W)\-\d+|' + r'\//|\/|\d+\.\d+|\d+|\W|\w+)') re_expr = re.split(regex, expression) re_expr = [x for x in re_expr if x and x != ' '] return re_expr @@ -158,7 +159,7 @@ def get_arguments(expression): arg = [] point = 1 while expression: - if expression[0] == ',': + if expression[0] == ',' and point == 1: res.append(arg.copy()) arg.clear() elif expression[0] == ')': @@ -258,14 +259,14 @@ def calc_iteration(expression): arg = [] ops, arg0 = get_arguments(expression) while arg0: - arg.append(calc_iteration(arg0.pop())) + arg.append(calc_iteration(arg0.pop(0))) try: if ops == 'round': stack.append(round(*arg)) elif ops == 'abs': stack.append(abs(*arg)) else: - stack.append(getattr(math, ops)(*reversed(arg))) + stack.append(getattr(math, ops)(*arg)) except ValueError: raise CalcError('ERROR: invalid argument for function {0}'.format(ops)) except TypeError: @@ -347,4 +348,3 @@ def main(): if __name__ == '__main__': main() - From f136f96a362669f1320e638d0dca4f11ace115cf Mon Sep 17 00:00:00 2001 From: Stacy Date: Thu, 8 Nov 2018 01:22:58 +0300 Subject: [PATCH 23/49] Fix postfix notation --- final_task/pycalc/calc.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/final_task/pycalc/calc.py b/final_task/pycalc/calc.py index 87b5515..19c9351 100644 --- a/final_task/pycalc/calc.py +++ b/final_task/pycalc/calc.py @@ -139,8 +139,8 @@ def correct_expression(expression): if '()' in expression: raise CalcError('ERROR: invalid bracket expression') expression = insert_multiplication(match_negative_value(fix_missing_zero(fix_multi_operations(expression)))) - regex = re.compile(r'(<=|==|!=|>=|log10|log2|log1p|expm1|atan2|^-\d+.\d+|^-\d+|(?<=\W\W)\-\d+\.\d+|(?<=\W\W)\-\d+|' - r'(?<=\()\-\d+.\d+|(?<=\()\-\d+|(?<=[a-z]\W)\-\d+.\d+|(?<=[a-z]\W)\-\d+|' + regex = re.compile(r'(<=|==|!=|>=|log1p|^-\d+\.\d+|^-\d+|(?<=\W\W)\-\d+\.\d+|(?<=\W\W)\-\d+|' + r'(?<=\()\-\d+\.\d+|(?<=\()\-\d+|(?<=[a-z]\W)\-\d+\.\d+|(?<=[a-z]\W)\-\d+|' r'\//|\/|\d+\.\d+|\d+|\W|\w+)') re_expr = re.split(regex, expression) re_expr = [x for x in re_expr if x and x != ' '] @@ -312,7 +312,9 @@ def to_postfix(expression): ops_bracket.pop() res.append(i) elif i in binary_operations: - if stack and stack[-1] in binary_operations and binary_operations[stack[-1]] > binary_operations[i]: + if item + 1 >= len(expression) or expression[item + 1] in binary_operations: + raise CalcError('ERROR: invalid operator "{0}"'.format(expression[:item + 1])) + if stack and stack[-1] in binary_operations and binary_operations[stack[-1]] >= binary_operations[i] and i != '^': while stack and stack[-1] in binary_operations and binary_operations[stack[-1]] >= binary_operations[i]: res.append(stack.pop()) stack.append(i) @@ -348,3 +350,4 @@ def main(): if __name__ == '__main__': main() +#print(to_postfix("2+3*5>=10+12/2")) \ No newline at end of file From e1985e5a4d7bc9c3626afda678a6b15ccf424588 Mon Sep 17 00:00:00 2001 From: Stacy Date: Thu, 8 Nov 2018 15:44:23 +0300 Subject: [PATCH 24/49] Add unary operation --- final_task/pycalc/calc.py | 37 ++++++++++++++++++++---------------- final_task/pycalc/nincalc.py | 0 2 files changed, 21 insertions(+), 16 deletions(-) create mode 100644 final_task/pycalc/nincalc.py diff --git a/final_task/pycalc/calc.py b/final_task/pycalc/calc.py index 19c9351..96405e7 100644 --- a/final_task/pycalc/calc.py +++ b/final_task/pycalc/calc.py @@ -19,6 +19,8 @@ class CalcError(Exception): "^": 2, } +unary_operation = ['~'] + ops_list = {'abs', 'pow', 'round', 'log', 'log10', 'sqrt', 'sin', 'asin', 'cos', 'acos', 'hypot', 'tan', 'atan', 'copysign', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'ldexp', 'fsum', 'isfinite', 'isinf', 'isnan', 'modf', 'trunc', 'exp', 'expm1', 'log1p', 'log2', 'degrees', 'radians', 'cosh', 'sinh', @@ -86,8 +88,6 @@ def fix_missing_zero(expression): res = expression else: res = insert(regex, expression, token) - # match = re.split(regex, expression) - # res = '0'.join(match) return res @@ -104,10 +104,11 @@ def match_negative_value(expression): if not find: res = expression else: - res = insert(regex, expression, token) - # match = re.split(regex, expression) - # res = '1'.join(match) - return res + while find: + position = re.search(regex, expression).end() + expression = '~'.join([expression[:position - 1], expression[position:]]) + find = re.search(regex, expression) + return expression def insert_multiplication(expression): @@ -124,8 +125,6 @@ def insert_multiplication(expression): res = expression else: res = insert(regex, expression, token) - # match = re.split(regex, expression) - # res = '*'.join(match) return res @@ -140,7 +139,7 @@ def correct_expression(expression): raise CalcError('ERROR: invalid bracket expression') expression = insert_multiplication(match_negative_value(fix_missing_zero(fix_multi_operations(expression)))) regex = re.compile(r'(<=|==|!=|>=|log1p|^-\d+\.\d+|^-\d+|(?<=\W\W)\-\d+\.\d+|(?<=\W\W)\-\d+|' - r'(?<=\()\-\d+\.\d+|(?<=\()\-\d+|(?<=[a-z]\W)\-\d+\.\d+|(?<=[a-z]\W)\-\d+|' + r'(?<=\()\-\d+\.\d+|(?<=\()\-\d+|(?<=[a-z]\W)\-\d+\.\d+|(?<=[a-z]\W)\-\d+|(?<=\))\-|' r'\//|\/|\d+\.\d+|\d+|\W|\w+)') re_expr = re.split(regex, expression) re_expr = [x for x in re_expr if x and x != ' '] @@ -199,12 +198,16 @@ def calc_iteration(expression): Returns: The return result of calculation """ + inv = 1 stack = [] while expression: i = expression[0] if is_float(i): stack.append(float(i)) expression.remove(i) + elif i in unary_operation: + inv = -1 + expression.remove(i) elif i in comparison_operators: operator = expression.pop(0) a = stack.pop() @@ -223,7 +226,8 @@ def calc_iteration(expression): res = a > b stack.append(res) elif i in constants: - stack.append(getattr(math, i)) + stack.append(getattr(math, i) * inv) + inv = 1 expression.remove(i) elif i in binary_operations: if len(stack) > 1 and isinstance(stack[-1], (int, float)) and isinstance(stack[-2], (int, float)): @@ -262,11 +266,12 @@ def calc_iteration(expression): arg.append(calc_iteration(arg0.pop(0))) try: if ops == 'round': - stack.append(round(*arg)) + stack.append(round(*arg) * inv) elif ops == 'abs': - stack.append(abs(*arg)) + stack.append(abs(*arg) * inv) else: - stack.append(getattr(math, ops)(*arg)) + stack.append(getattr(math, ops)(*arg) * inv) + inv = 1 except ValueError: raise CalcError('ERROR: invalid argument for function {0}'.format(ops)) except TypeError: @@ -289,7 +294,7 @@ def to_postfix(expression): raise CalcError('ERROR: invalid operator "{0}"'.format(expression[0])) for item in range(len(expression)): i = expression[item] - if is_float(i) or i in ops_list or i in constants: + if is_float(i) or i in ops_list or i in constants or i in unary_operation: res.append(i) elif i in comparison_operators: while stack: @@ -314,7 +319,8 @@ def to_postfix(expression): elif i in binary_operations: if item + 1 >= len(expression) or expression[item + 1] in binary_operations: raise CalcError('ERROR: invalid operator "{0}"'.format(expression[:item + 1])) - if stack and stack[-1] in binary_operations and binary_operations[stack[-1]] >= binary_operations[i] and i != '^': + if stack and stack[-1] in binary_operations and binary_operations[stack[-1]] >= binary_operations[ + i] and i != '^': while stack and stack[-1] in binary_operations and binary_operations[stack[-1]] >= binary_operations[i]: res.append(stack.pop()) stack.append(i) @@ -350,4 +356,3 @@ def main(): if __name__ == '__main__': main() -#print(to_postfix("2+3*5>=10+12/2")) \ No newline at end of file diff --git a/final_task/pycalc/nincalc.py b/final_task/pycalc/nincalc.py new file mode 100644 index 0000000..e69de29 From 83b2efa06c192f36a36dd4f1e9091ed5e61c5cee Mon Sep 17 00:00:00 2001 From: Stacy Date: Thu, 8 Nov 2018 15:55:29 +0300 Subject: [PATCH 25/49] Add unary operation 2 --- final_task/pycalc/calc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/final_task/pycalc/calc.py b/final_task/pycalc/calc.py index 96405e7..a76ec1a 100644 --- a/final_task/pycalc/calc.py +++ b/final_task/pycalc/calc.py @@ -99,7 +99,7 @@ def match_negative_value(expression): The return string with correct negative value """ token = '1' - regex = r'(?<=^-)(?=[a-z])|(?<=\(-)(?=[a-z])|(?<=\W\-)(?=[a-z])' + regex = r'(?<=^-)(?=[a-z])|(?<=\(-)(?=[a-z])|(?<=\^\-)(?=[a-z])|(?<=\*\-)(?=[a-z])|(?<=\/\-)(?=[a-z])' find = re.search(regex, expression) if not find: res = expression From 97159e8d5b712ff4c23cbe9bb3d916d3da1c346e Mon Sep 17 00:00:00 2001 From: Stacy Date: Thu, 8 Nov 2018 16:21:51 +0300 Subject: [PATCH 26/49] Add unary operation 3 --- final_task/pycalc/calc.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/final_task/pycalc/calc.py b/final_task/pycalc/calc.py index a76ec1a..034cf0a 100644 --- a/final_task/pycalc/calc.py +++ b/final_task/pycalc/calc.py @@ -99,7 +99,7 @@ def match_negative_value(expression): The return string with correct negative value """ token = '1' - regex = r'(?<=^-)(?=[a-z])|(?<=\(-)(?=[a-z])|(?<=\^\-)(?=[a-z])|(?<=\*\-)(?=[a-z])|(?<=\/\-)(?=[a-z])' + regex = r'(?<=^-)(?=[a-z])|(?<=\(-)(?=[a-z])|(?<=\^\-)(?=[a-z])|(?<=\*\-)(?=[a-z])|(?<=\/\-)(?=[a-z])|(?<=\s\-)(?=[a-z])' find = re.search(regex, expression) if not find: res = expression @@ -317,8 +317,6 @@ def to_postfix(expression): ops_bracket.pop() res.append(i) elif i in binary_operations: - if item + 1 >= len(expression) or expression[item + 1] in binary_operations: - raise CalcError('ERROR: invalid operator "{0}"'.format(expression[:item + 1])) if stack and stack[-1] in binary_operations and binary_operations[stack[-1]] >= binary_operations[ i] and i != '^': while stack and stack[-1] in binary_operations and binary_operations[stack[-1]] >= binary_operations[i]: From f004e87d906ea528ac9e63b65954b04143a892e3 Mon Sep 17 00:00:00 2001 From: Stacy Date: Thu, 8 Nov 2018 17:29:34 +0300 Subject: [PATCH 27/49] More raise exeptions --- final_task/pycalc/calc.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/final_task/pycalc/calc.py b/final_task/pycalc/calc.py index 034cf0a..bc285c9 100644 --- a/final_task/pycalc/calc.py +++ b/final_task/pycalc/calc.py @@ -47,6 +47,12 @@ class CalcError(Exception): def fix_multi_operations(expression): + """Fix multiple operation with '+' and '-' + Args: + expression: input string with math expression + Returns: + The return fixed string + """ mul_operators = re.search(r'\+\+|\-\-|\+\-|-\+', expression) while mul_operators: expression = expression.replace('++', '+') @@ -99,7 +105,8 @@ def match_negative_value(expression): The return string with correct negative value """ token = '1' - regex = r'(?<=^-)(?=[a-z])|(?<=\(-)(?=[a-z])|(?<=\^\-)(?=[a-z])|(?<=\*\-)(?=[a-z])|(?<=\/\-)(?=[a-z])|(?<=\s\-)(?=[a-z])' + regex = re.compile(r'(?<=^-)(?=[a-z])|(?<=\(-)(?=[a-z])|(?<=\^\-)(?=[a-z])|(?<=\*\-)(?=[a-z])|' + r'(?<=\/\-)(?=[a-z])|(?<=\s\-)(?=[a-z])') find = re.search(regex, expression) if not find: res = expression @@ -137,6 +144,8 @@ def correct_expression(expression): """ if '()' in expression: raise CalcError('ERROR: invalid bracket expression') + elif expression == '': + raise CalcError('ERROR: empty expression') expression = insert_multiplication(match_negative_value(fix_missing_zero(fix_multi_operations(expression)))) regex = re.compile(r'(<=|==|!=|>=|log1p|^-\d+\.\d+|^-\d+|(?<=\W\W)\-\d+\.\d+|(?<=\W\W)\-\d+|' r'(?<=\()\-\d+\.\d+|(?<=\()\-\d+|(?<=[a-z]\W)\-\d+\.\d+|(?<=[a-z]\W)\-\d+|(?<=\))\-|' @@ -154,6 +163,8 @@ def get_arguments(expression): The return list of arguments """ ops = expression.pop(0) + if not expression: + raise CalcError('ERROR: invalid input') res = [] arg = [] point = 1 @@ -276,6 +287,8 @@ def calc_iteration(expression): raise CalcError('ERROR: invalid argument for function {0}'.format(ops)) except TypeError: raise CalcError('ERROR: invalid number of arguments for function {0}'.format(ops)) + if len(stack) > 1 or not is_float(stack[-1]): + raise CalcError('ERROR: invalid expression') return stack.pop() @@ -301,8 +314,8 @@ def to_postfix(expression): res.append(stack.pop()) res.append(i) elif i == '(': - if expression[item + 1] in binary_operations: - raise CalcError('ERROR: invalid operator "{0}"'.format(expression[item + 1])) + if item + 1 >= len(expression) or expression[item + 1] in binary_operations: + raise CalcError('ERROR: invalid operator') if res and res[-1] in ops_list: ops_bracket.append(i) stack.append(i) @@ -338,6 +351,12 @@ def to_postfix(expression): def evaluate(expression): + """Evaluate expression + Args: + expression: input string with math expression + Returns: + The return result of evaluate + """ return calc_iteration(to_postfix(expression)) From bee72f2e27cd5c160ded38885378de119ed4f75b Mon Sep 17 00:00:00 2001 From: Stacy Date: Thu, 8 Nov 2018 17:34:01 +0300 Subject: [PATCH 28/49] More raise exeptions 2 --- final_task/pycalc/calc.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/final_task/pycalc/calc.py b/final_task/pycalc/calc.py index bc285c9..5153fa8 100644 --- a/final_task/pycalc/calc.py +++ b/final_task/pycalc/calc.py @@ -220,6 +220,8 @@ def calc_iteration(expression): inv = -1 expression.remove(i) elif i in comparison_operators: + if len(expression) < 3: + raise CalcError('ERROR: invalid input') operator = expression.pop(0) a = stack.pop() b = calc_iteration(expression) From b2add3e8e09e2cbb1cf76642aec5bb6f8472bc99 Mon Sep 17 00:00:00 2001 From: Stacy Date: Thu, 8 Nov 2018 17:37:24 +0300 Subject: [PATCH 29/49] fix pep --- final_task/pycalc/calc.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/final_task/pycalc/calc.py b/final_task/pycalc/calc.py index 5153fa8..c6f3c4c 100644 --- a/final_task/pycalc/calc.py +++ b/final_task/pycalc/calc.py @@ -271,7 +271,7 @@ def calc_iteration(expression): stack.append(a ** b) else: stack.append(i) - expression.remove((i)) + expression.remove(i) if i in ops_list: arg = [] ops, arg0 = get_arguments(expression) @@ -332,8 +332,8 @@ def to_postfix(expression): ops_bracket.pop() res.append(i) elif i in binary_operations: - if stack and stack[-1] in binary_operations and binary_operations[stack[-1]] >= binary_operations[ - i] and i != '^': + if stack and stack[-1] in binary_operations and \ + binary_operations[stack[-1]] >= binary_operations[i] and i != '^': while stack and stack[-1] in binary_operations and binary_operations[stack[-1]] >= binary_operations[i]: res.append(stack.pop()) stack.append(i) From 61b0ea74df093bd55600296e1d860913c7186ae1 Mon Sep 17 00:00:00 2001 From: Stacy Date: Fri, 9 Nov 2018 19:44:15 +0300 Subject: [PATCH 30/49] Fixed implicit multiplication --- final_task/pycalc/calc.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/final_task/pycalc/calc.py b/final_task/pycalc/calc.py index c6f3c4c..c228faf 100644 --- a/final_task/pycalc/calc.py +++ b/final_task/pycalc/calc.py @@ -142,16 +142,23 @@ def correct_expression(expression): Returns: The return list of tokens """ + res = [] if '()' in expression: raise CalcError('ERROR: invalid bracket expression') elif expression == '': raise CalcError('ERROR: empty expression') expression = insert_multiplication(match_negative_value(fix_missing_zero(fix_multi_operations(expression)))) - regex = re.compile(r'(<=|==|!=|>=|log1p|^-\d+\.\d+|^-\d+|(?<=\W\W)\-\d+\.\d+|(?<=\W\W)\-\d+|' + regex = re.compile(r'(<=|==|!=|>=|e|pi|tau|inf|nan|log1p|^-\d+\.\d+|^-\d+|(?<=\W\W)\-\d+\.\d+|(?<=\W\W)\-\d+|' r'(?<=\()\-\d+\.\d+|(?<=\()\-\d+|(?<=[a-z]\W)\-\d+\.\d+|(?<=[a-z]\W)\-\d+|(?<=\))\-|' r'\//|\/|\d+\.\d+|\d+|\W|\w+)') re_expr = re.split(regex, expression) re_expr = [x for x in re_expr if x and x != ' '] + for i in reversed(range(len(re_expr))): + if i > 0: + if re_expr[i] == '(' and is_float(re_expr[i - 1]): + re_expr.insert(i, '*') + elif re_expr[i] in constants and re_expr[i - 1] in constants: + re_expr.insert(i, '*') return re_expr From 838d8f2d06fa318087cc7047d173800a2a9d49f6 Mon Sep 17 00:00:00 2001 From: Stacy Date: Fri, 9 Nov 2018 20:38:54 +0300 Subject: [PATCH 31/49] Fixed implicit multiplication 2 --- final_task/pycalc/calc.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/final_task/pycalc/calc.py b/final_task/pycalc/calc.py index c228faf..1e5d3ed 100644 --- a/final_task/pycalc/calc.py +++ b/final_task/pycalc/calc.py @@ -159,6 +159,8 @@ def correct_expression(expression): re_expr.insert(i, '*') elif re_expr[i] in constants and re_expr[i - 1] in constants: re_expr.insert(i, '*') + elif (re_expr[i] in constants or re_expr[i] in ops_list) and is_float(re_expr[i - 1]): + re_expr.insert(i, '*') return re_expr From 1695641ea18b65e8b8cbe75228b0c44a07f51e4d Mon Sep 17 00:00:00 2001 From: Stacy Date: Sat, 10 Nov 2018 20:31:45 +0300 Subject: [PATCH 32/49] Check CI --- final_task/pycalc/calc.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/final_task/pycalc/calc.py b/final_task/pycalc/calc.py index 1e5d3ed..bcc504e 100644 --- a/final_task/pycalc/calc.py +++ b/final_task/pycalc/calc.py @@ -148,7 +148,8 @@ def correct_expression(expression): elif expression == '': raise CalcError('ERROR: empty expression') expression = insert_multiplication(match_negative_value(fix_missing_zero(fix_multi_operations(expression)))) - regex = re.compile(r'(<=|==|!=|>=|e|pi|tau|inf|nan|log1p|^-\d+\.\d+|^-\d+|(?<=\W\W)\-\d+\.\d+|(?<=\W\W)\-\d+|' + regex = re.compile(r'(<=|==|!=|>=|(?<=[^a-z])e|e|pi|tau|inf|nan|log1p|^-\d+\.\d+|^-\d+|(?<=\W\W)\-\d+\.\d+|' + r'(?<=\W\W)\-\d+|' r'(?<=\()\-\d+\.\d+|(?<=\()\-\d+|(?<=[a-z]\W)\-\d+\.\d+|(?<=[a-z]\W)\-\d+|(?<=\))\-|' r'\//|\/|\d+\.\d+|\d+|\W|\w+)') re_expr = re.split(regex, expression) From 7e07b9ccf99980894fbaa4b48389ab28176e7063 Mon Sep 17 00:00:00 2001 From: Stacy Date: Sat, 10 Nov 2018 23:34:52 +0300 Subject: [PATCH 33/49] rm --- final_task/pycalc/nincalc.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 final_task/pycalc/nincalc.py diff --git a/final_task/pycalc/nincalc.py b/final_task/pycalc/nincalc.py deleted file mode 100644 index e69de29..0000000 From 38f3e905756f84452c3d42cfb0a1d610f71d1866 Mon Sep 17 00:00:00 2001 From: Stacy Date: Tue, 13 Nov 2018 22:52:21 +0300 Subject: [PATCH 34/49] Add use-modules support --- final_task/pycalc/calc.py | 74 ++++++++++++++++++++++++---------- final_task/pycalc/my_module.py | 0 2 files changed, 53 insertions(+), 21 deletions(-) create mode 100644 final_task/pycalc/my_module.py diff --git a/final_task/pycalc/calc.py b/final_task/pycalc/calc.py index bcc504e..e5da587 100644 --- a/final_task/pycalc/calc.py +++ b/final_task/pycalc/calc.py @@ -2,6 +2,7 @@ import re import math import argparse +import importlib class CalcError(Exception): @@ -81,7 +82,7 @@ def insert(regex, expression, token): def fix_missing_zero(expression): - """Inserts a zero in the number of the construction .number: .3 -> 0.3 + """Inserts a zero in the number of the construction .number: .3 => 0.3 Args: expression: input string with math expression Returns: @@ -98,13 +99,12 @@ def fix_missing_zero(expression): def match_negative_value(expression): - """Fix missing -1, if math function looks like -sin + """change unary minus to '~' for processing Args: expression: input string with math expression Returns: The return string with correct negative value """ - token = '1' regex = re.compile(r'(?<=^-)(?=[a-z])|(?<=\(-)(?=[a-z])|(?<=\^\-)(?=[a-z])|(?<=\*\-)(?=[a-z])|' r'(?<=\/\-)(?=[a-z])|(?<=\s\-)(?=[a-z])') find = re.search(regex, expression) @@ -143,15 +143,12 @@ def correct_expression(expression): The return list of tokens """ res = [] - if '()' in expression: - raise CalcError('ERROR: invalid bracket expression') - elif expression == '': + if expression == '': raise CalcError('ERROR: empty expression') expression = insert_multiplication(match_negative_value(fix_missing_zero(fix_multi_operations(expression)))) regex = re.compile(r'(<=|==|!=|>=|(?<=[^a-z])e|e|pi|tau|inf|nan|log1p|^-\d+\.\d+|^-\d+|(?<=\W\W)\-\d+\.\d+|' - r'(?<=\W\W)\-\d+|' - r'(?<=\()\-\d+\.\d+|(?<=\()\-\d+|(?<=[a-z]\W)\-\d+\.\d+|(?<=[a-z]\W)\-\d+|(?<=\))\-|' - r'\//|\/|\d+\.\d+|\d+|\W|\w+)') + r'(?<=\W\W)\-\d+|(?<=\()\-\d+\.\d+|(?<=\()\-\d+|(?<=[a-z]\W)\-\d+\.\d+|(?<=[a-z]\W)\-\d+|' + r'(?<=\))\-|\//|\/|\d+\.\d+|\d+|\W|\w+)') re_expr = re.split(regex, expression) re_expr = [x for x in re_expr if x and x != ' '] for i in reversed(range(len(re_expr))): @@ -212,10 +209,24 @@ def is_float(value): return False -def calc_iteration(expression): +def is_func(value): + """Check is value function + Args: + value: expression token + Returns: + The return True, if value is function and False if not + """ + if value[0].isalpha or value[0] == '_': + return True + else: + return False + + +def calc_iteration(expression, mod_list): """Calculate math expression Args: expression: input string with math expression + mod_list: list of module names Returns: The return result of calculation """ @@ -282,13 +293,16 @@ def calc_iteration(expression): else: stack.append(i) expression.remove(i) - if i in ops_list: + else: arg = [] ops, arg0 = get_arguments(expression) - while arg0: - arg.append(calc_iteration(arg0.pop(0))) + while arg0 and arg0 != [[]]: + arg.append(calc_iteration(arg0.pop(0), mod_list)) try: - if ops == 'round': + token = get_func(mod_list, ops) + if token: + stack.append(token(*arg)) + elif ops == 'round': stack.append(round(*arg) * inv) elif ops == 'abs': stack.append(abs(*arg) * inv) @@ -319,16 +333,14 @@ def to_postfix(expression): raise CalcError('ERROR: invalid operator "{0}"'.format(expression[0])) for item in range(len(expression)): i = expression[item] - if is_float(i) or i in ops_list or i in constants or i in unary_operation: - res.append(i) - elif i in comparison_operators: + if i in comparison_operators: while stack: res.append(stack.pop()) res.append(i) elif i == '(': if item + 1 >= len(expression) or expression[item + 1] in binary_operations: raise CalcError('ERROR: invalid operator') - if res and res[-1] in ops_list: + if res and is_func(res[-1]): ops_bracket.append(i) stack.append(i) elif i == ')': @@ -353,6 +365,8 @@ def to_postfix(expression): while stack[-1] != '(': res.append(stack.pop()) res.append(i) + elif is_float(i) or is_func(i) or i in unary_operation: + res.append(i) else: raise CalcError('ERROR: input invalid token "{0}"'.format(i)) for i in reversed(stack): @@ -362,26 +376,44 @@ def to_postfix(expression): return res -def evaluate(expression): +def evaluate(expression, mod_list): """Evaluate expression Args: expression: input string with math expression + mod_list: list of module names Returns: The return result of evaluate """ - return calc_iteration(to_postfix(expression)) + return calc_iteration(to_postfix(expression), mod_list) + + +def get_func(module_list, func_name): + """Find function in imported module + Args: + module_list: list of modules to import + func_name: function name + Returns: + The return function + """ + for mod in module_list: + imported = importlib.import_module(mod) + if hasattr(imported, func_name): + return getattr(imported, func_name) def main(): """Calc main function""" parser = argparse.ArgumentParser(description='Pure-python command-line calculator.') + parser.add_argument("-m", "--use-modules", dest='MODULE', nargs='+', + required=False, default='', help="additional modules to use") parser.add_argument("EXPRESSION", help="expression string to evaluate") args = parser.parse_args() try: - print(evaluate(args.EXPRESSION)) + print(evaluate(args.EXPRESSION, args.MODULE)) except CalcError as exception: print(exception) if __name__ == '__main__': main() + diff --git a/final_task/pycalc/my_module.py b/final_task/pycalc/my_module.py new file mode 100644 index 0000000..e69de29 From 0a1609cdb67c48b09c59e9e4d52e03f01e0bf10a Mon Sep 17 00:00:00 2001 From: Stacy Date: Wed, 14 Nov 2018 00:25:56 +0300 Subject: [PATCH 35/49] Fix convert to RPN --- final_task/pycalc/calc.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/final_task/pycalc/calc.py b/final_task/pycalc/calc.py index e5da587..8750531 100644 --- a/final_task/pycalc/calc.py +++ b/final_task/pycalc/calc.py @@ -216,7 +216,7 @@ def is_func(value): Returns: The return True, if value is function and False if not """ - if value[0].isalpha or value[0] == '_': + if value[0].isalpha() or value[0] == '_': return True else: return False @@ -245,7 +245,7 @@ def calc_iteration(expression, mod_list): raise CalcError('ERROR: invalid input') operator = expression.pop(0) a = stack.pop() - b = calc_iteration(expression) + b = calc_iteration(expression, mod_list) if operator == '<': res = a < b elif operator == '<=': @@ -366,7 +366,7 @@ def to_postfix(expression): res.append(stack.pop()) res.append(i) elif is_float(i) or is_func(i) or i in unary_operation: - res.append(i) + res.append(i) else: raise CalcError('ERROR: input invalid token "{0}"'.format(i)) for i in reversed(stack): @@ -405,7 +405,7 @@ def main(): """Calc main function""" parser = argparse.ArgumentParser(description='Pure-python command-line calculator.') parser.add_argument("-m", "--use-modules", dest='MODULE', nargs='+', - required=False, default='', help="additional modules to use") + required=False, default=None, help="additional modules to use") parser.add_argument("EXPRESSION", help="expression string to evaluate") args = parser.parse_args() try: @@ -416,4 +416,3 @@ def main(): if __name__ == '__main__': main() - From fa239f165e2db0de288d7ba90a6bb16618fef084 Mon Sep 17 00:00:00 2001 From: Stacy Date: Wed, 14 Nov 2018 00:41:17 +0300 Subject: [PATCH 36/49] Fix catch exceptions --- final_task/pycalc/calc.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/final_task/pycalc/calc.py b/final_task/pycalc/calc.py index 8750531..fa262e7 100644 --- a/final_task/pycalc/calc.py +++ b/final_task/pycalc/calc.py @@ -395,10 +395,13 @@ def get_func(module_list, func_name): Returns: The return function """ - for mod in module_list: - imported = importlib.import_module(mod) - if hasattr(imported, func_name): - return getattr(imported, func_name) + try: + for mod in module_list: + imported = importlib.import_module(mod) + if hasattr(imported, func_name): + return getattr(imported, func_name) + except Exception: + pass def main(): From 15b65c278c655927dbe3f9882f12ade5cb568e51 Mon Sep 17 00:00:00 2001 From: Stacy Date: Wed, 14 Nov 2018 00:54:27 +0300 Subject: [PATCH 37/49] Fix catch exceptions 2 --- final_task/pycalc/calc.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/final_task/pycalc/calc.py b/final_task/pycalc/calc.py index fa262e7..1aa273a 100644 --- a/final_task/pycalc/calc.py +++ b/final_task/pycalc/calc.py @@ -313,6 +313,8 @@ def calc_iteration(expression, mod_list): raise CalcError('ERROR: invalid argument for function {0}'.format(ops)) except TypeError: raise CalcError('ERROR: invalid number of arguments for function {0}'.format(ops)) + except AttributeError: + raise CalcError('ERROR: module "math" has no attribute {0}'.format(ops)) if len(stack) > 1 or not is_float(stack[-1]): raise CalcError('ERROR: invalid expression') return stack.pop() @@ -419,3 +421,4 @@ def main(): if __name__ == '__main__': main() + From 5a76986b1b69dbcac753e9630c404ba727c51194 Mon Sep 17 00:00:00 2001 From: Stacy Date: Wed, 14 Nov 2018 00:57:32 +0300 Subject: [PATCH 38/49] Fix pycodestyle --- final_task/pycalc/calc.py | 1 - 1 file changed, 1 deletion(-) diff --git a/final_task/pycalc/calc.py b/final_task/pycalc/calc.py index 1aa273a..755f876 100644 --- a/final_task/pycalc/calc.py +++ b/final_task/pycalc/calc.py @@ -421,4 +421,3 @@ def main(): if __name__ == '__main__': main() - From a1909bbeaaac4822564aef015a8fe220650e1d9e Mon Sep 17 00:00:00 2001 From: Stacy Date: Wed, 14 Nov 2018 11:58:13 +0300 Subject: [PATCH 39/49] Fix catch empty string exeption --- final_task/pycalc/calc.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/final_task/pycalc/calc.py b/final_task/pycalc/calc.py index 755f876..6a17c9e 100644 --- a/final_task/pycalc/calc.py +++ b/final_task/pycalc/calc.py @@ -61,6 +61,11 @@ def fix_multi_operations(expression): expression = expression.replace('+-', '-') expression = expression.replace('-+', '-') mul_operators = re.search(r'\+\+|\-\-|\+\-|-\+', expression) + try: + if expression[0] == '+': + expression = expression[1:] + except IndexError: + raise CalcError('ERROR: empty expression') return expression @@ -143,14 +148,14 @@ def correct_expression(expression): The return list of tokens """ res = [] - if expression == '': - raise CalcError('ERROR: empty expression') expression = insert_multiplication(match_negative_value(fix_missing_zero(fix_multi_operations(expression)))) regex = re.compile(r'(<=|==|!=|>=|(?<=[^a-z])e|e|pi|tau|inf|nan|log1p|^-\d+\.\d+|^-\d+|(?<=\W\W)\-\d+\.\d+|' r'(?<=\W\W)\-\d+|(?<=\()\-\d+\.\d+|(?<=\()\-\d+|(?<=[a-z]\W)\-\d+\.\d+|(?<=[a-z]\W)\-\d+|' r'(?<=\))\-|\//|\/|\d+\.\d+|\d+|\W|\w+)') re_expr = re.split(regex, expression) re_expr = [x for x in re_expr if x and x != ' '] + if expression[0] in binary_operations: + raise CalcError('ERROR: invalid operator "{0}"'.format(expression[0])) for i in reversed(range(len(re_expr))): if i > 0: if re_expr[i] == '(' and is_float(re_expr[i - 1]): @@ -331,8 +336,7 @@ def to_postfix(expression): stack = [] ops_bracket = [] expression = correct_expression(expression) - if expression[0] in binary_operations: - raise CalcError('ERROR: invalid operator "{0}"'.format(expression[0])) + for item in range(len(expression)): i = expression[item] if i in comparison_operators: From d62772ab8620ac84123175ab4f1d0a2c231ad84c Mon Sep 17 00:00:00 2001 From: Stacy Date: Wed, 14 Nov 2018 12:02:42 +0300 Subject: [PATCH 40/49] Test calc functions --- final_task/pycalc/test_calc.py | 146 +++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 final_task/pycalc/test_calc.py diff --git a/final_task/pycalc/test_calc.py b/final_task/pycalc/test_calc.py new file mode 100644 index 0000000..dae6770 --- /dev/null +++ b/final_task/pycalc/test_calc.py @@ -0,0 +1,146 @@ +import unittest +import time +from final_task.pycalc import calc + + +class TestCalc(unittest.TestCase): + + def test_correct_expression(self): + expect = ['~', 'sin', '(', '30', ')', '+', '0.25', '-', '(', '~', 'pow', '(', '2', ',', '2', ')', + ')'] + actual = calc.correct_expression('-sin(30)+.25-(-pow(2, 2))') + self.assertEqual(expect, actual) + + def test_insert_multiplication(self): + expect = '3*log10(2)*5+2*(2+3)*(7+1)*log1p(8)' + actual = calc.insert_multiplication('3log10(2)5+2(2+3)(7+1)log1p(8)') + self.assertEqual(expect, actual) + + def test_correct_negative_value(self): + expect = '~sin(40+5)+3(~pi)' + actual = calc.match_negative_value('-sin(40+5)+3(-pi)') + self.assertEqual(expect, actual) + + def test_with_zero_number(self): + expect = '5+0.25*0.3abs(-0.75)' + actual = calc.fix_missing_zero('5+.25*.3abs(-.75)') + self.assertEqual(expect, actual) + + def test_invalid_brackets(self): + with self.assertRaises(calc.CalcError): + calc.correct_expression('sin()+3') + calc.to_postfix('sin30(5-1)') + calc.to_postfix('(') + calc.to_postfix('1+2/3+5)-6') + + def test_invalid_input(self): + with self.assertRaises(calc.CalcError): + calc.to_postfix('2+3+sin(45)+pov(2, 6)') + calc.to_postfix('1+2*3-4@') + calc.to_postfix('1-#/2') + calc.to_postfix('3*6>>5+7') + calc.to_postfix('./7+2') + calc.to_postfix('.7*(sin())') + calc.to_postfix('23+3+') + calc.to_postfix('abs(-)') + calc.correct_expression('') + calc.correct_expression('-') + + def test_invalid_operators(self): + with self.assertRaises(calc.CalcError): + calc.to_postfix('1+2-*6-3') + calc.to_postfix('cos(*+30)/2+3') + + def test_reversed_polish_notation(self): + expect = ['3', '5', '2', '2', '^', '*', '3', '1', '-', '/', '+', 'abs', '-2', ')', '-', 'pow', '1', '1', '+', + ',', '1', '2', '*', '1', '+', ')', '+'] + actual = calc.to_postfix('3+5*2^2/(3-1)-abs(-2)+pow(1+1, 1*2+1)') + self.assertEqual(expect, actual) + + def test_comparison(self): + expect = True + actual = calc.calc_iteration(calc.to_postfix('2+3*5>=10+12/2'), None) + self.assertEqual(expect, actual) + + def test_addition(self): + expect = 8.05 + actual = calc.calc_iteration(calc.to_postfix('3.5+.5+2.75+1.3'), None) + self.assertEqual(expect, actual) + + def test_subtraction(self): + expect = 3 + actual = calc.calc_iteration(calc.to_postfix('25-22'), None) + self.assertEqual(expect, actual) + + def test_multiplication(self): + expect = 5 + actual = calc.calc_iteration(calc.to_postfix('2.5*2'), None) + self.assertEqual(expect, actual) + + def test_division(self): + expect = 6 + actual = calc.calc_iteration(calc.to_postfix('24/4'), None) + self.assertEqual(expect, actual) + + def test_division_by_zero(self): + with self.assertRaises(calc.CalcError): + calc.calc_iteration(calc.to_postfix('3 / 0'), None) + + def test_modulus(self): + expect = 7 + actual = calc.calc_iteration(calc.to_postfix('29%11'), None) + self.assertEqual(expect, actual) + + def test_modulo_by_zero(self): + with self.assertRaises(calc.CalcError): + calc.calc_iteration(calc.to_postfix('3 % 0'), None) + + def test_floor_division(self): + expect = 4 + actual = calc.calc_iteration(calc.to_postfix('9//2'), None) + self.assertEqual(expect, actual) + + def test_floor_division_by_zero(self): + with self.assertRaises(calc.CalcError): + calc.calc_iteration(calc.to_postfix('3 // 0'), None) + + def test_power(self): + expect = 8 + actual = calc.calc_iteration(calc.to_postfix('2^3'), None) + self.assertEqual(expect, actual) + + def test_round(self): + expect = 8 + actual = calc.calc_iteration(calc.to_postfix('round(7.51)'), None) + self.assertEqual(expect, actual) + + def test_abs(self): + expect = 6 + actual = calc.calc_iteration(calc.to_postfix('abs(-6)'), None) + self.assertEqual(expect, actual) + + def test_multi_operations(self): + expect = 2 + actual = calc.evaluate('--1-(+-1)', None) + self.assertEqual(expect, actual) + + def test_get_args(self): + expect = ('pow', [['2'], ['3']]) + actual = calc.get_arguments(['pow', '2', ',', '3', ')']) + self.assertEqual(expect, actual) + + def test_is_function(self): + expect = (True, True, False, False) + actual = (calc.is_func('_func_'), calc.is_func('my_func'), calc.is_func('.'), calc.is_func('44')) + self.assertEqual(expect, actual) + + def test_is_float(self): + expect = (True, True, False, False) + actual = (calc.is_float('-2'), calc.is_float('55'), calc.is_float('44,7'), calc.is_float('float')) + self.assertEqual(expect, actual) + + def test_calc_iteration(self): + expect = (1, 8) + actual = (calc.calc_iteration(['sin', 'pi', '2', '/', ')'], None), + calc.calc_iteration(['pow', '2', ',', '3', ')'], None)) + self.assertEqual(expect, actual) From 7cdf9c1920e708803361e2ffaa0622bb4ffe37da Mon Sep 17 00:00:00 2001 From: Stacy Date: Wed, 14 Nov 2018 12:09:51 +0300 Subject: [PATCH 41/49] Fix catch empty string exeption 2 --- final_task/pycalc/calc.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/final_task/pycalc/calc.py b/final_task/pycalc/calc.py index 6a17c9e..baa9129 100644 --- a/final_task/pycalc/calc.py +++ b/final_task/pycalc/calc.py @@ -61,11 +61,8 @@ def fix_multi_operations(expression): expression = expression.replace('+-', '-') expression = expression.replace('-+', '-') mul_operators = re.search(r'\+\+|\-\-|\+\-|-\+', expression) - try: - if expression[0] == '+': - expression = expression[1:] - except IndexError: - raise CalcError('ERROR: empty expression') + if expression and expression[0] == '+': + expression = expression[1:] return expression @@ -154,6 +151,8 @@ def correct_expression(expression): r'(?<=\))\-|\//|\/|\d+\.\d+|\d+|\W|\w+)') re_expr = re.split(regex, expression) re_expr = [x for x in re_expr if x and x != ' '] + if expression == '': + raise CalcError('ERROR: empty expression') if expression[0] in binary_operations: raise CalcError('ERROR: invalid operator "{0}"'.format(expression[0])) for i in reversed(range(len(re_expr))): From e3253f23c93c6b1c75814998e87b7eb20dc1c5fa Mon Sep 17 00:00:00 2001 From: Stacy Date: Wed, 14 Nov 2018 12:13:14 +0300 Subject: [PATCH 42/49] Fix catch empty string exeption 3 --- final_task/pycalc/calc.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/final_task/pycalc/calc.py b/final_task/pycalc/calc.py index baa9129..45e7629 100644 --- a/final_task/pycalc/calc.py +++ b/final_task/pycalc/calc.py @@ -153,8 +153,6 @@ def correct_expression(expression): re_expr = [x for x in re_expr if x and x != ' '] if expression == '': raise CalcError('ERROR: empty expression') - if expression[0] in binary_operations: - raise CalcError('ERROR: invalid operator "{0}"'.format(expression[0])) for i in reversed(range(len(re_expr))): if i > 0: if re_expr[i] == '(' and is_float(re_expr[i - 1]): @@ -335,7 +333,8 @@ def to_postfix(expression): stack = [] ops_bracket = [] expression = correct_expression(expression) - + if expression[0] in binary_operations: + raise CalcError('ERROR: invalid operator "{0}"'.format(expression[0])) for item in range(len(expression)): i = expression[item] if i in comparison_operators: From 1320e85607cc27a1b72aa12739e2a8aaf9545918 Mon Sep 17 00:00:00 2001 From: Stacy Date: Wed, 14 Nov 2018 12:17:51 +0300 Subject: [PATCH 43/49] Fix tests --- final_task/pycalc/my_module.py | 0 test_calc.py | 117 --------------------------------- 2 files changed, 117 deletions(-) delete mode 100644 final_task/pycalc/my_module.py delete mode 100644 test_calc.py diff --git a/final_task/pycalc/my_module.py b/final_task/pycalc/my_module.py deleted file mode 100644 index e69de29..0000000 diff --git a/test_calc.py b/test_calc.py deleted file mode 100644 index 810bc1e..0000000 --- a/test_calc.py +++ /dev/null @@ -1,117 +0,0 @@ -import unittest -from pycalc import calc - - -class TestCalc(unittest.TestCase): - - def test_correct_expression(self): - expect = ['-1', '*', 'sin', '(', '30', ')', '+', '0.25', '-', '(', '-1', '*', 'pow', '(', '2', ',', '2', ')', - ')'] - actual = calc.correct_expression('-sin(30)+.25-(-pow(2, 2))') - self.assertEqual(expect, actual) - - def test_insert_multiplication(self): - expect = '3*log10(2)*5+2*(2+3)*(7+1)*log1p(8)' - actual = calc.insert_multiplication('3log10(2)5+2(2+3)(7+1)log1p(8)') - self.assertEqual(expect, actual) - - def test_correct_negative_value(self): - expect = '-1sin(40+5)+3(-1pi)' - actual = calc.match_negative_value('-sin(40+5)+3(-pi)') - self.assertEqual(expect, actual) - - def test_with_zero_number(self): - expect = '5+0.25*0.3abs(-0.75)' - actual = calc.fix_missing_zero('5+.25*.3abs(-.75)') - self.assertEqual(expect, actual) - - def test_invalid_brackets(self): - with self.assertRaises(calc.CalcError): - calc.correct_expression('sin()+3') - calc.to_postfix('sin30(5-1)') - calc.to_postfix('(') - calc.to_postfix('1+2/3+5)-6') - - def test_invalid_input(self): - with self.assertRaises(calc.CalcError): - calc.to_postfix('2+3+sin(45)+pov(2, 6)') - calc.to_postfix('1+2*3-4@') - calc.to_postfix('1-#/2') - calc.to_postfix('3*6>>5+7') - calc.to_postfix('./7+2') - calc.to_postfix('.7*(sin())') - calc.to_postfix('23+3+') - calc.to_postfix('abs(-)') - - def test_invalid_operators(self): - with self.assertRaises(calc.CalcError): - calc.to_postfix('1+2-*6-3') - calc.to_postfix('cos(+30)/2+3') - - def test_reversed_polish_notation(self): - expect = ['3', '5', '2', '2', '^', '*', '3', '1', '-', '/', '+', 'abs', '-2', ')', '-', 'pow', '1', '1', '+', - ',', '1', '2', '*', '1', '+', ')', '+'] - actual = calc.to_postfix('3+5*2^2/(3-1)-abs(-2)+pow(1+1, 1*2+1)') - self.assertEqual(expect, actual) - - def test_comparison(self): - expect = True - actual = calc.calc_iteration(calc.to_postfix('2+3*5>=10+12/2')) - self.assertEqual(expect, actual) - - def test_addition(self): - expect = 8.05 - actual = calc.calc_iteration(calc.to_postfix('3.5+.5+2.75+1.3')) - self.assertEqual(expect, actual) - - def test_subtraction(self): - expect = 3 - actual = calc.calc_iteration(calc.to_postfix('25-22')) - self.assertEqual(expect, actual) - - def test_multiplication(self): - expect = 5 - actual = calc.calc_iteration(calc.to_postfix('2.5*2')) - self.assertEqual(expect, actual) - - def test_division(self): - expect = 6 - actual = calc.calc_iteration(calc.to_postfix('24/4')) - self.assertEqual(expect, actual) - - def test_division_by_zero(self): - with self.assertRaises(calc.CalcError): - calc.calc_iteration(calc.to_postfix('3 / 0')) - - def test_modulus(self): - expect = 7 - actual = calc.calc_iteration(calc.to_postfix('29%11')) - self.assertEqual(expect, actual) - - def test_modulo_by_zero(self): - with self.assertRaises(calc.CalcError): - calc.calc_iteration(calc.to_postfix('3 % 0')) - - def test_floor_division(self): - expect = 4 - actual = calc.calc_iteration(calc.to_postfix('9//2')) - self.assertEqual(expect, actual) - - def test_floor_division_by_zero(self): - with self.assertRaises(calc.CalcError): - calc.calc_iteration(calc.to_postfix('3 // 0')) - - def test_power(self): - expect = 8 - actual = calc.calc_iteration(calc.to_postfix('2^3')) - self.assertEqual(expect, actual) - - def test_round(self): - expect = 8 - actual = calc.calc_iteration(calc.to_postfix('round(7.51)')) - self.assertEqual(expect, actual) - - def test_abs(self): - expect = 6 - actual = calc.calc_iteration(calc.to_postfix('abs(-6)')) - self.assertEqual(expect, actual) \ No newline at end of file From b941d366e653b78329ff89a05e112612bc220cfd Mon Sep 17 00:00:00 2001 From: Stacy Date: Wed, 14 Nov 2018 12:24:36 +0300 Subject: [PATCH 44/49] Del url --- final_task/setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/final_task/setup.py b/final_task/setup.py index 90e23e8..e698217 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -6,7 +6,6 @@ description='Pure-python command-line calculator.', author='Anastasiya Holubeva', author_email='anastasyago@yandex.ru', - url='https://github.com/hfvh/Pycalc', packages=['pycalc'], entry_points={ 'console_scripts': [ From 8f9fee74f8de3e789478f440f207b5c4c6c54544 Mon Sep 17 00:00:00 2001 From: Stacy Date: Wed, 14 Nov 2018 12:53:42 +0300 Subject: [PATCH 45/49] Refactoring --- final_task/pycalc/calc.py | 90 +++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 41 deletions(-) diff --git a/final_task/pycalc/calc.py b/final_task/pycalc/calc.py index 45e7629..f514aae 100644 --- a/final_task/pycalc/calc.py +++ b/final_task/pycalc/calc.py @@ -224,6 +224,53 @@ def is_func(value): return False +def process_binary(i, stack): + if len(stack) > 1 and isinstance(stack[-1], (int, float)) and isinstance(stack[-2], (int, float)): + b = stack.pop() + a = stack.pop() + if i == '+': + stack.append(a + b) + elif i == '-': + stack.append(a - b) + elif i == '*': + stack.append(a * b) + elif i == '/': + try: + stack.append(a / b) + except ZeroDivisionError: + raise CalcError('ERROR: division by zero') + elif i == '//': + try: + stack.append(a // b) + except ZeroDivisionError: + raise CalcError('ERROR: floor division by zero') + elif i == '%': + try: + stack.append(a % b) + except ZeroDivisionError: + raise CalcError('ERROR: modulus by zero') + elif i == '^': + stack.append(a ** b) + else: + stack.append(i) + + +def process_comparison(operator, a, b): + if operator == '<': + res = a < b + elif operator == '<=': + res = a <= b + elif operator == '==': + res = a == b + elif operator == '!=': + res = a != b + elif operator == '>=': + res = a >= b + elif operator == '>': + res = a > b + return res + + def calc_iteration(expression, mod_list): """Calculate math expression Args: @@ -248,52 +295,13 @@ def calc_iteration(expression, mod_list): operator = expression.pop(0) a = stack.pop() b = calc_iteration(expression, mod_list) - if operator == '<': - res = a < b - elif operator == '<=': - res = a <= b - elif operator == '==': - res = a == b - elif operator == '!=': - res = a != b - elif operator == '>=': - res = a >= b - elif operator == '>': - res = a > b - stack.append(res) + stack.append(process_comparison(operator, a, b)) elif i in constants: stack.append(getattr(math, i) * inv) inv = 1 expression.remove(i) elif i in binary_operations: - if len(stack) > 1 and isinstance(stack[-1], (int, float)) and isinstance(stack[-2], (int, float)): - b = stack.pop() - a = stack.pop() - if i == '+': - stack.append(a + b) - elif i == '-': - stack.append(a - b) - elif i == '*': - stack.append(a * b) - elif i == '/': - try: - stack.append(a / b) - except ZeroDivisionError: - raise CalcError('ERROR: division by zero') - elif i == '//': - try: - stack.append(a // b) - except ZeroDivisionError: - raise CalcError('ERROR: floor division by zero') - elif i == '%': - try: - stack.append(a % b) - except ZeroDivisionError: - raise CalcError('ERROR: modulus by zero') - elif i == '^': - stack.append(a ** b) - else: - stack.append(i) + process_binary(i, stack) expression.remove(i) else: arg = [] From dea0967d1ae3917a29a312038dc0bb659f929be6 Mon Sep 17 00:00:00 2001 From: Stacy Date: Wed, 14 Nov 2018 13:06:36 +0300 Subject: [PATCH 46/49] Docstring --- final_task/pycalc/calc.py | 55 ++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/final_task/pycalc/calc.py b/final_task/pycalc/calc.py index f514aae..7e28047 100644 --- a/final_task/pycalc/calc.py +++ b/final_task/pycalc/calc.py @@ -224,38 +224,51 @@ def is_func(value): return False -def process_binary(i, stack): +def process_binary(item, stack): + """Process binary operation + Args: + item: binary operation + stack: list for add result of operation + """ if len(stack) > 1 and isinstance(stack[-1], (int, float)) and isinstance(stack[-2], (int, float)): b = stack.pop() a = stack.pop() - if i == '+': + if item == '+': stack.append(a + b) - elif i == '-': + elif item == '-': stack.append(a - b) - elif i == '*': + elif item == '*': stack.append(a * b) - elif i == '/': + elif item == '/': try: stack.append(a / b) except ZeroDivisionError: raise CalcError('ERROR: division by zero') - elif i == '//': + elif item == '//': try: stack.append(a // b) except ZeroDivisionError: raise CalcError('ERROR: floor division by zero') - elif i == '%': + elif item == '%': try: stack.append(a % b) except ZeroDivisionError: raise CalcError('ERROR: modulus by zero') - elif i == '^': + elif item == '^': stack.append(a ** b) else: - stack.append(i) + stack.append(item) def process_comparison(operator, a, b): + """Process comparison operation + Args: + operator: comparison operation + a: first value to compare + b: second value to compare + Returns: + The return result of comparison operation + """ if operator == '<': res = a < b elif operator == '<=': @@ -282,27 +295,27 @@ def calc_iteration(expression, mod_list): inv = 1 stack = [] while expression: - i = expression[0] - if is_float(i): - stack.append(float(i)) - expression.remove(i) - elif i in unary_operation: + item = expression[0] + if is_float(item): + stack.append(float(item)) + expression.remove(item) + elif item in unary_operation: inv = -1 expression.remove(i) - elif i in comparison_operators: + elif item in comparison_operators: if len(expression) < 3: raise CalcError('ERROR: invalid input') operator = expression.pop(0) a = stack.pop() b = calc_iteration(expression, mod_list) stack.append(process_comparison(operator, a, b)) - elif i in constants: - stack.append(getattr(math, i) * inv) + elif item in constants: + stack.append(getattr(math, item) * inv) inv = 1 - expression.remove(i) - elif i in binary_operations: - process_binary(i, stack) - expression.remove(i) + expression.remove(item) + elif item in binary_operations: + process_binary(item, stack) + expression.remove(item) else: arg = [] ops, arg0 = get_arguments(expression) From 1c61962149ffc76a079d8255d32c773d406b33b5 Mon Sep 17 00:00:00 2001 From: Stacy Date: Wed, 14 Nov 2018 13:10:36 +0300 Subject: [PATCH 47/49] Fix variable name --- final_task/pycalc/calc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/final_task/pycalc/calc.py b/final_task/pycalc/calc.py index 7e28047..f4e28ce 100644 --- a/final_task/pycalc/calc.py +++ b/final_task/pycalc/calc.py @@ -301,7 +301,7 @@ def calc_iteration(expression, mod_list): expression.remove(item) elif item in unary_operation: inv = -1 - expression.remove(i) + expression.remove(item) elif item in comparison_operators: if len(expression) < 3: raise CalcError('ERROR: invalid input') From fa09667612e0f729d92ae7bc717914bd2a5fed8c Mon Sep 17 00:00:00 2001 From: Stacy Date: Wed, 14 Nov 2018 13:50:23 +0300 Subject: [PATCH 48/49] Refactoring 2 --- final_task/pycalc/calc.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/final_task/pycalc/calc.py b/final_task/pycalc/calc.py index f4e28ce..2c3f4dd 100644 --- a/final_task/pycalc/calc.py +++ b/final_task/pycalc/calc.py @@ -22,13 +22,6 @@ class CalcError(Exception): unary_operation = ['~'] -ops_list = {'abs', 'pow', 'round', 'log', 'log10', 'sqrt', 'sin', 'asin', 'cos', 'acos', 'hypot', 'tan', 'atan', - 'copysign', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'ldexp', 'fsum', 'isfinite', 'isinf', - 'isnan', 'modf', 'trunc', 'exp', 'expm1', 'log1p', 'log2', 'degrees', 'radians', 'cosh', 'sinh', - 'tanh', 'acosh', 'asinh', 'atanh', 'erf', 'erfc', 'gamma', 'lgamma', 'inv', 'gcd', 'isclose', - 'isdexp', 'atan2', 'ceil', - } - constants = { "pi", "e", @@ -159,7 +152,7 @@ def correct_expression(expression): re_expr.insert(i, '*') elif re_expr[i] in constants and re_expr[i - 1] in constants: re_expr.insert(i, '*') - elif (re_expr[i] in constants or re_expr[i] in ops_list) and is_float(re_expr[i - 1]): + elif (re_expr[i] in constants or is_func(re_expr[i])) and is_float(re_expr[i - 1]): re_expr.insert(i, '*') return re_expr @@ -189,7 +182,7 @@ def get_arguments(expression): return ops, res else: arg.append(expression[0]) - elif expression[0] in ops_list: + elif is_func(expression[0]) and expression[0] not in constants: point += 1 arg.append(expression[0]) else: From 978d999502d4fb22d4a3645e60c614ac397f7e74 Mon Sep 17 00:00:00 2001 From: Stacy Date: Mon, 3 Dec 2018 18:50:59 +0300 Subject: [PATCH 49/49] Fix regex --- final_task/pycalc/calc.py | 47 ++++++--------------------------------- 1 file changed, 7 insertions(+), 40 deletions(-) diff --git a/final_task/pycalc/calc.py b/final_task/pycalc/calc.py index 2c3f4dd..80a1da5 100644 --- a/final_task/pycalc/calc.py +++ b/final_task/pycalc/calc.py @@ -59,23 +59,6 @@ def fix_multi_operations(expression): return expression -def insert(regex, expression, token): - """Inserts token in expression - Args: - regex: pattern to find position - expression: input string with math expression - token: token to insert - Returns: - The return fixed string - """ - find = re.search(regex, expression) - while find: - position = re.search(regex, expression).end() - expression = token.join([expression[:position], expression[position:]]) - find = re.search(regex, expression) - return expression - - def fix_missing_zero(expression): """Inserts a zero in the number of the construction .number: .3 => 0.3 Args: @@ -85,31 +68,19 @@ def fix_missing_zero(expression): """ token = '0' regex = r'(?<=\W)(?=\.\d)|(?<=^)(?=\.\d)' - find = re.search(regex, expression) - if not find: - res = expression - else: - res = insert(regex, expression, token) + res = re.sub(regex, token, expression) return res def match_negative_value(expression): - """change unary minus to '~' for processing + """change unary minus with functions and constants to '~' for processing Args: expression: input string with math expression Returns: The return string with correct negative value """ - regex = re.compile(r'(?<=^-)(?=[a-z])|(?<=\(-)(?=[a-z])|(?<=\^\-)(?=[a-z])|(?<=\*\-)(?=[a-z])|' - r'(?<=\/\-)(?=[a-z])|(?<=\s\-)(?=[a-z])') - find = re.search(regex, expression) - if not find: - res = expression - else: - while find: - position = re.search(regex, expression).end() - expression = '~'.join([expression[:position - 1], expression[position:]]) - find = re.search(regex, expression) + regex = re.compile(r'(?<=^)-(?=[a-z])|(?<=[\*\^\(\/])-(?=[a-z])|(?<=\s)-(?=[a-z])') + expression = re.sub(regex, '~', expression) return expression @@ -122,12 +93,8 @@ def insert_multiplication(expression): """ token = '*' regex = r'(?<=\))(?=\w+)|(?<=\))(?=\()|(?<=[^a-z][^a-z]\d)(?=\()|(?<=^\d)(?=\()|(?<=\d)(?=e|[a-z][a-z])' - find = re.search(regex, expression) - if not find: - res = expression - else: - res = insert(regex, expression, token) - return res + result = re.sub(regex, token, expression) + return result def correct_expression(expression): @@ -419,7 +386,7 @@ def get_func(module_list, func_name): if hasattr(imported, func_name): return getattr(imported, func_name) except Exception: - pass + return None def main():