From fce40ec17b6e1b6170e123da6514abc709b47034 Mon Sep 17 00:00:00 2001 From: Andrey Mirugin Date: Tue, 11 Dec 2018 20:57:23 +0300 Subject: [PATCH 01/16] calculator functionality --- final_task/pycalc/__init__.py | 0 final_task/pycalc/pycalc.py | 501 ++++++++++++++++++++++++++++++++++ final_task/setup.py | 13 + 3 files changed, 514 insertions(+) create mode 100644 final_task/pycalc/__init__.py create mode 100644 final_task/pycalc/pycalc.py diff --git a/final_task/pycalc/__init__.py b/final_task/pycalc/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py new file mode 100644 index 0000000..feea31a --- /dev/null +++ b/final_task/pycalc/pycalc.py @@ -0,0 +1,501 @@ +import math +import argparse + +constants = { + "e": math.e, + "pi": math.pi +} + +comparison = { + "<": lambda x, y: x < y, + ">": lambda x, y: x > y, + ">=": lambda x, y: x >= y, + "<=": lambda x, y: x <= y, + "!=": lambda x, y: x != y, + "==": lambda x, y: x == y +} + +signs = { + "+": lambda x, y: x + y, + "-": lambda x, y: x - y, + "*": lambda x, y: x * y, + "/": lambda x, y: x / y, + "%": lambda x, y: x % y, + "//": lambda x, y: x // y, + "^": lambda x, y: x ** y +} + +functions = { + "abs": abs, + "round": round, + "acosh": math.acosh, + "asinh": math.asinh, + "atanh": math.atanh, + "acos": math.acos, + "asin": math.asin, + "atan": math.atan, + "cosh": math.cosh, + "sinh": math.sinh, + "tanh": math.tanh, + "factorial": math.factorial, + "log10": math.log10, + "log2": math.log2, + "cos": math.cos, + "sin": math.sin, + "tan": math.tan, + "exp": math.exp, + "sqrt": math.sqrt, + "log": lambda x, y = math.e: math.log(x, y), + "pow": lambda x, y: x ** y +} + +priority = { + "+": 0, + "-": 0, + "*": 1, + "/": 1, + "%": 1, + "//": 1, + "^": 2 +} + + +class BracketsNotBalancedException(Exception): + pass + + +class UnknownFunctionException(Exception): + pass + + +class UnknownElementException(Exception): + pass + + +class UnexpectedSpaceExeption(Exception): + pass + + +class EmptyExpressionException(Exception): + pass + + +class MissingArgumentException(Exception): + pass + + +class FunctionCalculatingException(Exception): + pass + + +def is_float(string): + """Check is string float""" + try: + float(string) + return True + except ValueError: + return False + + +def replace_long_unaries(expression): + """Replaces '-' and '+' signs to one '+' or '-' sign, if there are more then one of them""" + number_of_minuses = 0 + number_of_symbols = 0 + start = -1 + is_unar = False + end = -1 + result_expression = expression + for i in range(len(result_expression)): + if result_expression[i] == "+" or result_expression[i] == "-": + if not is_unar: + start = i + is_unar = True + if result_expression[i] == "-": + number_of_minuses += 1 + number_of_symbols += 1 + elif start != -1 and number_of_symbols > 1: + end = i + if number_of_minuses % 2: + result_expression = result_expression.replace( + result_expression[start:end], "-") + result_expression = replace_long_unaries(result_expression) + break + else: + result_expression = result_expression.replace( + result_expression[start:end], "+") + result_expression = replace_long_unaries(result_expression) + break + elif number_of_symbols == 1: + start = -1 + is_unar = False + number_of_minuses = 0 + number_of_symbols = 0 + if start != -1 and end == -1: + raise MissingArgumentException( + "Not enough argumets for binary operation") + return result_expression + + +def checking_and_solving_comparison(expression): + """ + Checks the expression for the presence of comparison and solves it if it is + Parameters + ---------- + expression : str + The expression that is searched for comparison + Returns + ------- + str + Resolved comparison if there was a comparison or expression unchanged if there no comparison + boolean + Is the expression comparison + """ + is_comparison = False + for i in range(len(expression)): + if comparison.get(expression[i]) != None: + is_comparison = True + if comparison.get(expression[i]+expression[i+1]) != None: + return [comparison[expression[i]+expression[i+1]](calc(expression[0:i]), + calc(expression[i+2:len(expression)])), is_comparison] + else: + return [comparison[expression[i]](calc(expression[0:i]), + calc(expression[i+1:len(expression)])), is_comparison] + elif i+1 < len(expression) and comparison.get(expression[i]+expression[i+1]) != None: + is_comparison = True + return [comparison[expression[i]+expression[i+1]](calc(expression[0:i]), + calc(expression[i+2:len(expression)])), is_comparison] + return [expression, is_comparison] + + +def find_left_element(expression, pointer): + """ + Find nearest element from the left to a pointer + Parameters + ---------- + expression : str + The expression that is searched for element + pointer : int + Position to start searching + Returns + ------- + str + Nearest element from the left to a pointer + int + Start position of element + """ + first_number = "" + start = 0 + for i in range(1, pointer+1): + prev = first_number + first_number = expression[pointer-i]+first_number + if not is_float(first_number): + first_number = prev + start = pointer-i+1 + break + elif expression[pointer-i] == '+' or expression[pointer-i] == '-': + if pointer-i-1 >= 0 and (expression[pointer-i-1].isdigit() or expression[pointer-i-1] == ")"): + first_number = prev + start = pointer-i+1 + break + else: + start = pointer-i + break + return [first_number, start] + + +def find_right_element(expression, pointer): + """ + Find nearest element from the right to a pointer + Parameters + ---------- + expression : str + The expression that is searched for element + pointer : int + Position to start searching + Returns + ------- + str + Nearest element from the right to a pointer + int + End position of element + """ + end = 0 + flag = False + second_number = "" + for i in range(pointer+1, len(expression)): + prev = second_number + second_number += expression[i] + if second_number == '-': + continue + elif not is_float(second_number): + flag = True + second_number = prev + end = i-1 + break + if not flag: + end = i + return [second_number, end] + + +def calc_by_position_of_sign(position, expression): + """ + Calculates two nearest elements (from the left and right) according to a sign at 'position' + Parameters + ---------- + position : int + Position of sign in expression + expression : str + The expression that is calculated + Returns + ------- + str + Result of calculation + int + Start position of left element + int + End of right element + """ + if position == 0: + raise MissingArgumentException( + "Not enough argumets for binary operation") + right_pointer = position + left_pointer = position + if position+1 == len(expression): + raise MissingArgumentException( + "Not enough argumets for binary operation") + if signs.get(expression[position]+expression[position+1]) != None: + right_pointer = position+1 + elif signs.get(expression[position]+expression[position-1]) != None: + left_pointer = position-1 + [first_number, start] = find_left_element(expression, left_pointer) + [second_number, end] = find_right_element(expression, right_pointer) + if first_number == "" or second_number == "": + raise MissingArgumentException( + "Not enough argumets for binary operation") + if left_pointer == right_pointer: + return [signs[expression[position]](float(first_number), float(second_number)), start, end] + else: + return [signs["//"](float(first_number), float(second_number)), start, end] + + +def calc_string(expression): + """ + Calculates expression, consisting of float numbers and signs of operations + Parameters + ---------- + expression : str + The expression that is calculated + Returns + ------- + float + Result of calculation + """ + if is_float(expression): + return float(expression) + maxprior = -1 + position = 0 + for i in range(0, len(expression)): + if (expression[i] == '-' or expression[i] == '+') and i == 0: + continue + if expression[i] in ('+', '-', '*', '/', '^', '%') and priority[expression[i]] > maxprior: + position = i + maxprior = priority[expression[i]] + elif expression[i] in ('+', '-', '*', '/', '^', '%') and maxprior == 2 and priority[expression[i]] >= maxprior: + position = i + maxprior = priority[expression[i]] + result = calc_by_position_of_sign(position, expression) + new_string = expression.replace( + expression[result[1]:result[2]+1], str("{:.16f}".format(result[0]))) + return calc_string(new_string) + + +def find_and_replace_consts(expression): + """Replaces constatnts in the 'expression'""" + if is_float(expression): + return expression + temp_expression = expression + for i in constants: + temp_expression = temp_expression.replace(i, str(constants[i])) + return temp_expression + + +def add_implicit_mult(expression): + """Adds multiplication sign where it is given implicitly""" + result_expression = expression + expr_left = "" + expr_right = "" + was_float = False + was_const = False + for i in range(len(result_expression)): + expr_right += result_expression[i] + if result_expression[i] in ("+", "-", "*", "^", "=", ">", "<", "!", "/", "(", ")", ","): + if result_expression[i] == ")" and i+1 < len(result_expression): + if not result_expression[i+1] in ("+", "-", "*", "^", "=", ">", "<", "!", "/", ")", ","): + result_expression = result_expression[0:i+1] + \ + "*"+result_expression[i+1:len(result_expression)] + expr_left = expr_right + expr_right = "" + was_const = False + was_float = False + if result_expression[i] == "(" and is_float(expr_left[0:len(expr_left)-1]): + result_expression = result_expression[0:i] + \ + "*"+result_expression[i:len(result_expression)] + elif is_float(expr_right): + was_float = True + elif not is_float(expr_right) and was_float: + result_expression = result_expression[0:i] + \ + "*"+result_expression[i:len(result_expression)] + was_float = False + elif constants.get(expr_right) != None: + was_const = True + elif constants.get(expr_right) == None and was_const: + is_func = False + temp = expr_right + for j in range(i+1, len(result_expression)): + if functions.get(temp) != None: + is_func = True + break + temp += result_expression[j] + if not is_func: + result_expression = result_expression[0:i] + \ + "*"+result_expression[i:len(result_expression)] + was_const = False + return result_expression + + +def solve_bracets(expression): + """Repalces expression in brackets on it's value""" + result_string = expression + start = -1 + brackets_balance = 0 + for i in range(len(expression)): + if expression[i] == '(': + if brackets_balance == 0: + start = i + brackets_balance += 1 + elif expression[i] == ')': + brackets_balance -= 1 + if start != -1 and brackets_balance == 0: + end = i + result_string = result_string.replace( + result_string[start:end+1], str("{:.16f}".format(calc(result_string[start+1:end])))) + result_string = solve_bracets(result_string) + break + if brackets_balance != 0: + raise BracketsNotBalancedException("brackets not balanced") + return result_string + + +def solve_functions(expression): + """Findes and replaces functions to it's value. Solves expression in arguments, if it is necessary""" + res_str = expression + is_func = False + brackets_balance = 0 + temp = "" + end = 0 + first_end = end + for i in range(len(expression)): + if not (res_str[i].isdigit() or res_str[i] in (".", '+', '-', '*', '/', '^', '%', ')', '(')): + if not is_func: + func_start = i + is_func = True + temp += res_str[i] + elif not res_str[i] in (".", '+', '-', '*', '/', '^', '%', ')', '(') and is_func: + temp += res_str[i] + elif res_str[i] == '(' and is_func: + if functions.get(temp) != None: + start = i+1 + for j in range(i, len(expression)): + if expression[j] == '(': + brackets_balance += 1 + elif expression[j] == ',' and brackets_balance == 1: + first_end = j + elif expression[j] == ')': + brackets_balance -= 1 + if brackets_balance == 0: + end = j + break + if first_end: + try: + res_str = res_str.replace(res_str[func_start:end+1], + str("{:.16f}".format(functions[temp](calc(res_str[start:first_end]), calc(res_str[first_end+1:end]))))) + res_str = solve_functions(res_str) + break + except Exception: + raise FunctionCalculatingException(f"Incorrect arguments in function '{temp}'") + else: + try: + res_str = res_str.replace(res_str[func_start:end+1], + str("{:.16f}".format(functions[temp](calc(res_str[start:end]))))) + res_str = solve_functions(res_str) + break + except Exception: + raise FunctionCalculatingException(f"Incorrect arguments in function '{temp}'") + else: + raise UnknownFunctionException(f"Unknown function '{temp}'") + elif res_str[i] in (".", '+', '-', '*', '/', '^', '%'): + temp = "" + is_func = False + if temp != "" and functions.get(temp) == None and constants.get(temp) == None: + raise UnknownElementException(f"Unknown element '{temp}'") + return res_str + + +def replace_spaces(expression): + """Findes and removes unenecessary spaces near signs""" + result_expression = expression + space_pos = result_expression.find(" ") + while space_pos != -1: + if space_pos-1 >= 0 and result_expression[space_pos-1] in ("+", "-", "*", "^", "=", ">", "<", "!", "/", ","): + if space_pos+1 < len(result_expression) and result_expression[space_pos+1] in ("*", "^", "=", ">", "<", "!", "/"): + raise UnexpectedSpaceExeption(f"Unexpected space between '{result_expression[space_pos-1]}' and '{result_expression[space_pos+1]}'") + else: + result_expression = result_expression.replace( + result_expression[space_pos], "", 1) + elif space_pos+1 < len(result_expression) and result_expression[space_pos+1] in ("+", "-", "*", "^", "=", ">", "<", "!", "/"): + if space_pos-1 >= 0 and result_expression[space_pos-1] in ("+", "-", "*", "^", "=", ">", "<", "!", "/"): + raise UnexpectedSpaceExeption(f"Unexpected space between '{result_expression[space_pos-1]}' and '{result_expression[space_pos+1]}'") + else: + result_expression = result_expression.replace( + result_expression[space_pos], "", 1) + else: + raise UnexpectedSpaceExeption("Unexpected space") + space_pos = result_expression.find(" ") + return result_expression + + +def calc(expression): + """Calculate expression with no spaces""" + result_expression = expression + result_expression = replace_long_unaries(result_expression) + result_expression = solve_functions(result_expression) + result_expression = replace_long_unaries(result_expression) + result_expression = solve_bracets(result_expression) + result_expression = replace_long_unaries(result_expression) + result_expression = find_and_replace_consts(result_expression) + result_expression = replace_long_unaries(result_expression) + return calc_string(result_expression) + + +def main(): + """Main function, that parse arguments and gives the result of calculation""" + parser = argparse.ArgumentParser( + description='Pure-python command-line calculator.') + parser.add_argument('EXPRESSION', type=str, + help='expression string to evaluate') + string = parser.parse_args() + try: + expression = string.EXPRESSION + expression = replace_spaces(expression) + expression = add_implicit_mult(expression) + [expression, is_comparison] = checking_and_solving_comparison( + expression) + if is_comparison: + return expression + return calc(expression) + except Exception as e: + return f"ERROR: {e}" + + +if __name__ == "__main__": + main() diff --git a/final_task/setup.py b/final_task/setup.py index e69de29..643fcc3 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -0,0 +1,13 @@ +from setuptools import setup, find_packages + +setup( + name = "pycalc", + author = "Andrey Mirugin", + version = "1.0", + author_email = "andrey.mirugin@gmail.com", + description = ("Pure-python command-line calculator."), + packages=find_packages(), + entry_points={ + 'console_script':['pycalc=pycalc.pycalc:main'] + } +) From c75fe6849f0c17ff1381580186df644c86f51d86 Mon Sep 17 00:00:00 2001 From: Andrey Mirugin Date: Tue, 11 Dec 2018 21:22:17 +0300 Subject: [PATCH 02/16] Removed old file --- final_task/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 final_task/__init__.py diff --git a/final_task/__init__.py b/final_task/__init__.py deleted file mode 100644 index e69de29..0000000 From 269be805a7aca29e4577d86cd3b2d9debc5f78ca Mon Sep 17 00:00:00 2001 From: Andrey Mirugin Date: Tue, 11 Dec 2018 21:34:06 +0300 Subject: [PATCH 03/16] delete pycalc folder --- final_task/{pycalc => }/__init__.py | 0 final_task/{pycalc => }/pycalc.py | 0 final_task/setup.py | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) rename final_task/{pycalc => }/__init__.py (100%) rename final_task/{pycalc => }/pycalc.py (100%) diff --git a/final_task/pycalc/__init__.py b/final_task/__init__.py similarity index 100% rename from final_task/pycalc/__init__.py rename to final_task/__init__.py diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc.py similarity index 100% rename from final_task/pycalc/pycalc.py rename to final_task/pycalc.py diff --git a/final_task/setup.py b/final_task/setup.py index 643fcc3..3256917 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -8,6 +8,6 @@ description = ("Pure-python command-line calculator."), packages=find_packages(), entry_points={ - 'console_script':['pycalc=pycalc.pycalc:main'] + 'console_script':['pycalc=pycalc:main'] } ) From 8cd32df9155101b4b765d6483cd55902bbdda812 Mon Sep 17 00:00:00 2001 From: Andrey Mirugin Date: Tue, 11 Dec 2018 21:44:47 +0300 Subject: [PATCH 04/16] print of results added --- final_task/pycalc.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/final_task/pycalc.py b/final_task/pycalc.py index feea31a..40adcb8 100644 --- a/final_task/pycalc.py +++ b/final_task/pycalc.py @@ -152,15 +152,15 @@ def checking_and_solving_comparison(expression): """ is_comparison = False for i in range(len(expression)): - if comparison.get(expression[i]) != None: + if comparison.get(expression[i]) is not None: is_comparison = True - if comparison.get(expression[i]+expression[i+1]) != None: + if comparison.get(expression[i]+expression[i+1]) is not None: return [comparison[expression[i]+expression[i+1]](calc(expression[0:i]), calc(expression[i+2:len(expression)])), is_comparison] else: return [comparison[expression[i]](calc(expression[0:i]), calc(expression[i+1:len(expression)])), is_comparison] - elif i+1 < len(expression) and comparison.get(expression[i]+expression[i+1]) != None: + elif i+1 < len(expression) and comparison.get(expression[i]+expression[i+1]) is not None: is_comparison = True return [comparison[expression[i]+expression[i+1]](calc(expression[0:i]), calc(expression[i+2:len(expression)])), is_comparison] @@ -242,10 +242,10 @@ def calc_by_position_of_sign(position, expression): Calculates two nearest elements (from the left and right) according to a sign at 'position' Parameters ---------- - position : int - Position of sign in expression expression : str The expression that is calculated + position : int + Position of sign in expression Returns ------- str @@ -263,9 +263,9 @@ def calc_by_position_of_sign(position, expression): if position+1 == len(expression): raise MissingArgumentException( "Not enough argumets for binary operation") - if signs.get(expression[position]+expression[position+1]) != None: + if signs.get(expression[position]+expression[position+1]) is not None: right_pointer = position+1 - elif signs.get(expression[position]+expression[position-1]) != None: + elif signs.get(expression[position]+expression[position-1]) is not None: left_pointer = position-1 [first_number, start] = find_left_element(expression, left_pointer) [second_number, end] = find_right_element(expression, right_pointer) @@ -346,13 +346,13 @@ def add_implicit_mult(expression): result_expression = result_expression[0:i] + \ "*"+result_expression[i:len(result_expression)] was_float = False - elif constants.get(expr_right) != None: + elif constants.get(expr_right) is not None: was_const = True - elif constants.get(expr_right) == None and was_const: + elif constants.get(expr_right) is None and was_const: is_func = False temp = expr_right for j in range(i+1, len(result_expression)): - if functions.get(temp) != None: + if functions.get(temp) is not None: is_func = True break temp += result_expression[j] @@ -403,7 +403,7 @@ def solve_functions(expression): elif not res_str[i] in (".", '+', '-', '*', '/', '^', '%', ')', '(') and is_func: temp += res_str[i] elif res_str[i] == '(' and is_func: - if functions.get(temp) != None: + if functions.get(temp) is not None: start = i+1 for j in range(i, len(expression)): if expression[j] == '(': @@ -436,7 +436,7 @@ def solve_functions(expression): elif res_str[i] in (".", '+', '-', '*', '/', '^', '%'): temp = "" is_func = False - if temp != "" and functions.get(temp) == None and constants.get(temp) == None: + if temp != "" and functions.get(temp) is None and constants.get(temp) is None: raise UnknownElementException(f"Unknown element '{temp}'") return res_str @@ -481,8 +481,7 @@ def main(): """Main function, that parse arguments and gives the result of calculation""" parser = argparse.ArgumentParser( description='Pure-python command-line calculator.') - parser.add_argument('EXPRESSION', type=str, - help='expression string to evaluate') + parser.add_argument('EXPRESSION', help='expression string to evaluate') string = parser.parse_args() try: expression = string.EXPRESSION @@ -491,11 +490,12 @@ def main(): [expression, is_comparison] = checking_and_solving_comparison( expression) if is_comparison: - return expression - return calc(expression) + print(expression) + else: + print(calc(expression)) except Exception as e: - return f"ERROR: {e}" - + print (f"ERROR: {e}") + return e if __name__ == "__main__": main() From dd32e59a03141010902877ce1229de2e13a8b2c8 Mon Sep 17 00:00:00 2001 From: Andrey Mirugin Date: Tue, 11 Dec 2018 21:50:07 +0300 Subject: [PATCH 05/16] little changes in codestyle and fixes --- final_task/{ => pycalc}/__init__.py | 0 final_task/{ => pycalc}/pycalc.py | 0 final_task/setup.py | 7 ++++--- pycalc_checker.py | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) rename final_task/{ => pycalc}/__init__.py (100%) rename final_task/{ => pycalc}/pycalc.py (100%) diff --git a/final_task/__init__.py b/final_task/pycalc/__init__.py similarity index 100% rename from final_task/__init__.py rename to final_task/pycalc/__init__.py diff --git a/final_task/pycalc.py b/final_task/pycalc/pycalc.py similarity index 100% rename from final_task/pycalc.py rename to final_task/pycalc/pycalc.py diff --git a/final_task/setup.py b/final_task/setup.py index 3256917..c0b0105 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -6,8 +6,9 @@ version = "1.0", author_email = "andrey.mirugin@gmail.com", description = ("Pure-python command-line calculator."), - packages=find_packages(), - entry_points={ - 'console_script':['pycalc=pycalc:main'] + packages = find_packages(), + entry_points = { + 'console_script': ['pycalc=pycalc.pycalc:main'] } ) + diff --git a/pycalc_checker.py b/pycalc_checker.py index 0b48a2b..c410e0f 100644 --- a/pycalc_checker.py +++ b/pycalc_checker.py @@ -5,7 +5,7 @@ from termcolor import colored -PYCALC_UTIL_NAME = "pycalc" +PYCALC_UTIL_NAME = "./pycalc" RETURN_CODE = 0 From c864d8af520cbde6a9f9df19a9f635e1484cdbd5 Mon Sep 17 00:00:00 2001 From: Andrey Mirugin Date: Tue, 11 Dec 2018 22:09:09 +0300 Subject: [PATCH 06/16] try to fix tests --- pycalc_checker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pycalc_checker.py b/pycalc_checker.py index c410e0f..0b48a2b 100644 --- a/pycalc_checker.py +++ b/pycalc_checker.py @@ -5,7 +5,7 @@ from termcolor import colored -PYCALC_UTIL_NAME = "./pycalc" +PYCALC_UTIL_NAME = "pycalc" RETURN_CODE = 0 From 291d37796908ff883afeecf26977d3fbd142e4b6 Mon Sep 17 00:00:00 2001 From: Andrey Mirugin Date: Sun, 16 Dec 2018 13:38:04 +0300 Subject: [PATCH 07/16] fixed console start and little bug fixes --- final_task/pycalc/pycalc.py | 13 +++++++------ final_task/setup.py | 18 ++++++++++-------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index 40adcb8..e4435a2 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -328,9 +328,9 @@ def add_implicit_mult(expression): was_const = False for i in range(len(result_expression)): expr_right += result_expression[i] - if result_expression[i] in ("+", "-", "*", "^", "=", ">", "<", "!", "/", "(", ")", ","): + if result_expression[i] in ("+", "-", "*", "^","%", "=", ">", "<", "!", "/", "(", ")", ","): if result_expression[i] == ")" and i+1 < len(result_expression): - if not result_expression[i+1] in ("+", "-", "*", "^", "=", ">", "<", "!", "/", ")", ","): + if not result_expression[i+1] in ("+", "-", "*","%", "^", "=", ">", "<", "!", "/", ")", ","): result_expression = result_expression[0:i+1] + \ "*"+result_expression[i+1:len(result_expression)] expr_left = expr_right @@ -446,14 +446,14 @@ def replace_spaces(expression): result_expression = expression space_pos = result_expression.find(" ") while space_pos != -1: - if space_pos-1 >= 0 and result_expression[space_pos-1] in ("+", "-", "*", "^", "=", ">", "<", "!", "/", ","): + if space_pos-1 >= 0 and result_expression[space_pos-1] in ("+", "-", "*", "^","%", "=", ">", "<", "!", "/", ","): if space_pos+1 < len(result_expression) and result_expression[space_pos+1] in ("*", "^", "=", ">", "<", "!", "/"): raise UnexpectedSpaceExeption(f"Unexpected space between '{result_expression[space_pos-1]}' and '{result_expression[space_pos+1]}'") else: result_expression = result_expression.replace( result_expression[space_pos], "", 1) - elif space_pos+1 < len(result_expression) and result_expression[space_pos+1] in ("+", "-", "*", "^", "=", ">", "<", "!", "/"): - if space_pos-1 >= 0 and result_expression[space_pos-1] in ("+", "-", "*", "^", "=", ">", "<", "!", "/"): + elif space_pos+1 < len(result_expression) and result_expression[space_pos+1] in ("+", "-", "*", "^","%", "=", ">", "<", "!", "/"): + if space_pos-1 >= 0 and result_expression[space_pos-1] in ("+", "-", "*", "^","%", "=", ">", "<", "!", "/"): raise UnexpectedSpaceExeption(f"Unexpected space between '{result_expression[space_pos-1]}' and '{result_expression[space_pos+1]}'") else: result_expression = result_expression.replace( @@ -497,5 +497,6 @@ def main(): print (f"ERROR: {e}") return e + if __name__ == "__main__": - main() + main() \ No newline at end of file diff --git a/final_task/setup.py b/final_task/setup.py index c0b0105..4a08ef9 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -1,14 +1,16 @@ from setuptools import setup, find_packages setup( - name = "pycalc", - author = "Andrey Mirugin", - version = "1.0", - author_email = "andrey.mirugin@gmail.com", - description = ("Pure-python command-line calculator."), - packages = find_packages(), - entry_points = { - 'console_script': ['pycalc=pycalc.pycalc:main'] + name="Calculator", + author="Andrey Mirugin", + version="1.0", + author_email="andrey.mirugin@gmail.com", + description=("Pure-python command-line calculator."), + packages=find_packages(), + entry_points={ + 'console_scripts': [ + 'pycalc = pycalc.pycalc:main', + ] } ) From d1d84ba103e28bb7deb46628405a0620cde27150 Mon Sep 17 00:00:00 2001 From: Andrey Mirugin Date: Sun, 16 Dec 2018 14:05:59 +0300 Subject: [PATCH 08/16] fix pep8 codestyle --- final_task/pycalc/pycalc.py | 42 ++++++++++++++++++++----------------- final_task/setup.py | 1 - 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index e4435a2..0e31dbf 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -328,9 +328,9 @@ def add_implicit_mult(expression): was_const = False for i in range(len(result_expression)): expr_right += result_expression[i] - if result_expression[i] in ("+", "-", "*", "^","%", "=", ">", "<", "!", "/", "(", ")", ","): + if result_expression[i] in ("+", "-", "*", "^", "%", "=", ">", "<", "!", "/", "(", ")", ","): if result_expression[i] == ")" and i+1 < len(result_expression): - if not result_expression[i+1] in ("+", "-", "*","%", "^", "=", ">", "<", "!", "/", ")", ","): + if not result_expression[i+1] in ("+", "-", "*", "%", "^", "=", ">", "<", "!", "/", ")", ","): result_expression = result_expression[0:i+1] + \ "*"+result_expression[i+1:len(result_expression)] expr_left = expr_right @@ -417,8 +417,10 @@ def solve_functions(expression): break if first_end: try: - res_str = res_str.replace(res_str[func_start:end+1], - str("{:.16f}".format(functions[temp](calc(res_str[start:first_end]), calc(res_str[first_end+1:end]))))) + res_str = res_str.replace( + res_str[func_start:end+1], + str("{:.16f}".format(functions[temp](calc(res_str[start:first_end]), + calc(res_str[first_end+1:end]))))) res_str = solve_functions(res_str) break except Exception: @@ -443,25 +445,27 @@ def solve_functions(expression): def replace_spaces(expression): """Findes and removes unenecessary spaces near signs""" - result_expression = expression - space_pos = result_expression.find(" ") + res_exp = expression + space_pos = res_exp.find(" ") while space_pos != -1: - if space_pos-1 >= 0 and result_expression[space_pos-1] in ("+", "-", "*", "^","%", "=", ">", "<", "!", "/", ","): - if space_pos+1 < len(result_expression) and result_expression[space_pos+1] in ("*", "^", "=", ">", "<", "!", "/"): - raise UnexpectedSpaceExeption(f"Unexpected space between '{result_expression[space_pos-1]}' and '{result_expression[space_pos+1]}'") + if space_pos-1 >= 0 and res_exp[space_pos-1] in ("+", "-", "*", "^", "%", "=", ">", "<", "!", "/", ","): + if space_pos+1 < len(res_exp) and res_exp[space_pos+1] in ("*", "^", "=", ">", "<", "!", "/", "%"): + error = f"Unexpected space between '{res_exp[space_pos-1]}' and '{res_exp[space_pos+1]}'" + raise UnexpectedSpaceExeption(error) else: - result_expression = result_expression.replace( - result_expression[space_pos], "", 1) - elif space_pos+1 < len(result_expression) and result_expression[space_pos+1] in ("+", "-", "*", "^","%", "=", ">", "<", "!", "/"): - if space_pos-1 >= 0 and result_expression[space_pos-1] in ("+", "-", "*", "^","%", "=", ">", "<", "!", "/"): - raise UnexpectedSpaceExeption(f"Unexpected space between '{result_expression[space_pos-1]}' and '{result_expression[space_pos+1]}'") + res_exp = res_exp.replace( + res_exp[space_pos], "", 1) + elif space_pos+1 < len(res_exp) and res_exp[space_pos+1] in ("+", "-", "*", "^", "%", "=", ">", "<", "!", "/"): + if space_pos-1 >= 0 and res_exp[space_pos-1] in ("+", "-", "*", "^", "%", "=", ">", "<", "!", "/"): + error = f"Unexpected space between '{res_exp[space_pos-1]}' and '{res_exp[space_pos+1]}'" + raise UnexpectedSpaceExeption(error) else: - result_expression = result_expression.replace( - result_expression[space_pos], "", 1) + res_exp = res_exp.replace( + res_exp[space_pos], "", 1) else: raise UnexpectedSpaceExeption("Unexpected space") - space_pos = result_expression.find(" ") - return result_expression + space_pos = res_exp.find(" ") + return res_exp def calc(expression): @@ -499,4 +503,4 @@ def main(): if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/final_task/setup.py b/final_task/setup.py index 4a08ef9..d4b4df9 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -13,4 +13,3 @@ ] } ) - From 57640586c7f0aa84f574a13f5f9bbc2b3d737f99 Mon Sep 17 00:00:00 2001 From: Andrey Mirugin Date: Sun, 16 Dec 2018 15:48:44 +0300 Subject: [PATCH 09/16] fix pep8 --- final_task/pycalc/pycalc.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index 0e31dbf..c42a916 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -181,7 +181,7 @@ def find_left_element(expression, pointer): str Nearest element from the left to a pointer int - Start position of element + Start position of element """ first_number = "" start = 0 @@ -217,7 +217,7 @@ def find_right_element(expression, pointer): str Nearest element from the right to a pointer int - End position of element + End position of element """ end = 0 flag = False @@ -498,7 +498,7 @@ def main(): else: print(calc(expression)) except Exception as e: - print (f"ERROR: {e}") + print(f"ERROR: {e}") return e From 7160c9e4961b09906cdf42d6e4b9deb345c69047 Mon Sep 17 00:00:00 2001 From: Andrey Mirugin Date: Sun, 16 Dec 2018 20:02:11 +0300 Subject: [PATCH 10/16] fix implicit mult and some bug fixes --- final_task/pycalc/pycalc.py | 53 +++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 11 deletions(-) diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index c42a916..7c6f3a6 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -88,6 +88,10 @@ class FunctionCalculatingException(Exception): pass +class EmptyBracketsException(Exception): + pass + + def is_float(string): """Check is string float""" try: @@ -183,24 +187,24 @@ def find_left_element(expression, pointer): int Start position of element """ - first_number = "" + first_element = "" start = 0 for i in range(1, pointer+1): - prev = first_number - first_number = expression[pointer-i]+first_number - if not is_float(first_number): - first_number = prev + prev = first_element + first_element = expression[pointer-i]+first_element + if not is_float(first_element): + first_element = prev start = pointer-i+1 break elif expression[pointer-i] == '+' or expression[pointer-i] == '-': if pointer-i-1 >= 0 and (expression[pointer-i-1].isdigit() or expression[pointer-i-1] == ")"): - first_number = prev + first_element = prev start = pointer-i+1 break else: start = pointer-i break - return [first_number, start] + return [first_element, start] def find_right_element(expression, pointer): @@ -248,7 +252,7 @@ def calc_by_position_of_sign(position, expression): Position of sign in expression Returns ------- - str + float Result of calculation int Start position of left element @@ -363,7 +367,7 @@ def add_implicit_mult(expression): return result_expression -def solve_bracets(expression): +def solve_brackets(expression): """Repalces expression in brackets on it's value""" result_string = expression start = -1 @@ -377,9 +381,11 @@ def solve_bracets(expression): brackets_balance -= 1 if start != -1 and brackets_balance == 0: end = i + if start == end+1: + raise EmptyBracketsException("Empty brackets") result_string = result_string.replace( result_string[start:end+1], str("{:.16f}".format(calc(result_string[start+1:end])))) - result_string = solve_bracets(result_string) + result_string = solve_brackets(result_string) break if brackets_balance != 0: raise BracketsNotBalancedException("brackets not balanced") @@ -462,7 +468,32 @@ def replace_spaces(expression): else: res_exp = res_exp.replace( res_exp[space_pos], "", 1) + elif space_pos-1 >= 0 and res_exp[space_pos-1] == ')': + if space_pos+1 < len(res_exp): + res_exp = res_exp.replace( + res_exp[space_pos], "", 1) + else: + raise UnexpectedSpaceExeption("Unexpected space in the end of expression") else: + elem = "" + if space_pos-1 >= 0: + for i in range(1, space_pos+1): + prev = elem + elem = res_exp[space_pos-i]+elem + if res_exp[space_pos-i] in ("+", "-", "*", "^", "%", "=", ">", "<", "!", "/", ",", "(", ")", " "): + elem = prev + break + if constants.get(elem) is not None: + res_exp = res_exp.replace( + res_exp[space_pos], "", 1) + space_pos = res_exp.find(" ") + continue + elif is_float(elem): + if space_pos+1 < len(res_exp) and not is_float(res_exp[space_pos+1]): + res_exp = res_exp.replace( + res_exp[space_pos], "", 1) + space_pos = res_exp.find(" ") + continue raise UnexpectedSpaceExeption("Unexpected space") space_pos = res_exp.find(" ") return res_exp @@ -474,7 +505,7 @@ def calc(expression): result_expression = replace_long_unaries(result_expression) result_expression = solve_functions(result_expression) result_expression = replace_long_unaries(result_expression) - result_expression = solve_bracets(result_expression) + result_expression = solve_brackets(result_expression) result_expression = replace_long_unaries(result_expression) result_expression = find_and_replace_consts(result_expression) result_expression = replace_long_unaries(result_expression) From 2027fc18cb615cea86e92a702de1c9bc99d81800 Mon Sep 17 00:00:00 2001 From: Andrey Mirugin Date: Sun, 16 Dec 2018 21:34:18 +0300 Subject: [PATCH 11/16] bug fixes --- final_task/pycalc/pycalc.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index 7c6f3a6..09db7e3 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -76,15 +76,11 @@ class UnexpectedSpaceExeption(Exception): pass -class EmptyExpressionException(Exception): - pass - - class MissingArgumentException(Exception): pass -class FunctionCalculatingException(Exception): +class FunctionArgumentsException(Exception): pass @@ -381,7 +377,7 @@ def solve_brackets(expression): brackets_balance -= 1 if start != -1 and brackets_balance == 0: end = i - if start == end+1: + if start+1 == end: raise EmptyBracketsException("Empty brackets") result_string = result_string.replace( result_string[start:end+1], str("{:.16f}".format(calc(result_string[start+1:end])))) @@ -430,7 +426,7 @@ def solve_functions(expression): res_str = solve_functions(res_str) break except Exception: - raise FunctionCalculatingException(f"Incorrect arguments in function '{temp}'") + raise FunctionArgumentsException(f"Incorrect arguments in function '{temp}'") else: try: res_str = res_str.replace(res_str[func_start:end+1], @@ -438,10 +434,12 @@ def solve_functions(expression): res_str = solve_functions(res_str) break except Exception: - raise FunctionCalculatingException(f"Incorrect arguments in function '{temp}'") + raise FunctionArgumentsException(f"Incorrect arguments in function '{temp}'") else: raise UnknownFunctionException(f"Unknown function '{temp}'") elif res_str[i] in (".", '+', '-', '*', '/', '^', '%'): + if temp != "" and functions.get(temp) is None and constants.get(temp) is None: + raise UnknownElementException(f"Unknown element '{temp}'") temp = "" is_func = False if temp != "" and functions.get(temp) is None and constants.get(temp) is None: From 3ff846bcf9ef28e1b3c9345b3fc05c352ff3d07f Mon Sep 17 00:00:00 2001 From: Andrey Mirugin Date: Sun, 16 Dec 2018 21:35:45 +0300 Subject: [PATCH 12/16] unit tests added --- final_task/tests/__init__.py | 0 final_task/tests/pycalcTests.py | 195 ++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+) create mode 100644 final_task/tests/__init__.py create mode 100644 final_task/tests/pycalcTests.py diff --git a/final_task/tests/__init__.py b/final_task/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/final_task/tests/pycalcTests.py b/final_task/tests/pycalcTests.py new file mode 100644 index 0000000..e2a77f5 --- /dev/null +++ b/final_task/tests/pycalcTests.py @@ -0,0 +1,195 @@ +import unittest +import math +from pycalc.pycalc import ( + find_right_element, + find_left_element, + replace_long_unaries, + checking_and_solving_comparison, + calc_by_position_of_sign, + calc_string, + find_and_replace_consts, + add_implicit_mult, + solve_brackets, + solve_functions, + replace_spaces, + calc, + MissingArgumentException, + BracketsNotBalancedException, + UnknownFunctionException, + UnknownElementException, + UnexpectedSpaceExeption, + FunctionArgumentsException, + EmptyBracketsException +) + + +class TestPycalc(unittest.TestCase): + + def test_find_left_number_near_the_sign(self): + self.assertEqual(find_left_element("2+2", 1), ["2", 0]) + + def test_find_left_neg_number_near_the_sign_test(self): + self.assertEqual(find_left_element("4*-3+3", 4), ["-3", 2]) + + def test_find_right_number_near_the_sign(self): + self.assertEqual(find_right_element("2+2", 1), ["2", 2]) + + def test_find_right_neg_number_near_the_sign(self): + self.assertEqual(find_right_element("2*2+3*-4", 5), ["-4", 7]) + + def test_replaces_long_unaries_before_number(self): + self.assertEqual(replace_long_unaries("---+-+-3"), "-3") + + def test_replaces_long_unaries_in_middle_of_expression(self): + self.assertEqual(replace_long_unaries("3--+-+--+-4"), "3+4") + + def test_replaces_long_unaries_in_the_end_of_expression(self): + with self.assertRaises(MissingArgumentException): + replace_long_unaries("3--+-+--+-") + + def test_find_and_replace_consts_with_e_const(self): + self.assertEqual(find_and_replace_consts("e"), str(math.e)) + + def test_find_and_replace_consts_in_expression(self): + result = "1+4*"+str(math.e)+"+"+str(math.pi) + self.assertEqual(find_and_replace_consts("1+4*e+pi"), result) + + def test_find_and_replace_consts_with_no_consts(self): + self.assertEqual(find_and_replace_consts("e"), str(math.e)) + + def test_checking_and_solving_comparison_on_comparison_without_solving(self): + self.assertEqual(checking_and_solving_comparison("3<2"), [False, True]) + + def test_checking_and_solving_comparison_on_comparison_with_solving(self): + self.assertEqual(checking_and_solving_comparison("3+5<2+10"), [True, True]) + + def test_checking_and_solving_comparison_on_expression_without_comparison(self): + self.assertEqual(checking_and_solving_comparison("3+5"), ["3+5", False]) + + def test_calc_by_position_of_sign(self): + self.assertEqual(calc_by_position_of_sign(5, "3+4+5*7"), [35.0, 4, 6]) + + def test_calc_by_position_of_sign_with_neg_left_number(self): + self.assertEqual(calc_by_position_of_sign(2, "-5*7"), [(float)(-5*7), 0, 3]) + + def test_calc_by_position_of_sign_with_zero_pointer(self): + with self.assertRaises(MissingArgumentException): + calc_by_position_of_sign(0, "-5*7") + + def test_calc_by_position_of_sign_with_no_right_arg_because_of_end_of_expr(self): + with self.assertRaises(MissingArgumentException): + calc_by_position_of_sign(1, "7*") + + def test_calc_by_position_of_sign_with_no_right_arg(self): + with self.assertRaises(MissingArgumentException): + calc_by_position_of_sign(1, "7*/3") + + def test_calc_string_with_unaries(self): + self.assertEqual(calc_string("-5*7"), (float)(-5*7)) + + def test_calc_string_with_three_signs(self): + self.assertEqual(calc_string("-5*7*4^2"), (float)(-5*7*4**2)) + + def test_calc_string_check_associative(self): + self.assertEqual(calc_string("2+2*2"), (float)(2+2*2)) + + def test_calc_string_check_associative_on_powers(self): + self.assertEqual(calc_string("2^2^2"), (float)(2**2**2)) + + def test_add_implicit_multiplication_sign_after_brackets(self): + self.assertEqual(add_implicit_mult("(3+4)2"), "(3+4)*2") + + def test_add_implicit_multiplication_sign_before_brackets(self): + self.assertEqual(add_implicit_mult("2(3+4)"), "2*(3+4)") + + def test_add_implicit_multiplication_sign_between_brackets(self): + self.assertEqual(add_implicit_mult("(3+4)(2+3)"), "(3+4)*(2+3)") + + def test_add_implicit_multiplication_sign_before_func(self): + self.assertEqual(add_implicit_mult("3sin(3)"), "3*sin(3)") + + def test_add_implicit_multiplication_sign_before_const(self): + self.assertEqual(add_implicit_mult("3e"), "3*e") + + def test_add_implicit_multiplication_sign_after_func(self): + self.assertEqual(add_implicit_mult("sin(3)3"), "sin(3)*3") + + def test_add_implicit_multiplication_sign_after_const(self): + self.assertEqual(add_implicit_mult("e3"), "e*3") + + def test_add_implicit_multiplication_sign_between_consts(self): + self.assertEqual(add_implicit_mult("pie"), "pi*e") + + def test_solve_brackets_general(self): + self.assertEqual(solve_brackets("(2+3)*3"), "5.0000000000000000*3") + + def test_solve_brackets_with_unbalanced_brackets(self): + with self.assertRaises(BracketsNotBalancedException): + solve_brackets("2+3(") + + def test_solve_brackets_with_empty_brackets(self): + with self.assertRaises(EmptyBracketsException): + solve_brackets("2+3()") + + def test_solve_functions_general(self): + self.assertEqual(solve_functions("sin(3)"), str(math.sin(3))) + + def test_solve_functions_with_two_elems(self): + self.assertEqual(solve_functions("sin(3)+cos(3)"), str(math.sin(3))+'+'+str(math.cos(3))) + + def test_solve_functions_with_two_args(self): + self.assertEqual(solve_functions("log(120,10)"), str(math.log(120, 10))) + + def test_solve_functions_with_solving_in_args(self): + self.assertEqual(solve_functions("sin(3+5)"), str(math.sin(3+5))) + + def test_solve_functions_with_wrong_number_of_arguments(self): + with self.assertRaises(FunctionArgumentsException): + solve_functions("log(3,4,5)") + + def test_solve_functions_with_wrong_function_name(self): + with self.assertRaises(UnknownFunctionException): + solve_functions("logs(3)") + + def test_solve_functions_with_wrong_element_name(self): + with self.assertRaises(UnknownElementException): + solve_functions("3+logs+3") + + def test_replace_spaces_in_func(self): + self.assertEqual(replace_spaces("sin(3 + 5)"), "sin(3+5)") + + def test_replace_spaces_in_func_with_two_args(self): + self.assertEqual(replace_spaces("log(10, 5)"), "log(10,5)") + + def test_replace_spaces_err_in_comparison_sign(self): + with self.assertRaises(UnexpectedSpaceExeption): + replace_spaces("3< =4") + + def test_replace_spaces_err_func(self): + with self.assertRaises(UnexpectedSpaceExeption): + replace_spaces("sin (3+4)") + + def test_replace_spaces_for_implicit_mult_number_before_func(self): + self.assertEqual(replace_spaces("2 sin(3)"), "2sin(3)") + + def test_replace_spaces_for_implicit_mult_number_after_func(self): + self.assertEqual(replace_spaces("sin(3) 2"), "sin(3)2") + + def test_replace_spaces_for_implicit_mult_between_brackets(self): + self.assertEqual(replace_spaces("(2+3) (3+4)"), "(2+3)(3+4)") + + def test_replace_spaces_for_implicit_mult_with_const_after_number(self): + self.assertEqual(replace_spaces("2 e"), "2e") + + def test_replace_spaces_for_implicit_mult_with_const_before_number(self): + self.assertEqual(replace_spaces("pi 2"), "pi2") + + def test_calc_general(self): + self.assertEqual(calc("3+log(e)+4/2^2"), 3+math.log(math.e)+4/2**2) + + def test_calc_with_unaries(self): + self.assertEqual(calc("3+log(e)+--+-4/2^2"), 3+math.log(math.e)+--+-4/2**2) + + +if __name__ == "__main__": + unittest.main() From a95099ef01ab2f86a3e0aaae1603e9ba0229707b Mon Sep 17 00:00:00 2001 From: Andrey Mirugin Date: Sun, 16 Dec 2018 21:57:23 +0300 Subject: [PATCH 13/16] folders movement --- final_task/tests/__init__.py | 0 final_task/tests/pycalcTests.py | 195 -------------------------------- 2 files changed, 195 deletions(-) delete mode 100644 final_task/tests/__init__.py delete mode 100644 final_task/tests/pycalcTests.py diff --git a/final_task/tests/__init__.py b/final_task/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/final_task/tests/pycalcTests.py b/final_task/tests/pycalcTests.py deleted file mode 100644 index e2a77f5..0000000 --- a/final_task/tests/pycalcTests.py +++ /dev/null @@ -1,195 +0,0 @@ -import unittest -import math -from pycalc.pycalc import ( - find_right_element, - find_left_element, - replace_long_unaries, - checking_and_solving_comparison, - calc_by_position_of_sign, - calc_string, - find_and_replace_consts, - add_implicit_mult, - solve_brackets, - solve_functions, - replace_spaces, - calc, - MissingArgumentException, - BracketsNotBalancedException, - UnknownFunctionException, - UnknownElementException, - UnexpectedSpaceExeption, - FunctionArgumentsException, - EmptyBracketsException -) - - -class TestPycalc(unittest.TestCase): - - def test_find_left_number_near_the_sign(self): - self.assertEqual(find_left_element("2+2", 1), ["2", 0]) - - def test_find_left_neg_number_near_the_sign_test(self): - self.assertEqual(find_left_element("4*-3+3", 4), ["-3", 2]) - - def test_find_right_number_near_the_sign(self): - self.assertEqual(find_right_element("2+2", 1), ["2", 2]) - - def test_find_right_neg_number_near_the_sign(self): - self.assertEqual(find_right_element("2*2+3*-4", 5), ["-4", 7]) - - def test_replaces_long_unaries_before_number(self): - self.assertEqual(replace_long_unaries("---+-+-3"), "-3") - - def test_replaces_long_unaries_in_middle_of_expression(self): - self.assertEqual(replace_long_unaries("3--+-+--+-4"), "3+4") - - def test_replaces_long_unaries_in_the_end_of_expression(self): - with self.assertRaises(MissingArgumentException): - replace_long_unaries("3--+-+--+-") - - def test_find_and_replace_consts_with_e_const(self): - self.assertEqual(find_and_replace_consts("e"), str(math.e)) - - def test_find_and_replace_consts_in_expression(self): - result = "1+4*"+str(math.e)+"+"+str(math.pi) - self.assertEqual(find_and_replace_consts("1+4*e+pi"), result) - - def test_find_and_replace_consts_with_no_consts(self): - self.assertEqual(find_and_replace_consts("e"), str(math.e)) - - def test_checking_and_solving_comparison_on_comparison_without_solving(self): - self.assertEqual(checking_and_solving_comparison("3<2"), [False, True]) - - def test_checking_and_solving_comparison_on_comparison_with_solving(self): - self.assertEqual(checking_and_solving_comparison("3+5<2+10"), [True, True]) - - def test_checking_and_solving_comparison_on_expression_without_comparison(self): - self.assertEqual(checking_and_solving_comparison("3+5"), ["3+5", False]) - - def test_calc_by_position_of_sign(self): - self.assertEqual(calc_by_position_of_sign(5, "3+4+5*7"), [35.0, 4, 6]) - - def test_calc_by_position_of_sign_with_neg_left_number(self): - self.assertEqual(calc_by_position_of_sign(2, "-5*7"), [(float)(-5*7), 0, 3]) - - def test_calc_by_position_of_sign_with_zero_pointer(self): - with self.assertRaises(MissingArgumentException): - calc_by_position_of_sign(0, "-5*7") - - def test_calc_by_position_of_sign_with_no_right_arg_because_of_end_of_expr(self): - with self.assertRaises(MissingArgumentException): - calc_by_position_of_sign(1, "7*") - - def test_calc_by_position_of_sign_with_no_right_arg(self): - with self.assertRaises(MissingArgumentException): - calc_by_position_of_sign(1, "7*/3") - - def test_calc_string_with_unaries(self): - self.assertEqual(calc_string("-5*7"), (float)(-5*7)) - - def test_calc_string_with_three_signs(self): - self.assertEqual(calc_string("-5*7*4^2"), (float)(-5*7*4**2)) - - def test_calc_string_check_associative(self): - self.assertEqual(calc_string("2+2*2"), (float)(2+2*2)) - - def test_calc_string_check_associative_on_powers(self): - self.assertEqual(calc_string("2^2^2"), (float)(2**2**2)) - - def test_add_implicit_multiplication_sign_after_brackets(self): - self.assertEqual(add_implicit_mult("(3+4)2"), "(3+4)*2") - - def test_add_implicit_multiplication_sign_before_brackets(self): - self.assertEqual(add_implicit_mult("2(3+4)"), "2*(3+4)") - - def test_add_implicit_multiplication_sign_between_brackets(self): - self.assertEqual(add_implicit_mult("(3+4)(2+3)"), "(3+4)*(2+3)") - - def test_add_implicit_multiplication_sign_before_func(self): - self.assertEqual(add_implicit_mult("3sin(3)"), "3*sin(3)") - - def test_add_implicit_multiplication_sign_before_const(self): - self.assertEqual(add_implicit_mult("3e"), "3*e") - - def test_add_implicit_multiplication_sign_after_func(self): - self.assertEqual(add_implicit_mult("sin(3)3"), "sin(3)*3") - - def test_add_implicit_multiplication_sign_after_const(self): - self.assertEqual(add_implicit_mult("e3"), "e*3") - - def test_add_implicit_multiplication_sign_between_consts(self): - self.assertEqual(add_implicit_mult("pie"), "pi*e") - - def test_solve_brackets_general(self): - self.assertEqual(solve_brackets("(2+3)*3"), "5.0000000000000000*3") - - def test_solve_brackets_with_unbalanced_brackets(self): - with self.assertRaises(BracketsNotBalancedException): - solve_brackets("2+3(") - - def test_solve_brackets_with_empty_brackets(self): - with self.assertRaises(EmptyBracketsException): - solve_brackets("2+3()") - - def test_solve_functions_general(self): - self.assertEqual(solve_functions("sin(3)"), str(math.sin(3))) - - def test_solve_functions_with_two_elems(self): - self.assertEqual(solve_functions("sin(3)+cos(3)"), str(math.sin(3))+'+'+str(math.cos(3))) - - def test_solve_functions_with_two_args(self): - self.assertEqual(solve_functions("log(120,10)"), str(math.log(120, 10))) - - def test_solve_functions_with_solving_in_args(self): - self.assertEqual(solve_functions("sin(3+5)"), str(math.sin(3+5))) - - def test_solve_functions_with_wrong_number_of_arguments(self): - with self.assertRaises(FunctionArgumentsException): - solve_functions("log(3,4,5)") - - def test_solve_functions_with_wrong_function_name(self): - with self.assertRaises(UnknownFunctionException): - solve_functions("logs(3)") - - def test_solve_functions_with_wrong_element_name(self): - with self.assertRaises(UnknownElementException): - solve_functions("3+logs+3") - - def test_replace_spaces_in_func(self): - self.assertEqual(replace_spaces("sin(3 + 5)"), "sin(3+5)") - - def test_replace_spaces_in_func_with_two_args(self): - self.assertEqual(replace_spaces("log(10, 5)"), "log(10,5)") - - def test_replace_spaces_err_in_comparison_sign(self): - with self.assertRaises(UnexpectedSpaceExeption): - replace_spaces("3< =4") - - def test_replace_spaces_err_func(self): - with self.assertRaises(UnexpectedSpaceExeption): - replace_spaces("sin (3+4)") - - def test_replace_spaces_for_implicit_mult_number_before_func(self): - self.assertEqual(replace_spaces("2 sin(3)"), "2sin(3)") - - def test_replace_spaces_for_implicit_mult_number_after_func(self): - self.assertEqual(replace_spaces("sin(3) 2"), "sin(3)2") - - def test_replace_spaces_for_implicit_mult_between_brackets(self): - self.assertEqual(replace_spaces("(2+3) (3+4)"), "(2+3)(3+4)") - - def test_replace_spaces_for_implicit_mult_with_const_after_number(self): - self.assertEqual(replace_spaces("2 e"), "2e") - - def test_replace_spaces_for_implicit_mult_with_const_before_number(self): - self.assertEqual(replace_spaces("pi 2"), "pi2") - - def test_calc_general(self): - self.assertEqual(calc("3+log(e)+4/2^2"), 3+math.log(math.e)+4/2**2) - - def test_calc_with_unaries(self): - self.assertEqual(calc("3+log(e)+--+-4/2^2"), 3+math.log(math.e)+--+-4/2**2) - - -if __name__ == "__main__": - unittest.main() From b152e44069e38832ea74a75400bb25bc9c07f3a9 Mon Sep 17 00:00:00 2001 From: Andrey Mirugin Date: Sun, 16 Dec 2018 22:19:15 +0300 Subject: [PATCH 14/16] added tests, that was deleted --- final_task/pycalc/tests/pycalcTests.py | 195 +++++++++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 final_task/pycalc/tests/pycalcTests.py diff --git a/final_task/pycalc/tests/pycalcTests.py b/final_task/pycalc/tests/pycalcTests.py new file mode 100644 index 0000000..e2a77f5 --- /dev/null +++ b/final_task/pycalc/tests/pycalcTests.py @@ -0,0 +1,195 @@ +import unittest +import math +from pycalc.pycalc import ( + find_right_element, + find_left_element, + replace_long_unaries, + checking_and_solving_comparison, + calc_by_position_of_sign, + calc_string, + find_and_replace_consts, + add_implicit_mult, + solve_brackets, + solve_functions, + replace_spaces, + calc, + MissingArgumentException, + BracketsNotBalancedException, + UnknownFunctionException, + UnknownElementException, + UnexpectedSpaceExeption, + FunctionArgumentsException, + EmptyBracketsException +) + + +class TestPycalc(unittest.TestCase): + + def test_find_left_number_near_the_sign(self): + self.assertEqual(find_left_element("2+2", 1), ["2", 0]) + + def test_find_left_neg_number_near_the_sign_test(self): + self.assertEqual(find_left_element("4*-3+3", 4), ["-3", 2]) + + def test_find_right_number_near_the_sign(self): + self.assertEqual(find_right_element("2+2", 1), ["2", 2]) + + def test_find_right_neg_number_near_the_sign(self): + self.assertEqual(find_right_element("2*2+3*-4", 5), ["-4", 7]) + + def test_replaces_long_unaries_before_number(self): + self.assertEqual(replace_long_unaries("---+-+-3"), "-3") + + def test_replaces_long_unaries_in_middle_of_expression(self): + self.assertEqual(replace_long_unaries("3--+-+--+-4"), "3+4") + + def test_replaces_long_unaries_in_the_end_of_expression(self): + with self.assertRaises(MissingArgumentException): + replace_long_unaries("3--+-+--+-") + + def test_find_and_replace_consts_with_e_const(self): + self.assertEqual(find_and_replace_consts("e"), str(math.e)) + + def test_find_and_replace_consts_in_expression(self): + result = "1+4*"+str(math.e)+"+"+str(math.pi) + self.assertEqual(find_and_replace_consts("1+4*e+pi"), result) + + def test_find_and_replace_consts_with_no_consts(self): + self.assertEqual(find_and_replace_consts("e"), str(math.e)) + + def test_checking_and_solving_comparison_on_comparison_without_solving(self): + self.assertEqual(checking_and_solving_comparison("3<2"), [False, True]) + + def test_checking_and_solving_comparison_on_comparison_with_solving(self): + self.assertEqual(checking_and_solving_comparison("3+5<2+10"), [True, True]) + + def test_checking_and_solving_comparison_on_expression_without_comparison(self): + self.assertEqual(checking_and_solving_comparison("3+5"), ["3+5", False]) + + def test_calc_by_position_of_sign(self): + self.assertEqual(calc_by_position_of_sign(5, "3+4+5*7"), [35.0, 4, 6]) + + def test_calc_by_position_of_sign_with_neg_left_number(self): + self.assertEqual(calc_by_position_of_sign(2, "-5*7"), [(float)(-5*7), 0, 3]) + + def test_calc_by_position_of_sign_with_zero_pointer(self): + with self.assertRaises(MissingArgumentException): + calc_by_position_of_sign(0, "-5*7") + + def test_calc_by_position_of_sign_with_no_right_arg_because_of_end_of_expr(self): + with self.assertRaises(MissingArgumentException): + calc_by_position_of_sign(1, "7*") + + def test_calc_by_position_of_sign_with_no_right_arg(self): + with self.assertRaises(MissingArgumentException): + calc_by_position_of_sign(1, "7*/3") + + def test_calc_string_with_unaries(self): + self.assertEqual(calc_string("-5*7"), (float)(-5*7)) + + def test_calc_string_with_three_signs(self): + self.assertEqual(calc_string("-5*7*4^2"), (float)(-5*7*4**2)) + + def test_calc_string_check_associative(self): + self.assertEqual(calc_string("2+2*2"), (float)(2+2*2)) + + def test_calc_string_check_associative_on_powers(self): + self.assertEqual(calc_string("2^2^2"), (float)(2**2**2)) + + def test_add_implicit_multiplication_sign_after_brackets(self): + self.assertEqual(add_implicit_mult("(3+4)2"), "(3+4)*2") + + def test_add_implicit_multiplication_sign_before_brackets(self): + self.assertEqual(add_implicit_mult("2(3+4)"), "2*(3+4)") + + def test_add_implicit_multiplication_sign_between_brackets(self): + self.assertEqual(add_implicit_mult("(3+4)(2+3)"), "(3+4)*(2+3)") + + def test_add_implicit_multiplication_sign_before_func(self): + self.assertEqual(add_implicit_mult("3sin(3)"), "3*sin(3)") + + def test_add_implicit_multiplication_sign_before_const(self): + self.assertEqual(add_implicit_mult("3e"), "3*e") + + def test_add_implicit_multiplication_sign_after_func(self): + self.assertEqual(add_implicit_mult("sin(3)3"), "sin(3)*3") + + def test_add_implicit_multiplication_sign_after_const(self): + self.assertEqual(add_implicit_mult("e3"), "e*3") + + def test_add_implicit_multiplication_sign_between_consts(self): + self.assertEqual(add_implicit_mult("pie"), "pi*e") + + def test_solve_brackets_general(self): + self.assertEqual(solve_brackets("(2+3)*3"), "5.0000000000000000*3") + + def test_solve_brackets_with_unbalanced_brackets(self): + with self.assertRaises(BracketsNotBalancedException): + solve_brackets("2+3(") + + def test_solve_brackets_with_empty_brackets(self): + with self.assertRaises(EmptyBracketsException): + solve_brackets("2+3()") + + def test_solve_functions_general(self): + self.assertEqual(solve_functions("sin(3)"), str(math.sin(3))) + + def test_solve_functions_with_two_elems(self): + self.assertEqual(solve_functions("sin(3)+cos(3)"), str(math.sin(3))+'+'+str(math.cos(3))) + + def test_solve_functions_with_two_args(self): + self.assertEqual(solve_functions("log(120,10)"), str(math.log(120, 10))) + + def test_solve_functions_with_solving_in_args(self): + self.assertEqual(solve_functions("sin(3+5)"), str(math.sin(3+5))) + + def test_solve_functions_with_wrong_number_of_arguments(self): + with self.assertRaises(FunctionArgumentsException): + solve_functions("log(3,4,5)") + + def test_solve_functions_with_wrong_function_name(self): + with self.assertRaises(UnknownFunctionException): + solve_functions("logs(3)") + + def test_solve_functions_with_wrong_element_name(self): + with self.assertRaises(UnknownElementException): + solve_functions("3+logs+3") + + def test_replace_spaces_in_func(self): + self.assertEqual(replace_spaces("sin(3 + 5)"), "sin(3+5)") + + def test_replace_spaces_in_func_with_two_args(self): + self.assertEqual(replace_spaces("log(10, 5)"), "log(10,5)") + + def test_replace_spaces_err_in_comparison_sign(self): + with self.assertRaises(UnexpectedSpaceExeption): + replace_spaces("3< =4") + + def test_replace_spaces_err_func(self): + with self.assertRaises(UnexpectedSpaceExeption): + replace_spaces("sin (3+4)") + + def test_replace_spaces_for_implicit_mult_number_before_func(self): + self.assertEqual(replace_spaces("2 sin(3)"), "2sin(3)") + + def test_replace_spaces_for_implicit_mult_number_after_func(self): + self.assertEqual(replace_spaces("sin(3) 2"), "sin(3)2") + + def test_replace_spaces_for_implicit_mult_between_brackets(self): + self.assertEqual(replace_spaces("(2+3) (3+4)"), "(2+3)(3+4)") + + def test_replace_spaces_for_implicit_mult_with_const_after_number(self): + self.assertEqual(replace_spaces("2 e"), "2e") + + def test_replace_spaces_for_implicit_mult_with_const_before_number(self): + self.assertEqual(replace_spaces("pi 2"), "pi2") + + def test_calc_general(self): + self.assertEqual(calc("3+log(e)+4/2^2"), 3+math.log(math.e)+4/2**2) + + def test_calc_with_unaries(self): + self.assertEqual(calc("3+log(e)+--+-4/2^2"), 3+math.log(math.e)+--+-4/2**2) + + +if __name__ == "__main__": + unittest.main() From e754e1339c54a9f292e0b43780a872967da757ef Mon Sep 17 00:00:00 2001 From: Andrey Mirugin Date: Tue, 18 Dec 2018 22:14:16 +0300 Subject: [PATCH 15/16] little changes in importing functions and consts --- final_task/pycalc/pycalc.py | 32 +++++--------------------------- 1 file changed, 5 insertions(+), 27 deletions(-) diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index 09db7e3..65ea508 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -1,10 +1,8 @@ import math import argparse +import numbers -constants = { - "e": math.e, - "pi": math.pi -} +constants = {a: getattr(math, a) for a in dir(math) if isinstance(getattr(math, a), numbers.Number)} comparison = { "<": lambda x, y: x < y, @@ -25,29 +23,9 @@ "^": lambda x, y: x ** y } -functions = { - "abs": abs, - "round": round, - "acosh": math.acosh, - "asinh": math.asinh, - "atanh": math.atanh, - "acos": math.acos, - "asin": math.asin, - "atan": math.atan, - "cosh": math.cosh, - "sinh": math.sinh, - "tanh": math.tanh, - "factorial": math.factorial, - "log10": math.log10, - "log2": math.log2, - "cos": math.cos, - "sin": math.sin, - "tan": math.tan, - "exp": math.exp, - "sqrt": math.sqrt, - "log": lambda x, y = math.e: math.log(x, y), - "pow": lambda x, y: x ** y -} +functions = {a: getattr(math, a) for a in dir(math) if callable(getattr(math, a))} +functions['abs'] = abs +functions['round'] = round priority = { "+": 0, From d883fb3d79d754f59f07b51a5b50ee02126efa1a Mon Sep 17 00:00:00 2001 From: Andrey Mirugin Date: Thu, 20 Dec 2018 13:55:43 +0300 Subject: [PATCH 16/16] fixed test coverage --- final_task/pycalc/{tests/pycalcTests.py => testPycalc.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename final_task/pycalc/{tests/pycalcTests.py => testPycalc.py} (100%) diff --git a/final_task/pycalc/tests/pycalcTests.py b/final_task/pycalc/testPycalc.py similarity index 100% rename from final_task/pycalc/tests/pycalcTests.py rename to final_task/pycalc/testPycalc.py