From 852e30c19b8c1edacc86e9a03b304682966d8365 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Sun, 14 Apr 2019 18:55:01 +0300 Subject: [PATCH 001/103] initial commit: calc.py can calculate all valid expressinos, except expressions with more than one argument. --- calc.py | 428 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 428 insertions(+) create mode 100644 calc.py diff --git a/calc.py b/calc.py new file mode 100644 index 00000000..07a9696d --- /dev/null +++ b/calc.py @@ -0,0 +1,428 @@ +#!/usr/bin/python3 + +import operator +import math +from math import * + + +function_dict = { + 'abs': {'operator': abs, 'priority': 0}, + 'round': {'operator': round, 'priority': 0} +} +for k,v in math.__dict__.items(): + if k.startswith('_'): + continue + function_dict[k] = {'operator': v, 'priority': 0} + + +operator_dict = { + '+': {'operator': operator.add, 'priority': 4}, + '-': {'operator': operator.sub, 'priority': 4}, + '/': {'operator': operator.truediv, 'priority': 3}, + '*': {'operator': operator.mul, 'priority': 3}, + '%': {'operator': operator.mod, 'priority': 3}, + '//': {'operator': operator.floordiv, 'priority': 3}, + '^': {'operator': operator.pow, 'priority': 1}, + '==': {'operator': operator.eq, 'priority': 9}, + '!=': {'operator': operator.ne, 'priority': 9}, + '>': {'operator': operator.gt, 'priority': 9}, + '<': {'operator': operator.lt, 'priority': 9}, + '>=': {'operator': operator.ge, 'priority': 9}, + '<=': {'operator': operator.le, 'priority': 9}, + +} + + +a = "-pi+3-((-60/2^2-1)/(2+3)^(-3))" +# a = "100/3%2^2" +# a = ".1 * 2.0^56.0" +a = '60/2^2---1+-(+e)' +# a = '(2.0^(pi/pi+e/e+2.0^0.0))^(1.0/3.0)' +# a = "102%12%7" +# a = "666" +# a = 'sin(pi)+e*round(2+9)' +a= "sin(pi/2+cos(e)^2)*111*6" +# a = "1--1" +# a = "6-(-+-+13)" +# a= "-+---+-1" +# a = "-13" +# a = "2.0^(2.0^2.0*2.0^2.0)" +a = "sin(e^log(e^e^sin(23.0)*45.0) + cos(3.0+log10(e^-e)))" +# a = ('6') +a = "sin(pi/2^1) + log10(1*4+2^2+1)" +# a = "sin(-cos(-sin(3.0)-cos(-sin(-3.0*5.0)-sin(cos(43.0))))+cos(sin(sin(34.0-2.0^2.0))))--cos(1.0)--cos(0.0)^3.0" +# a = "log10(e)" +a = "10*e^0*log10(.4 -5/ -0.1-10) - -abs(-53/10) + -5" +# a = "log10(.4 -5/ (-0.1)-10)" +# a = ".4 -5/ (-0.1)-10" +# a = "1+2*4/3+1!=1+2*4/3+2" +# a = "8//3" + +unary_operators = [ + "-13", + "6-(-13)", + "1---1", + "-+---+-1", + +] +print(a) + + +def number_parser(number): + try: + return int(number) + except: + return float(number) + + +def function_parser(function_name): + if function_name == 'e' or function_name == 'pi': + return function_dict[function_name]['operator'] + return function_name + +def split_operators(s): + parsing_list = [] + last_number = "" + last_letter = "" + last_symbol = "" + arguments_list = [] + for i in s: + if i == " ": + continue + + if i.isnumeric() or i is '.': + if last_symbol: + parsing_list.append(last_symbol) + last_symbol = "" + if last_letter: + last_letter += i + else: + last_number += i + elif i.isalpha(): + if last_symbol: + parsing_list.append(last_symbol) + last_symbol = "" + last_letter += i + # elif i in operator_dict.keys(): + # last_symbol += i + + # elif last_letter: + # parsing_list.append(function_parser(last_letter)) + # last_letter = "" + # if parsing_list and parsing_list[-1] in function_dict.keys(): + # parsing_list.append(list()) + # # parsing_list.append(arguments_list) + # arguments_list.append(i) + # continue + else: + # if arguments_list and arguments_list.count('(') != arguments_list.count(')'): + # if last_number: + # arguments_list.append(last_number) + # arguments_list.append(i) + # last_number = "" + # continue + # if last_letter and function_parser(last_letter) not in function_dict.keys(): + # arguments_list.append(function_parser(last_letter)) + # arguments_list.append(i) + # last_letter = "" + # continue + if last_number: + parsing_list.append(number_parser(last_number)) + last_number = "" + if last_letter: + parsing_list.append(function_parser(last_letter)) + last_letter = "" + # if parsing_list and parsing_list[-1] in function_dict.keys(): + # # arguments_list = [] + # parsing_list.append(arguments_list) + # arguments_list.append(i) + # continue + # if last_symbol: + # last_symbol += i + # parsing_list.append(last_symbol) + # last_symbol = "" + if i: + # last_symbol += i + parsing_list.append(i) + if last_number: + parsing_list.append(number_parser(last_number)) + elif last_letter: + parsing_list.append(function_parser(last_letter)) + return parsing_list + +l_expr = split_operators(a) +print(l_expr) +# for i in l_expr: +# print(i) +# exit() + + +def clean_add_sub_operators(last_item, converted_list): + if last_item.count('-') % 2 == 0: + if converted_list[-1] == '(': + last_item= "" + else: + last_item = '+' + # converted_list.append(operator_dict['+']) + else: + last_item = '-' + return last_item + + +def converter(parsing_list): + if parsing_list[0] == "-": + converted_list = [0] + else: + converted_list = [] + last_item = "" + for i in parsing_list: + if i != '-' and i != '+' and last_item: + last_item = clean_add_sub_operators(last_item, converted_list) + if last_item == '+': + converted_list.append(operator_dict[last_item]) + last_item = '' + if type(i) is float or type(i) is int: + if last_item == '-' and converted_list[-1] != '(' and converted_list[-1] not in operator_dict.values(): + converted_list.append(operator_dict['+']) + converted_list.append(-i) + last_item = "" + elif last_item == '-': #and converted_list[-1] == '(': + converted_list.append(-i) + last_item = "" + else: + converted_list.append(i) + elif i in operator_dict.keys(): + if i == '-' or i == '+': + last_item +=i + else: + converted_list.append(operator_dict[i]) + elif i in function_dict.keys(): + if last_item: + if last_item == '-' and converted_list[-1] != '(': + converted_list.append(operator_dict['+']) + converted_list.append(-1) + converted_list.append(operator_dict['*']) + converted_list.append(function_dict[i]) + last_item = "" + elif last_item == '-' and converted_list[-1] == '(': + converted_list.append(-1) + converted_list.append(operator_dict['*']) + converted_list.append(function_dict[i]) + last_item = "" + else: + converted_list.append(function_dict[i]) + else: + if last_item: + converted_list.append(operator_dict['-']) + last_item = "" + converted_list.append(i) + + return converted_list + + +converted_list = converter(l_expr) +for i in converted_list: + print(i) + + +# exit() + + + + + +class OperandStack(): + + def __init__(self): + self.stack = list() + + def put_on_stack(self, item): + self.stack.append(item) + + def top(self): + return self.stack[-1] + + def take_from_stack(self): + return self.stack.pop() + + def is_empty(self): + """ + Returns True if no items on a stack, otherwise returns False + """ + if len(self.stack) == 0: + return True + else: + return False + + +operands = OperandStack() +function = OperandStack() + + +def calc_on_stack(): + operator_on_stack = function.take_from_stack() + if operator_on_stack in function_dict.values(): + first_operand = operands.take_from_stack() + current_result = operator_on_stack['operator'](first_operand) + elif operator_on_stack in operator_dict.values(): + if len(operands.stack) == 1: + second_operand = operands.take_from_stack() + first_operand = 0 + else: + second_operand = operands.take_from_stack() + first_operand = operands.take_from_stack() + current_result = operator_on_stack['operator'](first_operand, second_operand) + operands.put_on_stack(current_result) + if len(function.stack) and function.top() is not '(': + if current_operator['priority'] >= function.top()['priority']: + current_result = calc_on_stack() + return current_result + + + +operands = OperandStack() +function = OperandStack() + +for item in converted_list: + if type(item) is float or type(item) is int: + operands.put_on_stack(item) + elif item in operator_dict.values() or item in function_dict.values(): + current_operator = item + if function.is_empty(): + function.put_on_stack(current_operator) + else: + if function.top() is '(' or current_operator['priority'] < function.top()['priority'] or \ + current_operator == operator_dict['^'] and function.top() == operator_dict['^']: + function.put_on_stack(current_operator) + else: + current_result = calc_on_stack() + + function.put_on_stack(current_operator) + elif item is '(': + function.put_on_stack(item) + elif item is ')' and function.top() == '(': + function.take_from_stack() + else: + for i in range(len(function.stack)): + current_result = calc_on_stack() + if item is ')': + if function.top() is '(': + function.take_from_stack() + break + +if function.is_empty(): + current_result = operands.take_from_stack() +elif len(function.stack) == 1: + current_result = calc_on_stack() +else: + for i in range(len(function.stack)): + current_operator = function.top() + current_result = calc_on_stack() + if not len(function.stack): + break +print(current_result) + + + +# for i in unary_operators: + + + +# +# for i in range(self.operator_stack.length): +# current_result = self.calculate_on_stack() +# if self.operator_stack.length == 0: +# return current_result + +# print(-20+34-50*6-11*2/2) +# print(54-300+22) +# print(-math.pi+3-((-60/2**2-1)/(2+3)**(-3))) +# print(60/2**2-1) +# print(102%12%7) +# print(60/2**2---1+-+math.e) +# print(operands.stack) +# print(function.stack) +# print(math.sin(math.pi/2+math.cos(math.e)**2)*111*6) +# print(sin(-cos(-sin(3.0)-cos(-sin(-3.0*5.0)-sin(cos(43.0))))+cos(sin(sin(34.0-2.0**2.0))))--cos(1.0)--cos(0.0)**3.0) +# print(100/3%2**2) +# print((2.0**(math.pi/math.pi+math.e/math.e+2.0**0.0))**(1.0/3.0)) +# print(.1*2.0**56.0) +# print(math.sin(/math.pi)+math.e*round(2+9)) +# print(math.sin(math.pi/2)*111*6) +# print(2.0**(2.0**2.0**2.0**2.0)) +# print(math.sin(math.e**math.log(math.e**math.e**math.sin(23.0),45.0) + math.cos(3.0+math.log10(math.e**-math.e)))) +# print(sin(pi/2**1) + log10(1*4+2**2+1)) +print(10*e**0*log10(.4 -5/ -0.1-10) - -abs(-53/10) + -5) +# print(.4-5/-0.1-10) +# print(1+2*4/3+1>1+2*4/3+2) + + + +# def split_operators(s): +# expr = [] +# last_number = "" +# last_letter = "" +# for i in s: +# # if i == ' ': +# # continue +# # elif i is '-': +# # if last_number: +# # last_number = float(last_number) +# # expr.append(last_number) +# # last_number = '' +# # if last_letter: +# # expr.append(func_dict[last_letter]) +# # last_letter = "" +# # if expr: +# # expr.append(d['+']) +# # expr.append(-1.0) +# # expr.append(d['*']) +# # last_number = '' +# # else: +# # expr.append(-1.0) +# # expr.append(d['*']) +# if i.isnumeric() or i is '.': +# last_number += i +# elif i.isalpha(): +# last_letter += i +# else: +# if last_number and last_number is not '-': +# last_number = float(last_number) +# expr.append(last_number) +# last_number = "" +# if last_letter: +# if last_letter == 'e' or last_letter == 'pi': +# expr.append(func_dict[last_letter]['operator']) +# else: +# expr.append(func_dict[last_letter]) +# last_letter = "" +# if i: +# if i in d.keys(): +# expr.append(d[i]) +# elif i in func_dict.keys(): +# expr.append(func_dict[last_letter]) +# else: +# expr.append(i) +# if last_number: +# last_number = float(last_number) +# expr.append(last_number) +# elif last_letter: +# expr.append(func_dict[last_letter]) +# return expr + +# d = { +# '+': '+', +# '-': '-', +# '/': '/', +# '*': '*', +# '%': {'operator': operator.mod, 'priority': 3}, +# '//': {'operator': operator.floordiv, 'priority': 3}, +# '^': {'operator': operator.pow, 'priority': 1}, +# '==': {'operator': operator.eq, 'priority': 9}, +# '!=': {'operator': operator.ne, 'priority': 9}, +# '>': {'operator': operator.gt, 'priority': 9}, +# '<': {'operator': operator.lt, 'priority': 9}, +# '>=': {'operator': operator.ge, 'priority': 9}, +# '<=': {'operator': operator.le, 'priority': 9}, +# +# } \ No newline at end of file From ace43c3dd9b18bd3d2c9add16ec68bc87ad2cfcd Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Sun, 14 Apr 2019 21:26:44 +0300 Subject: [PATCH 002/103] feat: add calculation of two arguments separated by commo in the math functions --- calc.py | 151 ++++++++++++++------------------------------------------ 1 file changed, 37 insertions(+), 114 deletions(-) diff --git a/calc.py b/calc.py index 07a9696d..5e90b62a 100644 --- a/calc.py +++ b/calc.py @@ -57,6 +57,9 @@ # a = ".4 -5/ (-0.1)-10" # a = "1+2*4/3+1!=1+2*4/3+2" # a = "8//3" +a = 'sin(log(-sin(23.0),45.0)^2 + log(-sin(23.0),45.0)^3)' +# a= 'log(-sin(23.0),45.0)' +# a = 'sin(e^log(e^e^sin(23.0),45.0) + cos(3.0+log10(e^-e)))' unary_operators = [ "-13", @@ -85,7 +88,6 @@ def split_operators(s): last_number = "" last_letter = "" last_symbol = "" - arguments_list = [] for i in s: if i == " ": continue @@ -103,46 +105,14 @@ def split_operators(s): parsing_list.append(last_symbol) last_symbol = "" last_letter += i - # elif i in operator_dict.keys(): - # last_symbol += i - - # elif last_letter: - # parsing_list.append(function_parser(last_letter)) - # last_letter = "" - # if parsing_list and parsing_list[-1] in function_dict.keys(): - # parsing_list.append(list()) - # # parsing_list.append(arguments_list) - # arguments_list.append(i) - # continue else: - # if arguments_list and arguments_list.count('(') != arguments_list.count(')'): - # if last_number: - # arguments_list.append(last_number) - # arguments_list.append(i) - # last_number = "" - # continue - # if last_letter and function_parser(last_letter) not in function_dict.keys(): - # arguments_list.append(function_parser(last_letter)) - # arguments_list.append(i) - # last_letter = "" - # continue if last_number: parsing_list.append(number_parser(last_number)) last_number = "" if last_letter: parsing_list.append(function_parser(last_letter)) last_letter = "" - # if parsing_list and parsing_list[-1] in function_dict.keys(): - # # arguments_list = [] - # parsing_list.append(arguments_list) - # arguments_list.append(i) - # continue - # if last_symbol: - # last_symbol += i - # parsing_list.append(last_symbol) - # last_symbol = "" if i: - # last_symbol += i parsing_list.append(i) if last_number: parsing_list.append(number_parser(last_number)) @@ -163,7 +133,6 @@ def clean_add_sub_operators(last_item, converted_list): last_item= "" else: last_item = '+' - # converted_list.append(operator_dict['+']) else: last_item = '-' return last_item @@ -186,7 +155,7 @@ def converter(parsing_list): converted_list.append(operator_dict['+']) converted_list.append(-i) last_item = "" - elif last_item == '-': #and converted_list[-1] == '(': + elif last_item == '-': converted_list.append(-i) last_item = "" else: @@ -221,8 +190,8 @@ def converter(parsing_list): converted_list = converter(l_expr) -for i in converted_list: - print(i) +# for i in converted_list: +# print(i) # exit() @@ -246,9 +215,6 @@ def take_from_stack(self): return self.stack.pop() def is_empty(self): - """ - Returns True if no items on a stack, otherwise returns False - """ if len(self.stack) == 0: return True else: @@ -258,12 +224,23 @@ def is_empty(self): operands = OperandStack() function = OperandStack() +def print_calc_on_stack(*args): + print(args) def calc_on_stack(): operator_on_stack = function.take_from_stack() + global func_argments if operator_on_stack in function_dict.values(): - first_operand = operands.take_from_stack() - current_result = operator_on_stack['operator'](first_operand) + if func_argments: + second_operand = operands.take_from_stack() + first_operand = operands.take_from_stack() + current_result = operator_on_stack['operator'](first_operand, second_operand) + func_argments = False + print_calc_on_stack(first_operand, second_operand,operator_on_stack, current_result) + else: + first_operand = operands.take_from_stack() + current_result = operator_on_stack['operator'](first_operand) + print_calc_on_stack(first_operand, operator_on_stack,current_result) elif operator_on_stack in operator_dict.values(): if len(operands.stack) == 1: second_operand = operands.take_from_stack() @@ -272,6 +249,9 @@ def calc_on_stack(): second_operand = operands.take_from_stack() first_operand = operands.take_from_stack() current_result = operator_on_stack['operator'](first_operand, second_operand) + print_calc_on_stack(first_operand, second_operand, operator_on_stack,current_result) + elif operator_on_stack == '(': + return operands.put_on_stack(current_result) if len(function.stack) and function.top() is not '(': if current_operator['priority'] >= function.top()['priority']: @@ -282,7 +262,7 @@ def calc_on_stack(): operands = OperandStack() function = OperandStack() - +func_argments = False for item in converted_list: if type(item) is float or type(item) is int: operands.put_on_stack(item) @@ -304,7 +284,15 @@ def calc_on_stack(): function.take_from_stack() else: for i in range(len(function.stack)): + if item is ',' and function.top() is '(': + func_argments = True + break + elif func_argments: + calc_on_stack() + else: + func_argments = False current_result = calc_on_stack() + if item is ')': if function.top() is '(': function.take_from_stack() @@ -324,7 +312,7 @@ def calc_on_stack(): -# for i in unary_operators: + @@ -352,77 +340,12 @@ def calc_on_stack(): # print(2.0**(2.0**2.0**2.0**2.0)) # print(math.sin(math.e**math.log(math.e**math.e**math.sin(23.0),45.0) + math.cos(3.0+math.log10(math.e**-math.e)))) # print(sin(pi/2**1) + log10(1*4+2**2+1)) -print(10*e**0*log10(.4 -5/ -0.1-10) - -abs(-53/10) + -5) +# print(10*e**0*log10(.4 -5/ -0.1-10) - -abs(-53/10) + -5) # print(.4-5/-0.1-10) # print(1+2*4/3+1>1+2*4/3+2) +# print(sin(e**log(e**e**sin(23.0),45.0) + log(e**e**sin(23.0),45.0))) +# print(sin(e**log(e**e**sin(23.0),45.0) + cos(3.0+log10(e**-e)))) +print(sin(log(-sin(23.0),45.0)**2 + log(-sin(23.0),45.0)**3)) +# print(log(-sin(23.0),45.0)) - -# def split_operators(s): -# expr = [] -# last_number = "" -# last_letter = "" -# for i in s: -# # if i == ' ': -# # continue -# # elif i is '-': -# # if last_number: -# # last_number = float(last_number) -# # expr.append(last_number) -# # last_number = '' -# # if last_letter: -# # expr.append(func_dict[last_letter]) -# # last_letter = "" -# # if expr: -# # expr.append(d['+']) -# # expr.append(-1.0) -# # expr.append(d['*']) -# # last_number = '' -# # else: -# # expr.append(-1.0) -# # expr.append(d['*']) -# if i.isnumeric() or i is '.': -# last_number += i -# elif i.isalpha(): -# last_letter += i -# else: -# if last_number and last_number is not '-': -# last_number = float(last_number) -# expr.append(last_number) -# last_number = "" -# if last_letter: -# if last_letter == 'e' or last_letter == 'pi': -# expr.append(func_dict[last_letter]['operator']) -# else: -# expr.append(func_dict[last_letter]) -# last_letter = "" -# if i: -# if i in d.keys(): -# expr.append(d[i]) -# elif i in func_dict.keys(): -# expr.append(func_dict[last_letter]) -# else: -# expr.append(i) -# if last_number: -# last_number = float(last_number) -# expr.append(last_number) -# elif last_letter: -# expr.append(func_dict[last_letter]) -# return expr - -# d = { -# '+': '+', -# '-': '-', -# '/': '/', -# '*': '*', -# '%': {'operator': operator.mod, 'priority': 3}, -# '//': {'operator': operator.floordiv, 'priority': 3}, -# '^': {'operator': operator.pow, 'priority': 1}, -# '==': {'operator': operator.eq, 'priority': 9}, -# '!=': {'operator': operator.ne, 'priority': 9}, -# '>': {'operator': operator.gt, 'priority': 9}, -# '<': {'operator': operator.lt, 'priority': 9}, -# '>=': {'operator': operator.ge, 'priority': 9}, -# '<=': {'operator': operator.le, 'priority': 9}, -# -# } \ No newline at end of file From 62abf568d7259715a8ee60431c15ba4b469b861e Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Sun, 14 Apr 2019 23:15:56 +0300 Subject: [PATCH 003/103] refactor: extract main function "calculate", add list of examples of the expressions --- calc.py | 258 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 152 insertions(+), 106 deletions(-) diff --git a/calc.py b/calc.py index 5e90b62a..342e7db5 100644 --- a/calc.py +++ b/calc.py @@ -33,42 +33,47 @@ } -a = "-pi+3-((-60/2^2-1)/(2+3)^(-3))" -# a = "100/3%2^2" -# a = ".1 * 2.0^56.0" -a = '60/2^2---1+-(+e)' -# a = '(2.0^(pi/pi+e/e+2.0^0.0))^(1.0/3.0)' -# a = "102%12%7" -# a = "666" -# a = 'sin(pi)+e*round(2+9)' -a= "sin(pi/2+cos(e)^2)*111*6" -# a = "1--1" -# a = "6-(-+-+13)" -# a= "-+---+-1" -# a = "-13" -# a = "2.0^(2.0^2.0*2.0^2.0)" -a = "sin(e^log(e^e^sin(23.0)*45.0) + cos(3.0+log10(e^-e)))" -# a = ('6') -a = "sin(pi/2^1) + log10(1*4+2^2+1)" -# a = "sin(-cos(-sin(3.0)-cos(-sin(-3.0*5.0)-sin(cos(43.0))))+cos(sin(sin(34.0-2.0^2.0))))--cos(1.0)--cos(0.0)^3.0" -# a = "log10(e)" -a = "10*e^0*log10(.4 -5/ -0.1-10) - -abs(-53/10) + -5" -# a = "log10(.4 -5/ (-0.1)-10)" -# a = ".4 -5/ (-0.1)-10" -# a = "1+2*4/3+1!=1+2*4/3+2" -# a = "8//3" -a = 'sin(log(-sin(23.0),45.0)^2 + log(-sin(23.0),45.0)^3)' -# a= 'log(-sin(23.0),45.0)' -# a = 'sin(e^log(e^e^sin(23.0),45.0) + cos(3.0+log10(e^-e)))' - -unary_operators = [ +example = [ "-13", "6-(-13)", "1---1", "-+---+-1", + "1+2*2", + "1+(2+3*2)*3", + "10*(2+1)", + "10^(2+1)", + "100/3^2", + "100/3%2^2", + "pi+e", + "log(e)", + "sin(pi/2)", + "log10(100)", + "sin(pi/2)*111*6", + "2*sin(pi/2)", + "abs(-5)", + "round(123.45689)", + "102%12%7", + "100/4/3", + "2^3^4", + "1+2*3==1+2*3", + "e^5>=e^5+1", + "1+2*4/3+1!=1+2*4/3+2", + "(100)", + "666", + "-.1", + "1/3", + "1.0/3.0", + ".1 * 2.0^56.0", + "e^34", + "(2.0^(pi/pi+e/e+2.0^0.0))", + "(2.0^(pi/pi+e/e+2.0^0.0))^(1.0/3.0)", + "sin(pi/2^1) + log(1*4+2^2+1, 3^2)", + "10*e^0*log10(.4 -5/ -0.1-10) - -abs(-53/10) + -5", + "sin(-cos(-sin(3.0)-cos(-sin(-3.0*5.0)-sin(cos(log10(43.0))))+cos(sin(sin(34.0-2.0^2.0))))--cos(1.0)--cos(0.0)^3.0)", + "2.0^(2.0^2.0*2.0^2.0)", + "sin(e^log(e^e^sin(23.0),45.0) + cos(3.0+log10(e^-e)))" ] -print(a) def number_parser(number): @@ -83,6 +88,7 @@ def function_parser(function_name): return function_dict[function_name]['operator'] return function_name + def split_operators(s): parsing_list = [] last_number = "" @@ -91,7 +97,6 @@ def split_operators(s): for i in s: if i == " ": continue - if i.isnumeric() or i is '.': if last_symbol: parsing_list.append(last_symbol) @@ -120,12 +125,6 @@ def split_operators(s): parsing_list.append(function_parser(last_letter)) return parsing_list -l_expr = split_operators(a) -print(l_expr) -# for i in l_expr: -# print(i) -# exit() - def clean_add_sub_operators(last_item, converted_list): if last_item.count('-') % 2 == 0: @@ -185,21 +184,9 @@ def converter(parsing_list): converted_list.append(operator_dict['-']) last_item = "" converted_list.append(i) - return converted_list -converted_list = converter(l_expr) -# for i in converted_list: -# print(i) - - -# exit() - - - - - class OperandStack(): def __init__(self): @@ -221,12 +208,6 @@ def is_empty(self): return False -operands = OperandStack() -function = OperandStack() - -def print_calc_on_stack(*args): - print(args) - def calc_on_stack(): operator_on_stack = function.take_from_stack() global func_argments @@ -236,11 +217,11 @@ def calc_on_stack(): first_operand = operands.take_from_stack() current_result = operator_on_stack['operator'](first_operand, second_operand) func_argments = False - print_calc_on_stack(first_operand, second_operand,operator_on_stack, current_result) + # print_calc_on_stack(first_operand, second_operand,operator_on_stack, current_result) else: first_operand = operands.take_from_stack() current_result = operator_on_stack['operator'](first_operand) - print_calc_on_stack(first_operand, operator_on_stack,current_result) + # print_calc_on_stack(first_operand, operator_on_stack,current_result) elif operator_on_stack in operator_dict.values(): if len(operands.stack) == 1: second_operand = operands.take_from_stack() @@ -249,7 +230,7 @@ def calc_on_stack(): second_operand = operands.take_from_stack() first_operand = operands.take_from_stack() current_result = operator_on_stack['operator'](first_operand, second_operand) - print_calc_on_stack(first_operand, second_operand, operator_on_stack,current_result) + # print_calc_on_stack(first_operand, second_operand, operator_on_stack,current_result) elif operator_on_stack == '(': return operands.put_on_stack(current_result) @@ -259,68 +240,131 @@ def calc_on_stack(): return current_result - -operands = OperandStack() -function = OperandStack() -func_argments = False -for item in converted_list: - if type(item) is float or type(item) is int: - operands.put_on_stack(item) - elif item in operator_dict.values() or item in function_dict.values(): - current_operator = item - if function.is_empty(): - function.put_on_stack(current_operator) - else: - if function.top() is '(' or current_operator['priority'] < function.top()['priority'] or \ - current_operator == operator_dict['^'] and function.top() == operator_dict['^']: +def calculacte(converted_list): + global operands, function, func_argments, current_operator, current_result + operands = OperandStack() + function = OperandStack() + func_argments = False + for item in converted_list: + if type(item) is float or type(item) is int: + operands.put_on_stack(item) + elif item in operator_dict.values() or item in function_dict.values(): + current_operator = item + if function.is_empty(): function.put_on_stack(current_operator) else: + if function.top() is '(' or current_operator['priority'] < function.top()['priority'] or \ + current_operator == operator_dict['^'] and function.top() == operator_dict['^']: + function.put_on_stack(current_operator) + else: + current_result = calc_on_stack() + + function.put_on_stack(current_operator) + elif item is '(': + function.put_on_stack(item) + elif item is ')' and function.top() == '(': + function.take_from_stack() + else: + for i in range(len(function.stack)): + if item is ',' and function.top() is '(': + func_argments = True + break + elif func_argments: + calc_on_stack() + else: + func_argments = False current_result = calc_on_stack() - function.put_on_stack(current_operator) - elif item is '(': - function.put_on_stack(item) - elif item is ')' and function.top() == '(': - function.take_from_stack() + if item is ')': + if function.top() is '(': + function.take_from_stack() + break + if function.is_empty(): + current_result = operands.take_from_stack() + elif len(function.stack) == 1: + current_result = calc_on_stack() else: for i in range(len(function.stack)): - if item is ',' and function.top() is '(': - func_argments = True - break - elif func_argments: - calc_on_stack() - else: - func_argments = False + current_operator = function.top() current_result = calc_on_stack() + if not len(function.stack): + break - if item is ')': - if function.top() is '(': - function.take_from_stack() - break -if function.is_empty(): - current_result = operands.take_from_stack() -elif len(function.stack) == 1: - current_result = calc_on_stack() -else: - for i in range(len(function.stack)): - current_operator = function.top() - current_result = calc_on_stack() - if not len(function.stack): - break -print(current_result) +operands = OperandStack() +function = OperandStack() +for expression in example: + string = '' + for i in expression: + if i == '^': + string += '**' + else: + string +=i + try: + calculacte(converter(split_operators(expression))) + except: + print('fault') + if current_result != eval(string): + print(expression) + print(current_result, current_result == eval(string)) + print('\n') + +# operands = OperandStack() +# function = OperandStack() +# +# func_argments = False +# for item in converted_list: +# if type(item) is float or type(item) is int: +# operands.put_on_stack(item) +# elif item in operator_dict.values() or item in function_dict.values(): +# current_operator = item +# if function.is_empty(): +# function.put_on_stack(current_operator) +# else: +# if function.top() is '(' or current_operator['priority'] < function.top()['priority'] or \ +# current_operator == operator_dict['^'] and function.top() == operator_dict['^']: +# function.put_on_stack(current_operator) +# else: +# current_result = calc_on_stack() +# +# function.put_on_stack(current_operator) +# elif item is '(': +# function.put_on_stack(item) +# elif item is ')' and function.top() == '(': +# function.take_from_stack() +# else: +# for i in range(len(function.stack)): +# if item is ',' and function.top() is '(': +# func_argments = True +# break +# elif func_argments: +# calc_on_stack() +# else: +# func_argments = False +# current_result = calc_on_stack() +# +# if item is ')': +# if function.top() is '(': +# function.take_from_stack() +# break +# +# if function.is_empty(): +# current_result = operands.take_from_stack() +# elif len(function.stack) == 1: +# current_result = calc_on_stack() +# else: +# for i in range(len(function.stack)): +# current_operator = function.top() +# current_result = calc_on_stack() +# if not len(function.stack): +# break -# -# for i in range(self.operator_stack.length): -# current_result = self.calculate_on_stack() -# if self.operator_stack.length == 0: -# return current_result # print(-20+34-50*6-11*2/2) # print(54-300+22) @@ -332,7 +376,7 @@ def calc_on_stack(): # print(function.stack) # print(math.sin(math.pi/2+math.cos(math.e)**2)*111*6) # print(sin(-cos(-sin(3.0)-cos(-sin(-3.0*5.0)-sin(cos(43.0))))+cos(sin(sin(34.0-2.0**2.0))))--cos(1.0)--cos(0.0)**3.0) -# print(100/3%2**2) +# print(sin(-cos(-sin(3.0)-cos(-sin(-3.0**5.0)-sin(cos(log10(43.0))))+cos(sin(sin(34.0-2.0**2.0))))--cos(1.0)--cos(0.0)**3.0)) # print((2.0**(math.pi/math.pi+math.e/math.e+2.0**0.0))**(1.0/3.0)) # print(.1*2.0**56.0) # print(math.sin(/math.pi)+math.e*round(2+9)) @@ -345,7 +389,9 @@ def calc_on_stack(): # print(1+2*4/3+1>1+2*4/3+2) # print(sin(e**log(e**e**sin(23.0),45.0) + log(e**e**sin(23.0),45.0))) # print(sin(e**log(e**e**sin(23.0),45.0) + cos(3.0+log10(e**-e)))) -print(sin(log(-sin(23.0),45.0)**2 + log(-sin(23.0),45.0)**3)) +# print(sin(log(-sin(23.0),45.0)**2 + log(-sin(23.0),45.0)**3)) # print(log(-sin(23.0),45.0)) + + From 47c31662f26daa7ca76b9b9b7ce43d57f56d9605 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Mon, 15 Apr 2019 00:09:56 +0300 Subject: [PATCH 004/103] fix: operators with two elements "//, ==, !=, >=, <=" now is valid --- calc.py => final_task/calc.py | 48 +++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 14 deletions(-) rename calc.py => final_task/calc.py (89%) diff --git a/calc.py b/final_task/calc.py similarity index 89% rename from calc.py rename to final_task/calc.py index 342e7db5..1afbc4d9 100644 --- a/calc.py +++ b/final_task/calc.py @@ -16,25 +16,27 @@ operator_dict = { - '+': {'operator': operator.add, 'priority': 4}, - '-': {'operator': operator.sub, 'priority': 4}, - '/': {'operator': operator.truediv, 'priority': 3}, - '*': {'operator': operator.mul, 'priority': 3}, - '%': {'operator': operator.mod, 'priority': 3}, - '//': {'operator': operator.floordiv, 'priority': 3}, + '+': {'operator': operator.add, 'priority': 3}, + '-': {'operator': operator.sub, 'priority': 3}, + '/': {'operator': operator.truediv, 'priority': 2}, + '*': {'operator': operator.mul, 'priority': 2}, + '%': {'operator': operator.mod, 'priority': 2}, + '//': {'operator': operator.floordiv, 'priority': 2}, '^': {'operator': operator.pow, 'priority': 1}, - '==': {'operator': operator.eq, 'priority': 9}, - '!=': {'operator': operator.ne, 'priority': 9}, - '>': {'operator': operator.gt, 'priority': 9}, - '<': {'operator': operator.lt, 'priority': 9}, - '>=': {'operator': operator.ge, 'priority': 9}, - '<=': {'operator': operator.le, 'priority': 9}, + '==': {'operator': operator.eq, 'priority': 4}, + '!=': {'operator': operator.ne, 'priority': 4}, + '>': {'operator': operator.gt, 'priority': 4}, + '<': {'operator': operator.lt, 'priority': 4}, + '>=': {'operator': operator.ge, 'priority': 4}, + '<=': {'operator': operator.le, 'priority': 4}, } +# example = "sin(-cos(-sin(3.0)-cos(-sin(-3.0*5.0)-sin(cos(log10(43.0))))+cos(sin(sin(34.0-2.0^2.0))))--cos(1.0)--cos(0.0)^3.0)" example = [ "-13", + "8//3", "6-(-13)", "1---1", "-+---+-1", @@ -110,6 +112,14 @@ def split_operators(s): parsing_list.append(last_symbol) last_symbol = "" last_letter += i + elif i in "!=<>/": + if last_number: + parsing_list.append(number_parser(last_number)) + last_number = "" + if last_letter: + parsing_list.append(function_parser(last_letter)) + last_letter = "" + last_symbol += i else: if last_number: parsing_list.append(number_parser(last_number)) @@ -117,6 +127,8 @@ def split_operators(s): if last_letter: parsing_list.append(function_parser(last_letter)) last_letter = "" + # if i in str(operator_dict.keys()): + # last_symbol += i if i: parsing_list.append(i) if last_number: @@ -289,11 +301,19 @@ def calculacte(converted_list): current_result = calc_on_stack() if not len(function.stack): break + return current_result operands = OperandStack() function = OperandStack() +# print('example: {}'.format(example)) +# parser = split_operators(example) +# print(parser) +# converted_list = converter(parser) +# result = calculacte(converted_list) +# print(result) + for expression in example: string = '' for i in expression: @@ -304,8 +324,8 @@ def calculacte(converted_list): try: calculacte(converter(split_operators(expression))) except: - print('fault') - if current_result != eval(string): + print('fault on {} \n'.format(expression)) + if current_result == eval(string): print(expression) print(current_result, current_result == eval(string)) print('\n') From c110200ef5039b98fce58df50922a296f2d32c2f Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Sun, 21 Apr 2019 23:10:51 +0300 Subject: [PATCH 005/103] fix: resolve problem in converter function when we have sub function --- final_task/calc.py | 174 +++++++++++---------------------------------- 1 file changed, 43 insertions(+), 131 deletions(-) diff --git a/final_task/calc.py b/final_task/calc.py index 1afbc4d9..7957b1ec 100644 --- a/final_task/calc.py +++ b/final_task/calc.py @@ -32,7 +32,6 @@ } -# example = "sin(-cos(-sin(3.0)-cos(-sin(-3.0*5.0)-sin(cos(log10(43.0))))+cos(sin(sin(34.0-2.0^2.0))))--cos(1.0)--cos(0.0)^3.0)" example = [ "-13", @@ -127,8 +126,6 @@ def split_operators(s): if last_letter: parsing_list.append(function_parser(last_letter)) last_letter = "" - # if i in str(operator_dict.keys()): - # last_symbol += i if i: parsing_list.append(i) if last_number: @@ -161,10 +158,10 @@ def converter(parsing_list): if last_item == '+': converted_list.append(operator_dict[last_item]) last_item = '' - if type(i) is float or type(i) is int: + if type(i) is float or type(i) is int: if last_item == '-' and converted_list[-1] != '(' and converted_list[-1] not in operator_dict.values(): - converted_list.append(operator_dict['+']) - converted_list.append(-i) + converted_list.append(operator_dict[last_item]) + converted_list.append(i) last_item = "" elif last_item == '-': converted_list.append(-i) @@ -198,7 +195,6 @@ def converter(parsing_list): converted_list.append(i) return converted_list - class OperandStack(): def __init__(self): @@ -222,18 +218,16 @@ def is_empty(self): def calc_on_stack(): operator_on_stack = function.take_from_stack() - global func_argments + global func_arguments if operator_on_stack in function_dict.values(): - if func_argments: + if func_arguments: second_operand = operands.take_from_stack() first_operand = operands.take_from_stack() current_result = operator_on_stack['operator'](first_operand, second_operand) - func_argments = False - # print_calc_on_stack(first_operand, second_operand,operator_on_stack, current_result) + func_arguments = False else: first_operand = operands.take_from_stack() current_result = operator_on_stack['operator'](first_operand) - # print_calc_on_stack(first_operand, operator_on_stack,current_result) elif operator_on_stack in operator_dict.values(): if len(operands.stack) == 1: second_operand = operands.take_from_stack() @@ -242,7 +236,6 @@ def calc_on_stack(): second_operand = operands.take_from_stack() first_operand = operands.take_from_stack() current_result = operator_on_stack['operator'](first_operand, second_operand) - # print_calc_on_stack(first_operand, second_operand, operator_on_stack,current_result) elif operator_on_stack == '(': return operands.put_on_stack(current_result) @@ -252,11 +245,11 @@ def calc_on_stack(): return current_result -def calculacte(converted_list): - global operands, function, func_argments, current_operator, current_result +def calculate(converted_list): + global operands, function, func_arguments, current_operator, current_result operands = OperandStack() function = OperandStack() - func_argments = False + func_arguments = False for item in converted_list: if type(item) is float or type(item) is int: operands.put_on_stack(item) @@ -270,7 +263,6 @@ def calculacte(converted_list): function.put_on_stack(current_operator) else: current_result = calc_on_stack() - function.put_on_stack(current_operator) elif item is '(': function.put_on_stack(item) @@ -279,18 +271,18 @@ def calculacte(converted_list): else: for i in range(len(function.stack)): if item is ',' and function.top() is '(': - func_argments = True + func_arguments = True break - elif func_argments: - calc_on_stack() + elif func_arguments: + current_result = calc_on_stack() else: - func_argments = False - current_result = calc_on_stack() - - if item is ')': - if function.top() is '(': - function.take_from_stack() - break + func_arguments = False + if len(function.stack): + current_result = calc_on_stack() + if item is ')' and len(function.stack): + if function.top() is '(': + function.take_from_stack() + break if function.is_empty(): current_result = operands.take_from_stack() elif len(function.stack) == 1: @@ -307,110 +299,30 @@ def calculacte(converted_list): operands = OperandStack() function = OperandStack() -# print('example: {}'.format(example)) -# parser = split_operators(example) -# print(parser) -# converted_list = converter(parser) -# result = calculacte(converted_list) -# print(result) - -for expression in example: - string = '' - for i in expression: - if i == '^': - string += '**' - else: - string +=i - try: - calculacte(converter(split_operators(expression))) - except: - print('fault on {} \n'.format(expression)) - if current_result == eval(string): - print(expression) - print(current_result, current_result == eval(string)) - print('\n') - -# operands = OperandStack() -# function = OperandStack() -# -# func_argments = False -# for item in converted_list: -# if type(item) is float or type(item) is int: -# operands.put_on_stack(item) -# elif item in operator_dict.values() or item in function_dict.values(): -# current_operator = item -# if function.is_empty(): -# function.put_on_stack(current_operator) -# else: -# if function.top() is '(' or current_operator['priority'] < function.top()['priority'] or \ -# current_operator == operator_dict['^'] and function.top() == operator_dict['^']: -# function.put_on_stack(current_operator) -# else: -# current_result = calc_on_stack() -# -# function.put_on_stack(current_operator) -# elif item is '(': -# function.put_on_stack(item) -# elif item is ')' and function.top() == '(': -# function.take_from_stack() -# else: -# for i in range(len(function.stack)): -# if item is ',' and function.top() is '(': -# func_argments = True -# break -# elif func_argments: -# calc_on_stack() -# else: -# func_argments = False -# current_result = calc_on_stack() -# -# if item is ')': -# if function.top() is '(': -# function.take_from_stack() -# break -# -# if function.is_empty(): -# current_result = operands.take_from_stack() -# elif len(function.stack) == 1: -# current_result = calc_on_stack() -# else: -# for i in range(len(function.stack)): -# current_operator = function.top() -# current_result = calc_on_stack() -# if not len(function.stack): -# break - - - - - - - -# print(-20+34-50*6-11*2/2) -# print(54-300+22) -# print(-math.pi+3-((-60/2**2-1)/(2+3)**(-3))) -# print(60/2**2-1) -# print(102%12%7) -# print(60/2**2---1+-+math.e) -# print(operands.stack) -# print(function.stack) -# print(math.sin(math.pi/2+math.cos(math.e)**2)*111*6) -# print(sin(-cos(-sin(3.0)-cos(-sin(-3.0*5.0)-sin(cos(43.0))))+cos(sin(sin(34.0-2.0**2.0))))--cos(1.0)--cos(0.0)**3.0) -# print(sin(-cos(-sin(3.0)-cos(-sin(-3.0**5.0)-sin(cos(log10(43.0))))+cos(sin(sin(34.0-2.0**2.0))))--cos(1.0)--cos(0.0)**3.0)) -# print((2.0**(math.pi/math.pi+math.e/math.e+2.0**0.0))**(1.0/3.0)) -# print(.1*2.0**56.0) -# print(math.sin(/math.pi)+math.e*round(2+9)) -# print(math.sin(math.pi/2)*111*6) -# print(2.0**(2.0**2.0**2.0**2.0)) -# print(math.sin(math.e**math.log(math.e**math.e**math.sin(23.0),45.0) + math.cos(3.0+math.log10(math.e**-math.e)))) -# print(sin(pi/2**1) + log10(1*4+2**2+1)) -# print(10*e**0*log10(.4 -5/ -0.1-10) - -abs(-53/10) + -5) -# print(.4-5/-0.1-10) -# print(1+2*4/3+1>1+2*4/3+2) -# print(sin(e**log(e**e**sin(23.0),45.0) + log(e**e**sin(23.0),45.0))) -# print(sin(e**log(e**e**sin(23.0),45.0) + cos(3.0+log10(e**-e)))) -# print(sin(log(-sin(23.0),45.0)**2 + log(-sin(23.0),45.0)**3)) -# print(log(-sin(23.0),45.0)) +if type(example) is str: + print('example: {}'.format(example)) + parser = split_operators(example) + print(parser) + converted_list = converter(parser) + print(converted_list) + result = calculate(converted_list) + print(result) +else: + for expression in example: + string = '' + for i in expression: + if i == '^': + string += '**' + else: + string +=i + try: + calculate(converter(split_operators(expression))) + except: + print('fault on {} \n'.format(expression)) + if current_result == eval(string): + print(expression) + print(current_result, current_result == eval(string)) + print('\n') From bec2b2ae2acabc8d1ba6d5b78b6d3335d39be545 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Mon, 22 Apr 2019 15:46:25 +0300 Subject: [PATCH 006/103] feat: add argparse module, make distribution package wtih setuptools and define the main function to run the calculator --- final_task/calc.py | 153 ++++++++++++++++++++++++-------------------- final_task/setup.py | 13 ++++ 2 files changed, 95 insertions(+), 71 deletions(-) diff --git a/final_task/calc.py b/final_task/calc.py index 7957b1ec..3c2741c9 100644 --- a/final_task/calc.py +++ b/final_task/calc.py @@ -2,9 +2,16 @@ import operator import math +from argparse import ArgumentParser from math import * +parser = ArgumentParser(description='Pure-python command-line calculator.', prog='pycalc') +parser.add_argument('-m', '--use-modules', help='additional modules to use', metavar='MODULE [MODULE ...]') +parser.add_argument('EXPRESSION', help='expression string to calculate') +expression_line = parser.parse_args().EXPRESSION + + function_dict = { 'abs': {'operator': abs, 'priority': 0}, 'round': {'operator': round, 'priority': 0} @@ -33,48 +40,48 @@ } -example = [ - "-13", - "8//3", - "6-(-13)", - "1---1", - "-+---+-1", - "1+2*2", - "1+(2+3*2)*3", - "10*(2+1)", - "10^(2+1)", - "100/3^2", - "100/3%2^2", - "pi+e", - "log(e)", - "sin(pi/2)", - "log10(100)", - "sin(pi/2)*111*6", - "2*sin(pi/2)", - "abs(-5)", - "round(123.45689)", - "102%12%7", - "100/4/3", - "2^3^4", - "1+2*3==1+2*3", - "e^5>=e^5+1", - "1+2*4/3+1!=1+2*4/3+2", - "(100)", - "666", - "-.1", - "1/3", - "1.0/3.0", - ".1 * 2.0^56.0", - "e^34", - "(2.0^(pi/pi+e/e+2.0^0.0))", - "(2.0^(pi/pi+e/e+2.0^0.0))^(1.0/3.0)", - "sin(pi/2^1) + log(1*4+2^2+1, 3^2)", - "10*e^0*log10(.4 -5/ -0.1-10) - -abs(-53/10) + -5", - "sin(-cos(-sin(3.0)-cos(-sin(-3.0*5.0)-sin(cos(log10(43.0))))+cos(sin(sin(34.0-2.0^2.0))))--cos(1.0)--cos(0.0)^3.0)", - "2.0^(2.0^2.0*2.0^2.0)", - "sin(e^log(e^e^sin(23.0),45.0) + cos(3.0+log10(e^-e)))" - -] +# example = [ +# "-13", +# "8//3", +# "6-(-13)", +# "1---1", +# "-+---+-1", +# "1+2*2", +# "1+(2+3*2)*3", +# "10*(2+1)", +# "10^(2+1)", +# "100/3^2", +# "100/3%2^2", +# "pi+e", +# "log(e)", +# "sin(pi/2)", +# "log10(100)", +# "sin(pi/2)*111*6", +# "2*sin(pi/2)", +# "abs(-5)", +# "round(123.45689)", +# "102%12%7", +# "100/4/3", +# "2^3^4", +# "1+2*3==1+2*3", +# "e^5>=e^5+1", +# "1+2*4/3+1!=1+2*4/3+2", +# "(100)", +# "666", +# "-.1", +# "1/3", +# "1.0/3.0", +# ".1 * 2.0^56.0", +# "e^34", +# "(2.0^(pi/pi+e/e+2.0^0.0))", +# "(2.0^(pi/pi+e/e+2.0^0.0))^(1.0/3.0)", +# "sin(pi/2^1) + log(1*4+2^2+1, 3^2)", +# "10*e^0*log10(.4 -5/ -0.1-10) - -abs(-53/10) + -5", +# "sin(-cos(-sin(3.0)-cos(-sin(-3.0*5.0)-sin(cos(log10(43.0))))+cos(sin(sin(34.0-2.0^2.0))))--cos(1.0)--cos(0.0)^3.0)", +# "2.0^(2.0^2.0*2.0^2.0)", +# "sin(e^log(e^e^sin(23.0),45.0) + cos(3.0+log10(e^-e)))" +# +# ] def number_parser(number): @@ -90,12 +97,12 @@ def function_parser(function_name): return function_name -def split_operators(s): +def split_operators(expression_line): parsing_list = [] last_number = "" last_letter = "" last_symbol = "" - for i in s: + for i in expression_line: if i == " ": continue if i.isnumeric() or i is '.': @@ -296,34 +303,38 @@ def calculate(converted_list): return current_result -operands = OperandStack() -function = OperandStack() - -if type(example) is str: - print('example: {}'.format(example)) - parser = split_operators(example) - print(parser) - converted_list = converter(parser) - print(converted_list) - result = calculate(converted_list) - print(result) -else: - for expression in example: - string = '' - for i in expression: - if i == '^': - string += '**' - else: - string +=i - try: - calculate(converter(split_operators(expression))) - except: - print('fault on {} \n'.format(expression)) - if current_result == eval(string): - print(expression) - print(current_result, current_result == eval(string)) - print('\n') +def main(): + operands = OperandStack() + function = OperandStack() + if type(expression_line) is str: + print('example: {}'.format(expression_line)) + parser = split_operators(expression_line) + print(parser) + converted_list = converter(parser) + print(converted_list) + result = calculate(converted_list) + print(result) + else: + for expression in expression_line: + string = '' + for i in expression: + if i == '^': + string += '**' + else: + string +=i + try: + calculate(converter(split_operators(expression))) + except: + print('fault on {} \n'.format(expression)) + if current_result == eval(string): + print(expression) + print(current_result, current_result == eval(string)) + print('\n') + + +if __name__ == '__main__': + main() diff --git a/final_task/setup.py b/final_task/setup.py index e69de29b..2f25ee11 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -0,0 +1,13 @@ +from setuptools import setup, find_packages + +setup( + name='pycalc', + version='1.0', + author='Mikhail Sauchuk', + author_email='mishasavchuk@gmail.com', + packages=find_packages(), + entry_points={'console_scripts': ['pycalc = calc:main']}, + description='Pure-python command-line calculator.', + platforms='any', +) + From 2025e623ff3b6c20980a16671422d15c81e73c2b Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Thu, 25 Apr 2019 21:41:12 +0300 Subject: [PATCH 007/103] feat: add class Error and function check_expression to make validation of the users experession, add try-exsept block to main function --- final_task/calc.py | 179 ++++++++++++++++++--------------------------- 1 file changed, 73 insertions(+), 106 deletions(-) diff --git a/final_task/calc.py b/final_task/calc.py index 3c2741c9..b25933d1 100644 --- a/final_task/calc.py +++ b/final_task/calc.py @@ -3,7 +3,6 @@ import operator import math from argparse import ArgumentParser -from math import * parser = ArgumentParser(description='Pure-python command-line calculator.', prog='pycalc') @@ -36,52 +35,37 @@ '<': {'operator': operator.lt, 'priority': 4}, '>=': {'operator': operator.ge, 'priority': 4}, '<=': {'operator': operator.le, 'priority': 4}, - } +errors = { + 1: 'ERROR: Expression cannot be empty', + 2: lambda arg: 'ERROR: Extra operator {} at the end of an expression!'.format(arg), + 3: 'ERROR: Opening bracket required!', + 4: 'ERROR: Closing bracket required!', + } + + +class Error(Exception): + + def __init__(self, id, arg=None): + self.arg = arg + self.id = id + if self.arg is not None: + self.text = errors[self.id](self.arg) + else: + self.text = errors[self.id] + -# example = [ -# "-13", -# "8//3", -# "6-(-13)", -# "1---1", -# "-+---+-1", -# "1+2*2", -# "1+(2+3*2)*3", -# "10*(2+1)", -# "10^(2+1)", -# "100/3^2", -# "100/3%2^2", -# "pi+e", -# "log(e)", -# "sin(pi/2)", -# "log10(100)", -# "sin(pi/2)*111*6", -# "2*sin(pi/2)", -# "abs(-5)", -# "round(123.45689)", -# "102%12%7", -# "100/4/3", -# "2^3^4", -# "1+2*3==1+2*3", -# "e^5>=e^5+1", -# "1+2*4/3+1!=1+2*4/3+2", -# "(100)", -# "666", -# "-.1", -# "1/3", -# "1.0/3.0", -# ".1 * 2.0^56.0", -# "e^34", -# "(2.0^(pi/pi+e/e+2.0^0.0))", -# "(2.0^(pi/pi+e/e+2.0^0.0))^(1.0/3.0)", -# "sin(pi/2^1) + log(1*4+2^2+1, 3^2)", -# "10*e^0*log10(.4 -5/ -0.1-10) - -abs(-53/10) + -5", -# "sin(-cos(-sin(3.0)-cos(-sin(-3.0*5.0)-sin(cos(log10(43.0))))+cos(sin(sin(34.0-2.0^2.0))))--cos(1.0)--cos(0.0)^3.0)", -# "2.0^(2.0^2.0*2.0^2.0)", -# "sin(e^log(e^e^sin(23.0),45.0) + cos(3.0+log10(e^-e)))" -# -# ] +def check_expression(expression_line): + if not expression_line: + raise Error(id=1) + if expression_line[-1] in operator_dict.keys(): + raise Error(id=2, arg=expression_line[-1]) + if expression_line.count('(') < expression_line.count(')'): + raise Error(id=3) + elif expression_line.count('(') > expression_line.count(')'): + raise Error(id=4) + return True def number_parser(number): @@ -102,50 +86,51 @@ def split_operators(expression_line): last_number = "" last_letter = "" last_symbol = "" - for i in expression_line: - if i == " ": - continue - if i.isnumeric() or i is '.': - if last_symbol: - parsing_list.append(last_symbol) - last_symbol = "" - if last_letter: + if check_expression(expression_line): + for i in expression_line: + if i == " ": + continue + if i.isnumeric() or i is '.': + if last_symbol: + parsing_list.append(last_symbol) + last_symbol = "" + if last_letter: + last_letter += i + else: + last_number += i + elif i.isalpha(): + if last_symbol: + parsing_list.append(last_symbol) + last_symbol = "" last_letter += i + elif i in "!=<>/": + if last_number: + parsing_list.append(number_parser(last_number)) + last_number = "" + if last_letter: + parsing_list.append(function_parser(last_letter)) + last_letter = "" + last_symbol += i else: - last_number += i - elif i.isalpha(): - if last_symbol: - parsing_list.append(last_symbol) - last_symbol = "" - last_letter += i - elif i in "!=<>/": - if last_number: - parsing_list.append(number_parser(last_number)) - last_number = "" - if last_letter: - parsing_list.append(function_parser(last_letter)) - last_letter = "" - last_symbol += i - else: - if last_number: - parsing_list.append(number_parser(last_number)) - last_number = "" - if last_letter: - parsing_list.append(function_parser(last_letter)) - last_letter = "" - if i: - parsing_list.append(i) - if last_number: - parsing_list.append(number_parser(last_number)) - elif last_letter: - parsing_list.append(function_parser(last_letter)) + if last_number: + parsing_list.append(number_parser(last_number)) + last_number = "" + if last_letter: + parsing_list.append(function_parser(last_letter)) + last_letter = "" + if i: + parsing_list.append(i) + if last_number: + parsing_list.append(number_parser(last_number)) + elif last_letter: + parsing_list.append(function_parser(last_letter)) return parsing_list def clean_add_sub_operators(last_item, converted_list): if last_item.count('-') % 2 == 0: if converted_list[-1] == '(': - last_item= "" + last_item = "" else: last_item = '+' else: @@ -165,7 +150,7 @@ def converter(parsing_list): if last_item == '+': converted_list.append(operator_dict[last_item]) last_item = '' - if type(i) is float or type(i) is int: + if type(i) is float or type(i) is int: if last_item == '-' and converted_list[-1] != '(' and converted_list[-1] not in operator_dict.values(): converted_list.append(operator_dict[last_item]) converted_list.append(i) @@ -177,7 +162,7 @@ def converter(parsing_list): converted_list.append(i) elif i in operator_dict.keys(): if i == '-' or i == '+': - last_item +=i + last_item += i else: converted_list.append(operator_dict[i]) elif i in function_dict.keys(): @@ -202,7 +187,8 @@ def converter(parsing_list): converted_list.append(i) return converted_list -class OperandStack(): + +class OperandStack: def __init__(self): self.stack = list() @@ -308,33 +294,14 @@ def main(): function = OperandStack() if type(expression_line) is str: - print('example: {}'.format(expression_line)) parser = split_operators(expression_line) - print(parser) converted_list = converter(parser) - print(converted_list) result = calculate(converted_list) print(result) - else: - for expression in expression_line: - string = '' - for i in expression: - if i == '^': - string += '**' - else: - string +=i - try: - calculate(converter(split_operators(expression))) - except: - print('fault on {} \n'.format(expression)) - if current_result == eval(string): - print(expression) - print(current_result, current_result == eval(string)) - print('\n') if __name__ == '__main__': - main() - - - + try: + main() + except Error as err: + print(err.text) From 4c79af91925fa74d848e983e87cd2e9a6936dc96 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Thu, 25 Apr 2019 22:20:45 +0300 Subject: [PATCH 008/103] feat: add exception for blank about two operands --- final_task/calc.py | 17 ++++++++++++++++- final_task/setup.py | 1 + 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/final_task/calc.py b/final_task/calc.py index b25933d1..676f8eae 100644 --- a/final_task/calc.py +++ b/final_task/calc.py @@ -42,6 +42,7 @@ 2: lambda arg: 'ERROR: Extra operator {} at the end of an expression!'.format(arg), 3: 'ERROR: Opening bracket required!', 4: 'ERROR: Closing bracket required!', + 5: 'ERROR: Blank symbol between two operands', } @@ -86,11 +87,25 @@ def split_operators(expression_line): last_number = "" last_letter = "" last_symbol = "" + blank_item = False if check_expression(expression_line): for i in expression_line: if i == " ": - continue + blank_item = True + if last_symbol: + parsing_list.append(last_symbol) + last_symbol = "" + elif last_number: + parsing_list.append(number_parser(last_number)) + last_number = "" + elif last_letter: + parsing_list.append(function_parser(last_letter)) + last_letter = "" if i.isnumeric() or i is '.': + if blank_item and type(parsing_list[-1]) is not str: + raise Error(id=5) + elif blank_item: + blank_item = False if last_symbol: parsing_list.append(last_symbol) last_symbol = "" diff --git a/final_task/setup.py b/final_task/setup.py index 2f25ee11..785bfd07 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -9,5 +9,6 @@ entry_points={'console_scripts': ['pycalc = calc:main']}, description='Pure-python command-line calculator.', platforms='any', + py_modules=['pycalc'] ) From 47373251e8ef05637e01680b5c6ca69b855f86e4 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Fri, 10 May 2019 19:50:51 +0300 Subject: [PATCH 009/103] feat: update method function_parser with tau as a constant; extract ArgumentParser into method arg_parse; rename file calc.py to pycalc.py --- final_task/{calc.py => pycalc.py} | 19 +++++++++++++++---- final_task/setup.py | 15 ++++++++++++++- 2 files changed, 29 insertions(+), 5 deletions(-) rename final_task/{calc.py => pycalc.py} (94%) diff --git a/final_task/calc.py b/final_task/pycalc.py similarity index 94% rename from final_task/calc.py rename to final_task/pycalc.py index 676f8eae..9df1df26 100644 --- a/final_task/calc.py +++ b/final_task/pycalc.py @@ -2,15 +2,20 @@ import operator import math +import sys from argparse import ArgumentParser -parser = ArgumentParser(description='Pure-python command-line calculator.', prog='pycalc') -parser.add_argument('-m', '--use-modules', help='additional modules to use', metavar='MODULE [MODULE ...]') -parser.add_argument('EXPRESSION', help='expression string to calculate') -expression_line = parser.parse_args().EXPRESSION +def arg_parser(): + parser = ArgumentParser(description='Pure-python command-line calculator.', prog='pycalc') + parser.add_argument('-m', '--use-modules', help='additional modules to use', metavar='MODULE [MODULE ...]') + parser.add_argument('EXPRESSION', help='expression string to calculate') + expression_line = parser.parse_args().EXPRESSION + return expression_line +arg_parser() + function_dict = { 'abs': {'operator': abs, 'priority': 0}, 'round': {'operator': round, 'priority': 0} @@ -79,6 +84,11 @@ def number_parser(number): def function_parser(function_name): if function_name == 'e' or function_name == 'pi': return function_dict[function_name]['operator'] + elif function_name == 'tau': + if sys.version_info >= (3, 6): + return function_dict[function_name]['operator'] + else: + return 2 * function_dict['e']['operator'] return function_name @@ -305,6 +315,7 @@ def calculate(converted_list): def main(): + expression_line = arg_parser() operands = OperandStack() function = OperandStack() diff --git a/final_task/setup.py b/final_task/setup.py index 785bfd07..efa1c938 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -6,9 +6,22 @@ author='Mikhail Sauchuk', author_email='mishasavchuk@gmail.com', packages=find_packages(), - entry_points={'console_scripts': ['pycalc = calc:main']}, + entry_points={'console_scripts': ['pycalc = pycalc:main']}, description='Pure-python command-line calculator.', platforms='any', py_modules=['pycalc'] ) + +from setuptools import setup + +setup( + # name='pycalc', + # version='1.0', + # description='Pure-python command-line calculator.', + # author='Dubovik Pavel', + # author_email='geometryk@gmail.com', + # py_modules=['pycalc'], + entry_points = {'console_scripts': ['pycalc=pycalc:byild_parser',],}, + # platforms='any', +) \ No newline at end of file From f789ce852d7283ea225da2bc74638cc4a65cc025 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Fri, 10 May 2019 20:29:45 +0300 Subject: [PATCH 010/103] refactor: update long lines in the code, add some whitespaces --- final_task/pycalc.py | 32 +++++++++++++++++++------------- final_task/setup.py | 14 -------------- 2 files changed, 19 insertions(+), 27 deletions(-) diff --git a/final_task/pycalc.py b/final_task/pycalc.py index 9df1df26..948232f0 100644 --- a/final_task/pycalc.py +++ b/final_task/pycalc.py @@ -3,13 +3,20 @@ import operator import math import sys -from argparse import ArgumentParser +import argparse def arg_parser(): - parser = ArgumentParser(description='Pure-python command-line calculator.', prog='pycalc') - parser.add_argument('-m', '--use-modules', help='additional modules to use', metavar='MODULE [MODULE ...]') - parser.add_argument('EXPRESSION', help='expression string to calculate') + parser = argparse.ArgumentParser(description='Pure-python command-line calculator.', prog='pycalc') + parser.add_argument( + '-m', + '--use-modules', + help='additional modules to use', + metavar='MODULE [MODULE ...]' + ) + parser.add_argument( + 'EXPRESSION', help='expression string to calculate' + ) expression_line = parser.parse_args().EXPRESSION return expression_line @@ -20,7 +27,7 @@ def arg_parser(): 'abs': {'operator': abs, 'priority': 0}, 'round': {'operator': round, 'priority': 0} } -for k,v in math.__dict__.items(): +for k, v in math.__dict__.items(): if k.startswith('_'): continue function_dict[k] = {'operator': v, 'priority': 0} @@ -77,7 +84,7 @@ def check_expression(expression_line): def number_parser(number): try: return int(number) - except: + except ValueError: return float(number) @@ -176,7 +183,8 @@ def converter(parsing_list): converted_list.append(operator_dict[last_item]) last_item = '' if type(i) is float or type(i) is int: - if last_item == '-' and converted_list[-1] != '(' and converted_list[-1] not in operator_dict.values(): + if last_item == '-' and converted_list[-1] != '(' \ + and converted_list[-1] not in operator_dict.values(): converted_list.append(operator_dict[last_item]) converted_list.append(i) last_item = "" @@ -318,12 +326,10 @@ def main(): expression_line = arg_parser() operands = OperandStack() function = OperandStack() - - if type(expression_line) is str: - parser = split_operators(expression_line) - converted_list = converter(parser) - result = calculate(converted_list) - print(result) + parser = split_operators(expression_line) + converted_list = converter(parser) + result = calculate(converted_list) + print(result) if __name__ == '__main__': diff --git a/final_task/setup.py b/final_task/setup.py index efa1c938..6673483b 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -11,17 +11,3 @@ platforms='any', py_modules=['pycalc'] ) - - -from setuptools import setup - -setup( - # name='pycalc', - # version='1.0', - # description='Pure-python command-line calculator.', - # author='Dubovik Pavel', - # author_email='geometryk@gmail.com', - # py_modules=['pycalc'], - entry_points = {'console_scripts': ['pycalc=pycalc:byild_parser',],}, - # platforms='any', -) \ No newline at end of file From 164b04948d42e5207eca5357bfef566ba7fe5448 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Fri, 10 May 2019 21:05:16 +0300 Subject: [PATCH 011/103] feat: update method split_operators to calculate functions with several arguments --- final_task/pycalc.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/final_task/pycalc.py b/final_task/pycalc.py index 948232f0..d664f19a 100644 --- a/final_task/pycalc.py +++ b/final_task/pycalc.py @@ -21,8 +21,6 @@ def arg_parser(): return expression_line -arg_parser() - function_dict = { 'abs': {'operator': abs, 'priority': 0}, 'round': {'operator': round, 'priority': 0} @@ -55,6 +53,8 @@ def arg_parser(): 3: 'ERROR: Opening bracket required!', 4: 'ERROR: Closing bracket required!', 5: 'ERROR: Blank symbol between two operands', + 6: 'ERROR: Typo in the operand (two comma)', + 7: 'ERROR: Blank symbol between twice operator' } @@ -129,13 +129,20 @@ def split_operators(expression_line): if last_letter: last_letter += i else: - last_number += i + if '.' in last_number and i == '.': + raise Error(id=6, arg=last_number) + else: + last_number += i elif i.isalpha(): if last_symbol: parsing_list.append(last_symbol) last_symbol = "" last_letter += i elif i in "!=<>/": + if blank_item and str(parsing_list[-1]) in '!=<>/*': + raise Error(id=7) + elif blank_item: + blank_item = False if last_number: parsing_list.append(number_parser(last_number)) last_number = "" @@ -150,7 +157,10 @@ def split_operators(expression_line): if last_letter: parsing_list.append(function_parser(last_letter)) last_letter = "" - if i: + if last_symbol: + parsing_list.append(last_symbol) + last_symbol = "" + if i != ' ': parsing_list.append(i) if last_number: parsing_list.append(number_parser(last_number)) @@ -177,6 +187,8 @@ def converter(parsing_list): converted_list = [] last_item = "" for i in parsing_list: + if i == " ": + continue if i != '-' and i != '+' and last_item: last_item = clean_add_sub_operators(last_item, converted_list) if last_item == '+': From 9aa595813e42f9992c123c9ff51757f9e39c3558 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Fri, 10 May 2019 21:49:20 +0300 Subject: [PATCH 012/103] feat: remove class Errors, raise SyntaxError inside code --- final_task/pycalc.py | 45 ++++++++++++++------------------------------ 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/final_task/pycalc.py b/final_task/pycalc.py index d664f19a..f111cd05 100644 --- a/final_task/pycalc.py +++ b/final_task/pycalc.py @@ -47,44 +47,23 @@ def arg_parser(): '<=': {'operator': operator.le, 'priority': 4}, } -errors = { - 1: 'ERROR: Expression cannot be empty', - 2: lambda arg: 'ERROR: Extra operator {} at the end of an expression!'.format(arg), - 3: 'ERROR: Opening bracket required!', - 4: 'ERROR: Closing bracket required!', - 5: 'ERROR: Blank symbol between two operands', - 6: 'ERROR: Typo in the operand (two comma)', - 7: 'ERROR: Blank symbol between twice operator' - } - - -class Error(Exception): - - def __init__(self, id, arg=None): - self.arg = arg - self.id = id - if self.arg is not None: - self.text = errors[self.id](self.arg) - else: - self.text = errors[self.id] - def check_expression(expression_line): if not expression_line: - raise Error(id=1) + raise SyntaxError('Expression cannot be empty') if expression_line[-1] in operator_dict.keys(): - raise Error(id=2, arg=expression_line[-1]) + raise SyntaxError('ERROR: Extra operator {} at the end of an expression!'.format(expression_line[-1])) if expression_line.count('(') < expression_line.count(')'): - raise Error(id=3) + raise SyntaxError('ERROR: Closing bracket required!') elif expression_line.count('(') > expression_line.count(')'): - raise Error(id=4) + raise SyntaxError('ERROR: Closing bracket required!') return True def number_parser(number): try: return int(number) - except ValueError: + except SyntaxError: return float(number) @@ -120,7 +99,7 @@ def split_operators(expression_line): last_letter = "" if i.isnumeric() or i is '.': if blank_item and type(parsing_list[-1]) is not str: - raise Error(id=5) + raise SyntaxError('Blank symbol between two operands') elif blank_item: blank_item = False if last_symbol: @@ -130,7 +109,7 @@ def split_operators(expression_line): last_letter += i else: if '.' in last_number and i == '.': - raise Error(id=6, arg=last_number) + raise SyntaxError('ERROR: Typo in the operand (two comma)') else: last_number += i elif i.isalpha(): @@ -140,7 +119,7 @@ def split_operators(expression_line): last_letter += i elif i in "!=<>/": if blank_item and str(parsing_list[-1]) in '!=<>/*': - raise Error(id=7) + raise SyntaxError('ERROR: Blank symbol between twice operator') elif blank_item: blank_item = False if last_number: @@ -347,5 +326,9 @@ def main(): if __name__ == '__main__': try: main() - except Error as err: - print(err.text) + except SyntaxError as err: + print(err) + except ZeroDivisionError as err: + print('ERROR: {}!'.format(err)) + except ValueError as err: + print('ERROR: math error!') From 5302999db56c7e1272a451049875a53bf722123f Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Fri, 10 May 2019 22:02:39 +0300 Subject: [PATCH 013/103] fix: return ValueError in number_parser function --- final_task/pycalc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/final_task/pycalc.py b/final_task/pycalc.py index f111cd05..64fb71bb 100644 --- a/final_task/pycalc.py +++ b/final_task/pycalc.py @@ -63,7 +63,7 @@ def check_expression(expression_line): def number_parser(number): try: return int(number) - except SyntaxError: + except ValueError: return float(number) From 765b59a567ce323222dc2ef6e8e886b4587b9d70 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Fri, 10 May 2019 22:20:18 +0300 Subject: [PATCH 014/103] refactor: put try-except method into main function --- final_task/pycalc.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/final_task/pycalc.py b/final_task/pycalc.py index 64fb71bb..8c342a8a 100644 --- a/final_task/pycalc.py +++ b/final_task/pycalc.py @@ -50,11 +50,11 @@ def arg_parser(): def check_expression(expression_line): if not expression_line: - raise SyntaxError('Expression cannot be empty') + raise SyntaxError('ERROR: Expression cannot be empty') if expression_line[-1] in operator_dict.keys(): raise SyntaxError('ERROR: Extra operator {} at the end of an expression!'.format(expression_line[-1])) if expression_line.count('(') < expression_line.count(')'): - raise SyntaxError('ERROR: Closing bracket required!') + raise SyntaxError('ERROR: Opening bracket required!') elif expression_line.count('(') > expression_line.count(')'): raise SyntaxError('ERROR: Closing bracket required!') return True @@ -314,21 +314,21 @@ def calculate(converted_list): def main(): - expression_line = arg_parser() - operands = OperandStack() - function = OperandStack() - parser = split_operators(expression_line) - converted_list = converter(parser) - result = calculate(converted_list) - print(result) - - -if __name__ == '__main__': try: - main() + expression_line = arg_parser() + operands = OperandStack() + function = OperandStack() + parser = split_operators(expression_line) + converted_list = converter(parser) + result = calculate(converted_list) + print(result) except SyntaxError as err: print(err) except ZeroDivisionError as err: print('ERROR: {}!'.format(err)) except ValueError as err: print('ERROR: math error!') + + +if __name__ == '__main__': + main() From 4587c42a8ede76f5691c2ac01687b122a60fba8d Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Fri, 10 May 2019 22:59:13 +0300 Subject: [PATCH 015/103] feat: add function to raise an exception if expression consist of function without arguments, and update function_parser with check if there is such fucntion in function_dict --- final_task/pycalc.py | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/final_task/pycalc.py b/final_task/pycalc.py index 8c342a8a..2af9c97a 100644 --- a/final_task/pycalc.py +++ b/final_task/pycalc.py @@ -50,13 +50,21 @@ def arg_parser(): def check_expression(expression_line): if not expression_line: - raise SyntaxError('ERROR: Expression cannot be empty') + raise SyntaxError('Expression cannot be empty') if expression_line[-1] in operator_dict.keys(): - raise SyntaxError('ERROR: Extra operator {} at the end of an expression!'.format(expression_line[-1])) + raise SyntaxError('Extra operator {} at the end of an expression!'.format(expression_line[-1])) if expression_line.count('(') < expression_line.count(')'): - raise SyntaxError('ERROR: Opening bracket required!') + raise SyntaxError('Opening bracket required!') elif expression_line.count('(') > expression_line.count(')'): - raise SyntaxError('ERROR: Closing bracket required!') + raise SyntaxError('Closing bracket required!') + return True + + +def check_converted_list(converted_list): + if len(converted_list) == 1: + if type(converted_list[0]) is int or type(converted_list[0]) is float: + return True + raise SyntaxError('Expression must include at list one operand or one function with arguments!') return True @@ -75,7 +83,10 @@ def function_parser(function_name): return function_dict[function_name]['operator'] else: return 2 * function_dict['e']['operator'] - return function_name + elif function_name in function_dict.keys(): + return function_name + else: + raise SyntaxError('There is no function with this name {}!'.format(function_name)) def split_operators(expression_line): @@ -99,7 +110,7 @@ def split_operators(expression_line): last_letter = "" if i.isnumeric() or i is '.': if blank_item and type(parsing_list[-1]) is not str: - raise SyntaxError('Blank symbol between two operands') + raise SyntaxError('Blank symbol between two operands!') elif blank_item: blank_item = False if last_symbol: @@ -109,7 +120,7 @@ def split_operators(expression_line): last_letter += i else: if '.' in last_number and i == '.': - raise SyntaxError('ERROR: Typo in the operand (two comma)') + raise SyntaxError('Typo in the operand (two comma)!') else: last_number += i elif i.isalpha(): @@ -119,7 +130,7 @@ def split_operators(expression_line): last_letter += i elif i in "!=<>/": if blank_item and str(parsing_list[-1]) in '!=<>/*': - raise SyntaxError('ERROR: Blank symbol between twice operator') + raise SyntaxError('Blank symbol between twice operator') elif blank_item: blank_item = False if last_number: @@ -209,6 +220,7 @@ def converter(parsing_list): converted_list.append(operator_dict['-']) last_item = "" converted_list.append(i) + check_converted_list(converted_list) return converted_list @@ -323,7 +335,7 @@ def main(): result = calculate(converted_list) print(result) except SyntaxError as err: - print(err) + print('ERROR: {}'.format(err)) except ZeroDivisionError as err: print('ERROR: {}!'.format(err)) except ValueError as err: From 4fe0ddcf32082c5be561f8925cbcea968236690c Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Fri, 10 May 2019 23:04:26 +0300 Subject: [PATCH 016/103] feat: add exception if there is more than two arguments in the function --- final_task/pycalc.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/final_task/pycalc.py b/final_task/pycalc.py index 2af9c97a..78967128 100644 --- a/final_task/pycalc.py +++ b/final_task/pycalc.py @@ -223,7 +223,6 @@ def converter(parsing_list): check_converted_list(converted_list) return converted_list - class OperandStack: def __init__(self): @@ -300,6 +299,8 @@ def calculate(converted_list): else: for i in range(len(function.stack)): if item is ',' and function.top() is '(': + if func_arguments: + raise SyntaxError('This fucntion can have only two arguments') func_arguments = True break elif func_arguments: From ab26ff732a7e54657b3762d992bc8a8bd1810666 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Sun, 12 May 2019 13:19:46 +0300 Subject: [PATCH 017/103] feat: add expression check if expression start with operator, if function without arguments in the end of expression; fix: raise Error if there is twice operator in the end of expression; refactor: update cheking_convertid_list to checking_parsing_list and call this function before start to convert parsing list --- final_task/pycalc.py | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/final_task/pycalc.py b/final_task/pycalc.py index 78967128..a9cd3cfe 100644 --- a/final_task/pycalc.py +++ b/final_task/pycalc.py @@ -51,8 +51,6 @@ def arg_parser(): def check_expression(expression_line): if not expression_line: raise SyntaxError('Expression cannot be empty') - if expression_line[-1] in operator_dict.keys(): - raise SyntaxError('Extra operator {} at the end of an expression!'.format(expression_line[-1])) if expression_line.count('(') < expression_line.count(')'): raise SyntaxError('Opening bracket required!') elif expression_line.count('(') > expression_line.count(')'): @@ -60,11 +58,21 @@ def check_expression(expression_line): return True -def check_converted_list(converted_list): - if len(converted_list) == 1: - if type(converted_list[0]) is int or type(converted_list[0]) is float: +def check_parsing_list(parsing_list): + if parsing_list[0] in operator_dict.keys(): + if parsing_list[0] is '+' or parsing_list[0] is '-': + pass + else: + raise SyntaxError('Expression cannot start with "{}"'.format(parsing_list[0])) + if len(parsing_list) == 1: + if type(parsing_list[0]) is int or type(parsing_list[0]) is float: return True raise SyntaxError('Expression must include at list one operand or one function with arguments!') + if parsing_list[-1] in operator_dict.keys(): + raise SyntaxError('Extra operator "{}" at the end of an expression!'.format(parsing_list[-1])) + if parsing_list[-1] in function_dict.keys(): + raise SyntaxError('Function "{}" without argument in the end of expression'.format(parsing_list[-1])) + print(parsing_list) return True @@ -98,6 +106,8 @@ def split_operators(expression_line): if check_expression(expression_line): for i in expression_line: if i == " ": + if blank_item: + continue blank_item = True if last_symbol: parsing_list.append(last_symbol) @@ -156,6 +166,8 @@ def split_operators(expression_line): parsing_list.append(number_parser(last_number)) elif last_letter: parsing_list.append(function_parser(last_letter)) + elif last_symbol: + raise SyntaxError('Extra operator "{}" at the end of an expression!'.format(last_symbol)) return parsing_list @@ -171,7 +183,8 @@ def clean_add_sub_operators(last_item, converted_list): def converter(parsing_list): - if parsing_list[0] == "-": + check_parsing_list(parsing_list) + if parsing_list[0] == "-" or parsing_list[0] == "+": converted_list = [0] else: converted_list = [] @@ -220,7 +233,6 @@ def converter(parsing_list): converted_list.append(operator_dict['-']) last_item = "" converted_list.append(i) - check_converted_list(converted_list) return converted_list class OperandStack: From c692c3603bf30026524bc82acd00119ded6b6fea Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Sun, 12 May 2019 13:24:39 +0300 Subject: [PATCH 018/103] fix: remove print method --- final_task/pycalc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/final_task/pycalc.py b/final_task/pycalc.py index a9cd3cfe..95ea3392 100644 --- a/final_task/pycalc.py +++ b/final_task/pycalc.py @@ -72,7 +72,6 @@ def check_parsing_list(parsing_list): raise SyntaxError('Extra operator "{}" at the end of an expression!'.format(parsing_list[-1])) if parsing_list[-1] in function_dict.keys(): raise SyntaxError('Function "{}" without argument in the end of expression'.format(parsing_list[-1])) - print(parsing_list) return True @@ -340,7 +339,8 @@ def calculate(converted_list): def main(): try: - expression_line = arg_parser() + # expression_line = arg_parser() + expression_line = '-13' operands = OperandStack() function = OperandStack() parser = split_operators(expression_line) From 9f2bef99a3cc6a47c4d4e414e359e1b0fc83d008 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Sun, 12 May 2019 13:27:33 +0300 Subject: [PATCH 019/103] fix: remove one line for debug --- final_task/pycalc.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/final_task/pycalc.py b/final_task/pycalc.py index 95ea3392..30d540eb 100644 --- a/final_task/pycalc.py +++ b/final_task/pycalc.py @@ -339,8 +339,7 @@ def calculate(converted_list): def main(): try: - # expression_line = arg_parser() - expression_line = '-13' + expression_line = arg_parser() operands = OperandStack() function = OperandStack() parser = split_operators(expression_line) From 5b20da7250dabb1df7cf3ececf7ad41a140d4349 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Sun, 12 May 2019 13:50:10 +0300 Subject: [PATCH 020/103] fix: add symbol * to check if there is blank symbol betwwen twice operator --- final_task/pycalc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/final_task/pycalc.py b/final_task/pycalc.py index 30d540eb..058634aa 100644 --- a/final_task/pycalc.py +++ b/final_task/pycalc.py @@ -137,7 +137,7 @@ def split_operators(expression_line): parsing_list.append(last_symbol) last_symbol = "" last_letter += i - elif i in "!=<>/": + elif i in "!=<>/*": if blank_item and str(parsing_list[-1]) in '!=<>/*': raise SyntaxError('Blank symbol between twice operator') elif blank_item: From 8cf47b251970f320a5a3a6bd137dc9454bec334d Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Sun, 12 May 2019 13:54:48 +0300 Subject: [PATCH 021/103] refactor: update the max-line-length --- final_task/pycalc.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/final_task/pycalc.py b/final_task/pycalc.py index 058634aa..d0085da2 100644 --- a/final_task/pycalc.py +++ b/final_task/pycalc.py @@ -67,11 +67,11 @@ def check_parsing_list(parsing_list): if len(parsing_list) == 1: if type(parsing_list[0]) is int or type(parsing_list[0]) is float: return True - raise SyntaxError('Expression must include at list one operand or one function with arguments!') + raise SyntaxError('Expression must include at list one operand!') if parsing_list[-1] in operator_dict.keys(): raise SyntaxError('Extra operator "{}" at the end of an expression!'.format(parsing_list[-1])) if parsing_list[-1] in function_dict.keys(): - raise SyntaxError('Function "{}" without argument in the end of expression'.format(parsing_list[-1])) + raise SyntaxError('Function "{}" without argument'.format(parsing_list[-1])) return True @@ -234,6 +234,7 @@ def converter(parsing_list): converted_list.append(i) return converted_list + class OperandStack: def __init__(self): From e35b482df790b133060272752b4ccb029a45f76b Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Mon, 13 May 2019 23:50:51 +0300 Subject: [PATCH 022/103] feat: add operator_parser to check if the math operator is correct; refactor: update check_parsing_list - remove esle statment in first if block; refactor: update type into isinstance; feat: add OverflowError into exseptions --- final_task/pycalc.py | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/final_task/pycalc.py b/final_task/pycalc.py index d0085da2..7812c199 100644 --- a/final_task/pycalc.py +++ b/final_task/pycalc.py @@ -60,9 +60,7 @@ def check_expression(expression_line): def check_parsing_list(parsing_list): if parsing_list[0] in operator_dict.keys(): - if parsing_list[0] is '+' or parsing_list[0] is '-': - pass - else: + if parsing_list[0] is not '+' and parsing_list[0] is not '-': raise SyntaxError('Expression cannot start with "{}"'.format(parsing_list[0])) if len(parsing_list) == 1: if type(parsing_list[0]) is int or type(parsing_list[0]) is float: @@ -82,6 +80,12 @@ def number_parser(number): return float(number) +def operator_parser(operator_symbol): + if operator_symbol in operator_dict.keys(): + return operator_symbol + raise SyntaxError('Typo in math operator!') + + def function_parser(function_name): if function_name == 'e' or function_name == 'pi': return function_dict[function_name]['operator'] @@ -109,7 +113,7 @@ def split_operators(expression_line): continue blank_item = True if last_symbol: - parsing_list.append(last_symbol) + parsing_list.append(operator_parser(last_symbol)) last_symbol = "" elif last_number: parsing_list.append(number_parser(last_number)) @@ -123,7 +127,7 @@ def split_operators(expression_line): elif blank_item: blank_item = False if last_symbol: - parsing_list.append(last_symbol) + parsing_list.append(operator_parser(last_symbol)) last_symbol = "" if last_letter: last_letter += i @@ -134,10 +138,12 @@ def split_operators(expression_line): last_number += i elif i.isalpha(): if last_symbol: - parsing_list.append(last_symbol) + parsing_list.append(operator_parser(last_symbol)) last_symbol = "" last_letter += i elif i in "!=<>/*": + if len(last_symbol) == 2: + raise SyntaxError('Invalid operator "{}"'.format(last_symbol+i)) if blank_item and str(parsing_list[-1]) in '!=<>/*': raise SyntaxError('Blank symbol between twice operator') elif blank_item: @@ -157,7 +163,7 @@ def split_operators(expression_line): parsing_list.append(function_parser(last_letter)) last_letter = "" if last_symbol: - parsing_list.append(last_symbol) + parsing_list.append(operator_parser(last_symbol)) last_symbol = "" if i != ' ': parsing_list.append(i) @@ -166,7 +172,7 @@ def split_operators(expression_line): elif last_letter: parsing_list.append(function_parser(last_letter)) elif last_symbol: - raise SyntaxError('Extra operator "{}" at the end of an expression!'.format(last_symbol)) + raise SyntaxError('Extra operator "{}" at the end of the expression!'.format(last_symbol)) return parsing_list @@ -196,7 +202,7 @@ def converter(parsing_list): if last_item == '+': converted_list.append(operator_dict[last_item]) last_item = '' - if type(i) is float or type(i) is int: + if isinstance(i, float) or isinstance(i, int): if last_item == '-' and converted_list[-1] != '(' \ and converted_list[-1] not in operator_dict.values(): converted_list.append(operator_dict[last_item]) @@ -291,7 +297,7 @@ def calculate(converted_list): function = OperandStack() func_arguments = False for item in converted_list: - if type(item) is float or type(item) is int: + if isinstance(item, float) or isinstance(item, int): operands.put_on_stack(item) elif item in operator_dict.values() or item in function_dict.values(): current_operator = item @@ -352,7 +358,9 @@ def main(): except ZeroDivisionError as err: print('ERROR: {}!'.format(err)) except ValueError as err: - print('ERROR: math error!') + print('ERROR: {}!'.format(err)) + except OverflowError as err: + print('ERROR: {}!'.format(err)) if __name__ == '__main__': From 953073edc594a65847ac6b11948d8b70bf651fcf Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Mon, 13 May 2019 23:50:51 +0300 Subject: [PATCH 023/103] feat: add operator_parser to check if the math operator is correct; refactor: update check_parsing_list - remove esle statment in first if block; refactor: update type into isinstance; feat: add OverflowError into exseptions --- final_task/pycalc.py | 57 ++++++++++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/final_task/pycalc.py b/final_task/pycalc.py index d0085da2..e3d8325d 100644 --- a/final_task/pycalc.py +++ b/final_task/pycalc.py @@ -39,6 +39,7 @@ def arg_parser(): '%': {'operator': operator.mod, 'priority': 2}, '//': {'operator': operator.floordiv, 'priority': 2}, '^': {'operator': operator.pow, 'priority': 1}, + '**': {'operator': operator.pow, 'priority': 1}, '==': {'operator': operator.eq, 'priority': 4}, '!=': {'operator': operator.ne, 'priority': 4}, '>': {'operator': operator.gt, 'priority': 4}, @@ -60,9 +61,7 @@ def check_expression(expression_line): def check_parsing_list(parsing_list): if parsing_list[0] in operator_dict.keys(): - if parsing_list[0] is '+' or parsing_list[0] is '-': - pass - else: + if parsing_list[0] is not '+' and parsing_list[0] is not '-': raise SyntaxError('Expression cannot start with "{}"'.format(parsing_list[0])) if len(parsing_list) == 1: if type(parsing_list[0]) is int or type(parsing_list[0]) is float: @@ -82,6 +81,12 @@ def number_parser(number): return float(number) +def operator_parser(operator_symbol): + if operator_symbol in operator_dict.keys(): + return operator_symbol + raise SyntaxError('Typo in math operator!') + + def function_parser(function_name): if function_name == 'e' or function_name == 'pi': return function_dict[function_name]['operator'] @@ -109,7 +114,7 @@ def split_operators(expression_line): continue blank_item = True if last_symbol: - parsing_list.append(last_symbol) + parsing_list.append(operator_parser(last_symbol)) last_symbol = "" elif last_number: parsing_list.append(number_parser(last_number)) @@ -123,7 +128,7 @@ def split_operators(expression_line): elif blank_item: blank_item = False if last_symbol: - parsing_list.append(last_symbol) + parsing_list.append(operator_parser(last_symbol)) last_symbol = "" if last_letter: last_letter += i @@ -134,10 +139,12 @@ def split_operators(expression_line): last_number += i elif i.isalpha(): if last_symbol: - parsing_list.append(last_symbol) + parsing_list.append(operator_parser(last_symbol)) last_symbol = "" last_letter += i elif i in "!=<>/*": + if len(last_symbol) == 2: + raise SyntaxError('Invalid operator "{}"'.format(last_symbol+i)) if blank_item and str(parsing_list[-1]) in '!=<>/*': raise SyntaxError('Blank symbol between twice operator') elif blank_item: @@ -157,7 +164,7 @@ def split_operators(expression_line): parsing_list.append(function_parser(last_letter)) last_letter = "" if last_symbol: - parsing_list.append(last_symbol) + parsing_list.append(operator_parser(last_symbol)) last_symbol = "" if i != ' ': parsing_list.append(i) @@ -166,7 +173,7 @@ def split_operators(expression_line): elif last_letter: parsing_list.append(function_parser(last_letter)) elif last_symbol: - raise SyntaxError('Extra operator "{}" at the end of an expression!'.format(last_symbol)) + raise SyntaxError('Extra operator "{}" at the end of the expression!'.format(last_symbol)) return parsing_list @@ -194,24 +201,36 @@ def converter(parsing_list): if i != '-' and i != '+' and last_item: last_item = clean_add_sub_operators(last_item, converted_list) if last_item == '+': - converted_list.append(operator_dict[last_item]) - last_item = '' - if type(i) is float or type(i) is int: + try: + if converted_list[-1]['operator']: + raise SyntaxError('Missing operand between two math operators!') + except TypeError: + converted_list.append(operator_dict[last_item]) + last_item = '' + if isinstance(i, float) or isinstance(i, int): if last_item == '-' and converted_list[-1] != '(' \ and converted_list[-1] not in operator_dict.values(): converted_list.append(operator_dict[last_item]) converted_list.append(i) last_item = "" elif last_item == '-': - converted_list.append(-i) - last_item = "" + try: + if converted_list[-1]['operator']: + raise SyntaxError('Missing operand between two math operators!') + except TypeError: + converted_list.append(operator_dict[-i]) + last_item = "" else: converted_list.append(i) elif i in operator_dict.keys(): if i == '-' or i == '+': last_item += i else: - converted_list.append(operator_dict[i]) + try: + if converted_list[-1]['operator']: + raise SyntaxError('Missing operand between two math operators!') + except TypeError: + converted_list.append(operator_dict[i]) elif i in function_dict.keys(): if last_item: if last_item == '-' and converted_list[-1] != '(': @@ -291,7 +310,7 @@ def calculate(converted_list): function = OperandStack() func_arguments = False for item in converted_list: - if type(item) is float or type(item) is int: + if isinstance(item, float) or isinstance(item, int): operands.put_on_stack(item) elif item in operator_dict.values() or item in function_dict.values(): current_operator = item @@ -312,7 +331,7 @@ def calculate(converted_list): for i in range(len(function.stack)): if item is ',' and function.top() is '(': if func_arguments: - raise SyntaxError('This fucntion can have only two arguments') + raise SyntaxError('This function can have only two arguments') func_arguments = True break elif func_arguments: @@ -340,7 +359,7 @@ def calculate(converted_list): def main(): try: - expression_line = arg_parser() + expression_line = '2**-3'#arg_parser() operands = OperandStack() function = OperandStack() parser = split_operators(expression_line) @@ -352,7 +371,9 @@ def main(): except ZeroDivisionError as err: print('ERROR: {}!'.format(err)) except ValueError as err: - print('ERROR: math error!') + print('ERROR: {}!'.format(err)) + except OverflowError as err: + print('ERROR: {}!'.format(err)) if __name__ == '__main__': From 94124bb28cea24b6ba6651ef087e3bdc6e490d02 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Tue, 14 May 2019 00:33:36 +0300 Subject: [PATCH 024/103] feat: update operator_dict with ** operator; add checking if there are two consecutive operators --- final_task/pycalc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/final_task/pycalc.py b/final_task/pycalc.py index e3d8325d..a7926939 100644 --- a/final_task/pycalc.py +++ b/final_task/pycalc.py @@ -359,7 +359,7 @@ def calculate(converted_list): def main(): try: - expression_line = '2**-3'#arg_parser() + expression_line = arg_parser() operands = OperandStack() function = OperandStack() parser = split_operators(expression_line) From 951cfe37b22954f94bbc1b047ee63d97fff09712 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Tue, 14 May 2019 00:57:39 +0300 Subject: [PATCH 025/103] fix: remove extra lines in converter function --- final_task/pycalc.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/final_task/pycalc.py b/final_task/pycalc.py index 56481aaf..a7926939 100644 --- a/final_task/pycalc.py +++ b/final_task/pycalc.py @@ -207,8 +207,6 @@ def converter(parsing_list): except TypeError: converted_list.append(operator_dict[last_item]) last_item = '' - converted_list.append(operator_dict[last_item]) - last_item = '' if isinstance(i, float) or isinstance(i, int): if last_item == '-' and converted_list[-1] != '(' \ and converted_list[-1] not in operator_dict.values(): From 486cab91cda997f7ca1a9892b59f15af19ac4a73 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Tue, 14 May 2019 01:14:58 +0300 Subject: [PATCH 026/103] fix: remove raise the exception when last of two consecutive operators is minus and operand fater minus --- final_task/pycalc.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/final_task/pycalc.py b/final_task/pycalc.py index a7926939..3b631ff4 100644 --- a/final_task/pycalc.py +++ b/final_task/pycalc.py @@ -214,12 +214,8 @@ def converter(parsing_list): converted_list.append(i) last_item = "" elif last_item == '-': - try: - if converted_list[-1]['operator']: - raise SyntaxError('Missing operand between two math operators!') - except TypeError: - converted_list.append(operator_dict[-i]) - last_item = "" + converted_list.append(-i) + last_item = "" else: converted_list.append(i) elif i in operator_dict.keys(): From c1781300fcd793537deb88a4b4f12464cd5e61e0 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Tue, 14 May 2019 01:21:00 +0300 Subject: [PATCH 027/103] remove raise the exception when last of two consecutive operators is plus --- final_task/pycalc.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/final_task/pycalc.py b/final_task/pycalc.py index 3b631ff4..429a9a23 100644 --- a/final_task/pycalc.py +++ b/final_task/pycalc.py @@ -201,12 +201,8 @@ def converter(parsing_list): if i != '-' and i != '+' and last_item: last_item = clean_add_sub_operators(last_item, converted_list) if last_item == '+': - try: - if converted_list[-1]['operator']: - raise SyntaxError('Missing operand between two math operators!') - except TypeError: - converted_list.append(operator_dict[last_item]) - last_item = '' + converted_list.append(operator_dict[last_item]) + last_item = '' if isinstance(i, float) or isinstance(i, int): if last_item == '-' and converted_list[-1] != '(' \ and converted_list[-1] not in operator_dict.values(): From bafd158a9c105f74a1b5026de4c5d169fd013105 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Tue, 14 May 2019 23:23:53 +0300 Subject: [PATCH 028/103] feat: add unary_dict with inary operator "-", update priority to all operator in operator_dict, udpate functions converter, calculate, calc_on_stak with unary_dict --- final_task/pycalc.py | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/final_task/pycalc.py b/final_task/pycalc.py index 429a9a23..8aea694f 100644 --- a/final_task/pycalc.py +++ b/final_task/pycalc.py @@ -32,20 +32,24 @@ def arg_parser(): operator_dict = { - '+': {'operator': operator.add, 'priority': 3}, - '-': {'operator': operator.sub, 'priority': 3}, - '/': {'operator': operator.truediv, 'priority': 2}, - '*': {'operator': operator.mul, 'priority': 2}, - '%': {'operator': operator.mod, 'priority': 2}, - '//': {'operator': operator.floordiv, 'priority': 2}, + '+': {'operator': operator.add, 'priority': 4}, + '-': {'operator': operator.sub, 'priority': 4}, + '/': {'operator': operator.truediv, 'priority': 3}, + '*': {'operator': operator.mul, 'priority': 3}, + '%': {'operator': operator.mod, 'priority': 3}, + '//': {'operator': operator.floordiv, 'priority': 3}, '^': {'operator': operator.pow, 'priority': 1}, '**': {'operator': operator.pow, 'priority': 1}, - '==': {'operator': operator.eq, 'priority': 4}, - '!=': {'operator': operator.ne, 'priority': 4}, - '>': {'operator': operator.gt, 'priority': 4}, - '<': {'operator': operator.lt, 'priority': 4}, - '>=': {'operator': operator.ge, 'priority': 4}, - '<=': {'operator': operator.le, 'priority': 4}, + '==': {'operator': operator.eq, 'priority': 5}, + '!=': {'operator': operator.ne, 'priority': 5}, + '>': {'operator': operator.gt, 'priority': 5}, + '<': {'operator': operator.lt, 'priority': 5}, + '>=': {'operator': operator.ge, 'priority': 5}, + '<=': {'operator': operator.le, 'priority': 5}, +} + +unary_dict = { + '-': {'operator': operator.sub, 'priority': 2} } @@ -206,7 +210,7 @@ def converter(parsing_list): if isinstance(i, float) or isinstance(i, int): if last_item == '-' and converted_list[-1] != '(' \ and converted_list[-1] not in operator_dict.values(): - converted_list.append(operator_dict[last_item]) + converted_list.append(unary_dict[last_item]) converted_list.append(i) last_item = "" elif last_item == '-': @@ -279,7 +283,7 @@ def calc_on_stack(): else: first_operand = operands.take_from_stack() current_result = operator_on_stack['operator'](first_operand) - elif operator_on_stack in operator_dict.values(): + elif operator_on_stack in operator_dict.values() or operator_on_stack in unary_dict.values(): if len(operands.stack) == 1: second_operand = operands.take_from_stack() first_operand = 0 @@ -304,7 +308,7 @@ def calculate(converted_list): for item in converted_list: if isinstance(item, float) or isinstance(item, int): operands.put_on_stack(item) - elif item in operator_dict.values() or item in function_dict.values(): + elif item in operator_dict.values() or item in function_dict.values() or item in unary_dict.values(): current_operator = item if function.is_empty(): function.put_on_stack(current_operator) From 031417becae07432b93d444d88fc02c59aa5dc95 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Tue, 14 May 2019 23:56:37 +0300 Subject: [PATCH 029/103] fix: update if statment for sub operation in converter; feat: add extra check into pycalc_checker for operation_priority and error_cases --- final_task/pycalc.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/final_task/pycalc.py b/final_task/pycalc.py index 8aea694f..c1b98d9f 100644 --- a/final_task/pycalc.py +++ b/final_task/pycalc.py @@ -208,14 +208,19 @@ def converter(parsing_list): converted_list.append(operator_dict[last_item]) last_item = '' if isinstance(i, float) or isinstance(i, int): - if last_item == '-' and converted_list[-1] != '(' \ - and converted_list[-1] not in operator_dict.values(): - converted_list.append(unary_dict[last_item]) - converted_list.append(i) - last_item = "" - elif last_item == '-': - converted_list.append(-i) - last_item = "" + if last_item == '-': + if converted_list[-1] == 0: + converted_list.append(unary_dict[last_item]) + converted_list.append(i) + last_item = "" + elif last_item == '-' and converted_list[-1] != '(' \ + and converted_list[-1] not in operator_dict.values(): + converted_list.append(operator_dict[last_item]) + converted_list.append(i) + last_item = "" + else:# last_item == '-': + converted_list.append(-i) + last_item = "" else: converted_list.append(i) elif i in operator_dict.keys(): From 367eb4425707c568b0e940834d9ca2df72e93ccf Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 15 May 2019 00:02:55 +0300 Subject: [PATCH 030/103] style: remove long line --- final_task/pycalc.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/final_task/pycalc.py b/final_task/pycalc.py index c1b98d9f..e80d547a 100644 --- a/final_task/pycalc.py +++ b/final_task/pycalc.py @@ -218,7 +218,7 @@ def converter(parsing_list): converted_list.append(operator_dict[last_item]) converted_list.append(i) last_item = "" - else:# last_item == '-': + else: converted_list.append(-i) last_item = "" else: @@ -313,7 +313,9 @@ def calculate(converted_list): for item in converted_list: if isinstance(item, float) or isinstance(item, int): operands.put_on_stack(item) - elif item in operator_dict.values() or item in function_dict.values() or item in unary_dict.values(): + elif item in operator_dict.values() \ + or item in function_dict.values() \ + or item in unary_dict.values(): current_operator = item if function.is_empty(): function.put_on_stack(current_operator) From ea57e2170d6f2771b2f03689192df1a9815bf6ae Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 15 May 2019 00:05:03 +0300 Subject: [PATCH 031/103] feat: add test case for operation_priority and error_cases --- pycalc_checker.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pycalc_checker.py b/pycalc_checker.py index aa67c85b..c1696c84 100644 --- a/pycalc_checker.py +++ b/pycalc_checker.py @@ -23,6 +23,7 @@ "10^(2+1)": 10**(2+1), "100/3^2": 100/3**2, "100/3%2^2": 100/3%2**2, + "-15//2": -15//2, } @@ -88,6 +89,9 @@ "(((((", "abs", "pow(2, 3, 4)", + "2+/3", + "/2", + "2***2", ] From c1778edb1701fd3d96fd17cbbac7440fa8d65f1b Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 15 May 2019 21:47:43 +0300 Subject: [PATCH 032/103] feat: extract function split_operators into class SplitOperator, extract function check_expression into module, extract math operators and math functions into modele --- final_task/check_manager.py | 11 +++ final_task/expression_parser.py | 103 ++++++++++++++++++++++ final_task/operator_manager.py | 34 +++++++ final_task/pycalc.py | 151 +------------------------------- final_task/setup.py | 2 +- 5 files changed, 153 insertions(+), 148 deletions(-) create mode 100644 final_task/check_manager.py create mode 100644 final_task/expression_parser.py create mode 100644 final_task/operator_manager.py diff --git a/final_task/check_manager.py b/final_task/check_manager.py new file mode 100644 index 00000000..25cd0c6b --- /dev/null +++ b/final_task/check_manager.py @@ -0,0 +1,11 @@ +from . import operator_manager + + +def check_expression(expression_line): + if not expression_line: + raise SyntaxError('Expression cannot be empty') + if expression_line.count('(') < expression_line.count(')'): + raise SyntaxError('Opening bracket required!') + elif expression_line.count('(') > expression_line.count(')'): + raise SyntaxError('Closing bracket required!') + return True \ No newline at end of file diff --git a/final_task/expression_parser.py b/final_task/expression_parser.py new file mode 100644 index 00000000..2f5e17b3 --- /dev/null +++ b/final_task/expression_parser.py @@ -0,0 +1,103 @@ +import sys +from . import operator_manager + + +def check_expression(expression_line): + if not expression_line: + raise SyntaxError('Expression cannot be empty') + if expression_line.count('(') < expression_line.count(')'): + raise SyntaxError('Opening bracket required!') + elif expression_line.count('(') > expression_line.count(')'): + raise SyntaxError('Closing bracket required!') + return True + + +class SplitOperators: + + def __init__(self): + self.parsing_list = [] + self.last_number = "" + self.last_letter = "" + self.last_symbol = "" + self.blank_item = False + + def operartor_parser(self, operator_symbol): + if operator_symbol in operator_dict.keys(): + return operator_symbol + raise SyntaxError('Typo in math operator!') + + def number_parser(self, number): + try: + return int(number) + except ValueError: + return float(number) + + def function_parser(self, function_name): + if function_name == 'e' or function_name == 'pi': + return function_dict[function_name]['operator'] + elif function_name == 'tau': + if sys.version_info >= (3, 6): + return function_dict[function_name]['operator'] + else: + return 2 * function_dict['e']['operator'] + elif function_name in function_dict.keys(): + return function_name + else: + raise SyntaxError('There is no function with this name {}!'.format(function_name)) + + def append_to_parsing_list(self): + if self.last_symbol: + self.parsing_list.append(self.operartor_parser(self.last_symbol)) + self.last_symbol = "" + elif self.last_number: + self.parsing_list.append(self.number_parser(self.last_number)) + self.last_number = "" + elif self.last_letter: + self.parsing_list.append(self.function_parser(self.last_letter)) + self.last_letter = "" + + def split_operators(self, expression_line): + if check_expression(expression_line): + for i in expression_line: + if i == " ": + self.blank_item = True + self.append_to_parsing_list() + if i.isnumeric() or i == '.': + if self.blank_item and not isinstance(self.parsing_list[-1], str): + raise SyntaxError('Blank symbol between two operands!') + elif self.blank_item: + self.blank_item = False + if self.last_symbol: + self.append_to_parsing_list() + if self.last_letter: + self.last_letter += i + else: + if '.' in self.last_number and i == '.': + raise SyntaxError('Typo in the operand (two comma)!') + else: + self.last_number += i + elif i.isalpha(): + if self.last_symbol: + self.append_to_parsing_list() + self.last_letter += i + elif i in "!=<>/*": + if len(self.last_symbol) == 2: + raise SyntaxError('Invalid operator "{}"'.format(self.last_symbol + i)) + if self.blank_item and str(self.parsing_list[-1]) in "!=<>/*": + raise SyntaxError('Blank symbol between twice operator') + elif self.blank_item: + self.blank_item = False + self.append_to_parsing_list() + self.last_symbol += i + else: + self.append_to_parsing_list() + if i != ' ': + self.parsing_list.append(i) + if self.last_symbol: + raise SyntaxError('Extra operator "{}" at the end of the expression!'.format(self.last_symbol)) + self.append_to_parsing_list() + return self.parsing_list + + +test_ex = Token() +print(test_ex.split_operators('sin(e^log(e^e^sin(23.0),45.0) + cos(3.0+log10(e^-e)))')) diff --git a/final_task/operator_manager.py b/final_task/operator_manager.py new file mode 100644 index 00000000..f3a69aad --- /dev/null +++ b/final_task/operator_manager.py @@ -0,0 +1,34 @@ +import math +import operator + + +function_dict = { + 'abs': {'operator': abs, 'priority': 0}, + 'round': {'operator': round, 'priority': 0} +} +for k, v in math.__dict__.items(): + if k.startswith('_'): + continue + function_dict[k] = {'operator': v, 'priority': 0} + + +operator_dict = { + '+': {'operator': operator.add, 'priority': 4}, + '-': {'operator': operator.sub, 'priority': 4}, + '/': {'operator': operator.truediv, 'priority': 3}, + '*': {'operator': operator.mul, 'priority': 3}, + '%': {'operator': operator.mod, 'priority': 3}, + '//': {'operator': operator.floordiv, 'priority': 3}, + '^': {'operator': operator.pow, 'priority': 1}, + '**': {'operator': operator.pow, 'priority': 1}, + '==': {'operator': operator.eq, 'priority': 5}, + '!=': {'operator': operator.ne, 'priority': 5}, + '>': {'operator': operator.gt, 'priority': 5}, + '<': {'operator': operator.lt, 'priority': 5}, + '>=': {'operator': operator.ge, 'priority': 5}, + '<=': {'operator': operator.le, 'priority': 5}, +} + +unary_dict = { + '-': {'operator': operator.sub, 'priority': 2} +} \ No newline at end of file diff --git a/final_task/pycalc.py b/final_task/pycalc.py index e80d547a..a3e7ee1c 100644 --- a/final_task/pycalc.py +++ b/final_task/pycalc.py @@ -4,10 +4,11 @@ import math import sys import argparse +from . import expression_parser, operator_manager, check_manager def arg_parser(): - parser = argparse.ArgumentParser(description='Pure-python command-line calculator.', prog='pycalc') + parser = argparse.ArgumentParser(description='Pure-python command-line calculator.', prog='pycalc_not_my') parser.add_argument( '-m', '--use-modules', @@ -21,48 +22,6 @@ def arg_parser(): return expression_line -function_dict = { - 'abs': {'operator': abs, 'priority': 0}, - 'round': {'operator': round, 'priority': 0} -} -for k, v in math.__dict__.items(): - if k.startswith('_'): - continue - function_dict[k] = {'operator': v, 'priority': 0} - - -operator_dict = { - '+': {'operator': operator.add, 'priority': 4}, - '-': {'operator': operator.sub, 'priority': 4}, - '/': {'operator': operator.truediv, 'priority': 3}, - '*': {'operator': operator.mul, 'priority': 3}, - '%': {'operator': operator.mod, 'priority': 3}, - '//': {'operator': operator.floordiv, 'priority': 3}, - '^': {'operator': operator.pow, 'priority': 1}, - '**': {'operator': operator.pow, 'priority': 1}, - '==': {'operator': operator.eq, 'priority': 5}, - '!=': {'operator': operator.ne, 'priority': 5}, - '>': {'operator': operator.gt, 'priority': 5}, - '<': {'operator': operator.lt, 'priority': 5}, - '>=': {'operator': operator.ge, 'priority': 5}, - '<=': {'operator': operator.le, 'priority': 5}, -} - -unary_dict = { - '-': {'operator': operator.sub, 'priority': 2} -} - - -def check_expression(expression_line): - if not expression_line: - raise SyntaxError('Expression cannot be empty') - if expression_line.count('(') < expression_line.count(')'): - raise SyntaxError('Opening bracket required!') - elif expression_line.count('(') > expression_line.count(')'): - raise SyntaxError('Closing bracket required!') - return True - - def check_parsing_list(parsing_list): if parsing_list[0] in operator_dict.keys(): if parsing_list[0] is not '+' and parsing_list[0] is not '-': @@ -78,109 +37,6 @@ def check_parsing_list(parsing_list): return True -def number_parser(number): - try: - return int(number) - except ValueError: - return float(number) - - -def operator_parser(operator_symbol): - if operator_symbol in operator_dict.keys(): - return operator_symbol - raise SyntaxError('Typo in math operator!') - - -def function_parser(function_name): - if function_name == 'e' or function_name == 'pi': - return function_dict[function_name]['operator'] - elif function_name == 'tau': - if sys.version_info >= (3, 6): - return function_dict[function_name]['operator'] - else: - return 2 * function_dict['e']['operator'] - elif function_name in function_dict.keys(): - return function_name - else: - raise SyntaxError('There is no function with this name {}!'.format(function_name)) - - -def split_operators(expression_line): - parsing_list = [] - last_number = "" - last_letter = "" - last_symbol = "" - blank_item = False - if check_expression(expression_line): - for i in expression_line: - if i == " ": - if blank_item: - continue - blank_item = True - if last_symbol: - parsing_list.append(operator_parser(last_symbol)) - last_symbol = "" - elif last_number: - parsing_list.append(number_parser(last_number)) - last_number = "" - elif last_letter: - parsing_list.append(function_parser(last_letter)) - last_letter = "" - if i.isnumeric() or i is '.': - if blank_item and type(parsing_list[-1]) is not str: - raise SyntaxError('Blank symbol between two operands!') - elif blank_item: - blank_item = False - if last_symbol: - parsing_list.append(operator_parser(last_symbol)) - last_symbol = "" - if last_letter: - last_letter += i - else: - if '.' in last_number and i == '.': - raise SyntaxError('Typo in the operand (two comma)!') - else: - last_number += i - elif i.isalpha(): - if last_symbol: - parsing_list.append(operator_parser(last_symbol)) - last_symbol = "" - last_letter += i - elif i in "!=<>/*": - if len(last_symbol) == 2: - raise SyntaxError('Invalid operator "{}"'.format(last_symbol+i)) - if blank_item and str(parsing_list[-1]) in '!=<>/*': - raise SyntaxError('Blank symbol between twice operator') - elif blank_item: - blank_item = False - if last_number: - parsing_list.append(number_parser(last_number)) - last_number = "" - if last_letter: - parsing_list.append(function_parser(last_letter)) - last_letter = "" - last_symbol += i - else: - if last_number: - parsing_list.append(number_parser(last_number)) - last_number = "" - if last_letter: - parsing_list.append(function_parser(last_letter)) - last_letter = "" - if last_symbol: - parsing_list.append(operator_parser(last_symbol)) - last_symbol = "" - if i != ' ': - parsing_list.append(i) - if last_number: - parsing_list.append(number_parser(last_number)) - elif last_letter: - parsing_list.append(function_parser(last_letter)) - elif last_symbol: - raise SyntaxError('Extra operator "{}" at the end of the expression!'.format(last_symbol)) - return parsing_list - - def clean_add_sub_operators(last_item, converted_list): if last_item.count('-') % 2 == 0: if converted_list[-1] == '(': @@ -365,7 +221,8 @@ def main(): expression_line = arg_parser() operands = OperandStack() function = OperandStack() - parser = split_operators(expression_line) + parser = SplitOperators().split_operators + clear_parser = check_expression(parser) converted_list = converter(parser) result = calculate(converted_list) print(result) diff --git a/final_task/setup.py b/final_task/setup.py index 6673483b..0944e16c 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -1,7 +1,7 @@ from setuptools import setup, find_packages setup( - name='pycalc', + name='pycalc_not_my', version='1.0', author='Mikhail Sauchuk', author_email='mishasavchuk@gmail.com', From 07e2addb77cd8d59d9a639e94045c9027e52c399 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 15 May 2019 21:55:45 +0300 Subject: [PATCH 033/103] fix: resolve typo in package name --- final_task/check_manager.py | 2 +- final_task/expression_parser.py | 8 +++----- final_task/operator_manager.py | 2 +- final_task/pycalc.py | 2 +- final_task/setup.py | 2 +- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/final_task/check_manager.py b/final_task/check_manager.py index 25cd0c6b..eb130c77 100644 --- a/final_task/check_manager.py +++ b/final_task/check_manager.py @@ -8,4 +8,4 @@ def check_expression(expression_line): raise SyntaxError('Opening bracket required!') elif expression_line.count('(') > expression_line.count(')'): raise SyntaxError('Closing bracket required!') - return True \ No newline at end of file + return True diff --git a/final_task/expression_parser.py b/final_task/expression_parser.py index 2f5e17b3..72e3181a 100644 --- a/final_task/expression_parser.py +++ b/final_task/expression_parser.py @@ -94,10 +94,8 @@ def split_operators(self, expression_line): if i != ' ': self.parsing_list.append(i) if self.last_symbol: - raise SyntaxError('Extra operator "{}" at the end of the expression!'.format(self.last_symbol)) + raise SyntaxError( + 'Extra operator "{}" at the end of the expression!'.format(self.last_symbol) + ) self.append_to_parsing_list() return self.parsing_list - - -test_ex = Token() -print(test_ex.split_operators('sin(e^log(e^e^sin(23.0),45.0) + cos(3.0+log10(e^-e)))')) diff --git a/final_task/operator_manager.py b/final_task/operator_manager.py index f3a69aad..83ab20fb 100644 --- a/final_task/operator_manager.py +++ b/final_task/operator_manager.py @@ -31,4 +31,4 @@ unary_dict = { '-': {'operator': operator.sub, 'priority': 2} -} \ No newline at end of file +} diff --git a/final_task/pycalc.py b/final_task/pycalc.py index a3e7ee1c..e83f2bfc 100644 --- a/final_task/pycalc.py +++ b/final_task/pycalc.py @@ -8,7 +8,7 @@ def arg_parser(): - parser = argparse.ArgumentParser(description='Pure-python command-line calculator.', prog='pycalc_not_my') + parser = argparse.ArgumentParser(description='Pure-python command-line calculator.', prog='pycalc') parser.add_argument( '-m', '--use-modules', diff --git a/final_task/setup.py b/final_task/setup.py index 0944e16c..6673483b 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -1,7 +1,7 @@ from setuptools import setup, find_packages setup( - name='pycalc_not_my', + name='pycalc', version='1.0', author='Mikhail Sauchuk', author_email='mishasavchuk@gmail.com', From a1f94cd174b61a6c988e05e40a3cab112a87910a Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 15 May 2019 22:23:17 +0300 Subject: [PATCH 034/103] fix: in main function add expressin_line to call in split_operators --- final_task/pycalc.py | 2 +- final_task/setup.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/final_task/pycalc.py b/final_task/pycalc.py index e83f2bfc..547742d5 100644 --- a/final_task/pycalc.py +++ b/final_task/pycalc.py @@ -221,7 +221,7 @@ def main(): expression_line = arg_parser() operands = OperandStack() function = OperandStack() - parser = SplitOperators().split_operators + parser = SplitOperators().split_operators(expression_line) clear_parser = check_expression(parser) converted_list = converter(parser) result = calculate(converted_list) diff --git a/final_task/setup.py b/final_task/setup.py index 6673483b..c6f95de1 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -11,3 +11,6 @@ platforms='any', py_modules=['pycalc'] ) + + + From b61dba086943e07f66f1eebdc2de7aaa6663f3de Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 15 May 2019 22:28:46 +0300 Subject: [PATCH 035/103] fix: update import my modules --- final_task/pycalc.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/final_task/pycalc.py b/final_task/pycalc.py index 547742d5..020c2e57 100644 --- a/final_task/pycalc.py +++ b/final_task/pycalc.py @@ -4,7 +4,9 @@ import math import sys import argparse -from . import expression_parser, operator_manager, check_manager +from expression_parser import * +from operator_manager import * +from check_manager import * def arg_parser(): From 6fe197a404c52e32644d32e273b93de403b0f88d Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 15 May 2019 22:33:16 +0300 Subject: [PATCH 036/103] fix: update import my modules --- final_task/pycalc.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/final_task/pycalc.py b/final_task/pycalc.py index 020c2e57..a261931e 100644 --- a/final_task/pycalc.py +++ b/final_task/pycalc.py @@ -4,9 +4,9 @@ import math import sys import argparse -from expression_parser import * -from operator_manager import * -from check_manager import * +from .expression_parser import * +from .operator_manager import * +from .check_manager import * def arg_parser(): From fdb1b6312808120eb94fce7ef18d6ea12cf3971e Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 15 May 2019 22:45:50 +0300 Subject: [PATCH 037/103] feat: create packege pycalc and put there calc modules --- final_task/pycalc/__init__.py | 0 final_task/{ => pycalc}/check_manager.py | 0 final_task/{ => pycalc}/expression_parser.py | 0 final_task/{ => pycalc}/operator_manager.py | 0 final_task/{ => pycalc}/pycalc.py | 0 5 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 final_task/pycalc/__init__.py rename final_task/{ => pycalc}/check_manager.py (100%) rename final_task/{ => pycalc}/expression_parser.py (100%) rename final_task/{ => pycalc}/operator_manager.py (100%) rename final_task/{ => pycalc}/pycalc.py (100%) diff --git a/final_task/pycalc/__init__.py b/final_task/pycalc/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/final_task/check_manager.py b/final_task/pycalc/check_manager.py similarity index 100% rename from final_task/check_manager.py rename to final_task/pycalc/check_manager.py diff --git a/final_task/expression_parser.py b/final_task/pycalc/expression_parser.py similarity index 100% rename from final_task/expression_parser.py rename to final_task/pycalc/expression_parser.py diff --git a/final_task/operator_manager.py b/final_task/pycalc/operator_manager.py similarity index 100% rename from final_task/operator_manager.py rename to final_task/pycalc/operator_manager.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 From bb7f75de56ac78e5cdf48be31bae1bce8212783c Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 15 May 2019 22:51:19 +0300 Subject: [PATCH 038/103] fix: add package pycalc in entry_points --- final_task/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/final_task/setup.py b/final_task/setup.py index c6f95de1..33003e41 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -6,7 +6,7 @@ author='Mikhail Sauchuk', author_email='mishasavchuk@gmail.com', packages=find_packages(), - entry_points={'console_scripts': ['pycalc = pycalc:main']}, + entry_points={'console_scripts': ['pycalc = pycalc.pycalc:main']}, description='Pure-python command-line calculator.', platforms='any', py_modules=['pycalc'] From 3142b89ef0d155be899002b3c17dcac6777a551c Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 15 May 2019 22:58:32 +0300 Subject: [PATCH 039/103] fix: update import in expression_parser.py, update main function with clear_parser --- final_task/pycalc/expression_parser.py | 6 ++++-- final_task/pycalc/pycalc.py | 2 +- final_task/setup.py | 1 + 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/final_task/pycalc/expression_parser.py b/final_task/pycalc/expression_parser.py index 72e3181a..2903bb73 100644 --- a/final_task/pycalc/expression_parser.py +++ b/final_task/pycalc/expression_parser.py @@ -1,5 +1,5 @@ import sys -from . import operator_manager +from .operator_manager import * def check_expression(expression_line): @@ -43,7 +43,9 @@ def function_parser(self, function_name): elif function_name in function_dict.keys(): return function_name else: - raise SyntaxError('There is no function with this name {}!'.format(function_name)) + raise SyntaxError( + 'There is no function with this name {}!'.format(function_name) + ) def append_to_parsing_list(self): if self.last_symbol: diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index a261931e..c8d033de 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -225,7 +225,7 @@ def main(): function = OperandStack() parser = SplitOperators().split_operators(expression_line) clear_parser = check_expression(parser) - converted_list = converter(parser) + converted_list = converter(clear_parser) result = calculate(converted_list) print(result) except SyntaxError as err: diff --git a/final_task/setup.py b/final_task/setup.py index 33003e41..8ae60ed9 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -14,3 +14,4 @@ + From 96853489821a02090a2fa6cf4f98fcc3fbeaf6b5 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 15 May 2019 23:15:31 +0300 Subject: [PATCH 040/103] fix: now check_manager return list --- final_task/pycalc/check_manager.py | 2 +- final_task/setup.py | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/final_task/pycalc/check_manager.py b/final_task/pycalc/check_manager.py index eb130c77..affbf582 100644 --- a/final_task/pycalc/check_manager.py +++ b/final_task/pycalc/check_manager.py @@ -8,4 +8,4 @@ def check_expression(expression_line): raise SyntaxError('Opening bracket required!') elif expression_line.count('(') > expression_line.count(')'): raise SyntaxError('Closing bracket required!') - return True + return expression_line diff --git a/final_task/setup.py b/final_task/setup.py index 8ae60ed9..0449ea78 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -11,7 +11,3 @@ platforms='any', py_modules=['pycalc'] ) - - - - From 4f3a030563c08cad45d02b6cf045b80a4f9ed021 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 15 May 2019 23:28:57 +0300 Subject: [PATCH 041/103] fix: add if statmet to check if theres a duble math operator --- final_task/pycalc/expression_parser.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/final_task/pycalc/expression_parser.py b/final_task/pycalc/expression_parser.py index 2903bb73..b28e07f5 100644 --- a/final_task/pycalc/expression_parser.py +++ b/final_task/pycalc/expression_parser.py @@ -89,7 +89,8 @@ def split_operators(self, expression_line): raise SyntaxError('Blank symbol between twice operator') elif self.blank_item: self.blank_item = False - self.append_to_parsing_list() + if not self.last_symbol: + self.append_to_parsing_list() self.last_symbol += i else: self.append_to_parsing_list() From 8ac1b50e6a64882bd1fb953d34d760995fdc0b5d Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Thu, 16 May 2019 00:50:11 +0300 Subject: [PATCH 042/103] feat: extract metohds to parse numbers, functions, operators from spli_operators method, rename methods number_parser to number_chekc, operator_parser to operator_check, function_parser tp function_check --- final_task/pycalc/expression_parser.py | 80 +++++++++++++++----------- 1 file changed, 46 insertions(+), 34 deletions(-) diff --git a/final_task/pycalc/expression_parser.py b/final_task/pycalc/expression_parser.py index b28e07f5..8bc97951 100644 --- a/final_task/pycalc/expression_parser.py +++ b/final_task/pycalc/expression_parser.py @@ -21,18 +21,18 @@ def __init__(self): self.last_symbol = "" self.blank_item = False - def operartor_parser(self, operator_symbol): + def operartor_check(self, operator_symbol): if operator_symbol in operator_dict.keys(): return operator_symbol raise SyntaxError('Typo in math operator!') - def number_parser(self, number): + def number_check(self, number): try: return int(number) except ValueError: return float(number) - def function_parser(self, function_name): + def function_check(self, function_name): if function_name == 'e' or function_name == 'pi': return function_dict[function_name]['operator'] elif function_name == 'tau': @@ -49,15 +49,51 @@ def function_parser(self, function_name): def append_to_parsing_list(self): if self.last_symbol: - self.parsing_list.append(self.operartor_parser(self.last_symbol)) + self.parsing_list.append(self.operartor_check(self.last_symbol)) self.last_symbol = "" elif self.last_number: - self.parsing_list.append(self.number_parser(self.last_number)) + self.parsing_list.append(self.number_check(self.last_number)) self.last_number = "" elif self.last_letter: - self.parsing_list.append(self.function_parser(self.last_letter)) + self.parsing_list.append(self.function_check(self.last_letter)) self.last_letter = "" + def number_parser(self, number): + if self.blank_item and not isinstance(self.parsing_list[-1], str): + raise SyntaxError('Blank symbol between two operands!') + elif self.blank_item: + self.blank_item = False + if self.last_symbol: + self.append_to_parsing_list() + if self.last_letter: + self.last_letter += number + else: + if '.' in self.last_number and number == '.': + raise SyntaxError('Typo in the operand (two comma)!') + else: + self.last_number += number + + def function_parser(self, letter): + if self.last_symbol: + self.append_to_parsing_list() + self.last_letter += letter + + def twice_operator_parser(self, symbol): + if len(self.last_symbol) == 2: + raise SyntaxError('Invalid operator "{}"'.format(self.last_symbol + symbol)) + if self.blank_item and str(self.parsing_list[-1]) in "!=<>/*": + raise SyntaxError('Blank symbol between twice operator') + elif self.blank_item: + self.blank_item = False + if not self.last_symbol: + self.append_to_parsing_list() + self.last_symbol += symbol + + def simple_operator_bracket_parser(self, symbol): + self.append_to_parsing_list() + if symbol != ' ': + self.parsing_list.append(symbol) + def split_operators(self, expression_line): if check_expression(expression_line): for i in expression_line: @@ -65,37 +101,13 @@ def split_operators(self, expression_line): self.blank_item = True self.append_to_parsing_list() if i.isnumeric() or i == '.': - if self.blank_item and not isinstance(self.parsing_list[-1], str): - raise SyntaxError('Blank symbol between two operands!') - elif self.blank_item: - self.blank_item = False - if self.last_symbol: - self.append_to_parsing_list() - if self.last_letter: - self.last_letter += i - else: - if '.' in self.last_number and i == '.': - raise SyntaxError('Typo in the operand (two comma)!') - else: - self.last_number += i + self.number_parser(i) elif i.isalpha(): - if self.last_symbol: - self.append_to_parsing_list() - self.last_letter += i + self.function_parser(i) elif i in "!=<>/*": - if len(self.last_symbol) == 2: - raise SyntaxError('Invalid operator "{}"'.format(self.last_symbol + i)) - if self.blank_item and str(self.parsing_list[-1]) in "!=<>/*": - raise SyntaxError('Blank symbol between twice operator') - elif self.blank_item: - self.blank_item = False - if not self.last_symbol: - self.append_to_parsing_list() - self.last_symbol += i + self.twice_operator_parser(i) else: - self.append_to_parsing_list() - if i != ' ': - self.parsing_list.append(i) + self.symple_operator_bracket_parser(i) if self.last_symbol: raise SyntaxError( 'Extra operator "{}" at the end of the expression!'.format(self.last_symbol) From a245b70ce72a9efb86a84e66465106877171f653 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Thu, 16 May 2019 00:56:35 +0300 Subject: [PATCH 043/103] fix: correct typo in method name --- final_task/pycalc/expression_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/final_task/pycalc/expression_parser.py b/final_task/pycalc/expression_parser.py index 8bc97951..d57c0e41 100644 --- a/final_task/pycalc/expression_parser.py +++ b/final_task/pycalc/expression_parser.py @@ -107,7 +107,7 @@ def split_operators(self, expression_line): elif i in "!=<>/*": self.twice_operator_parser(i) else: - self.symple_operator_bracket_parser(i) + self.simple_operator_bracket_parser(i) if self.last_symbol: raise SyntaxError( 'Extra operator "{}" at the end of the expression!'.format(self.last_symbol) From 332f78abde261caaf8d78089a06199022cc7b425 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Thu, 16 May 2019 22:21:35 +0300 Subject: [PATCH 044/103] feat: extract function converter into class Converter and move it into module; extract class OperandStack into module; update check_manager with method from expression_parser about checkin numbers, operators, functions; update module expression_parser with new names of internal methods by adding "_" --- final_task/pycalc/check_manager.py | 52 ++++++++-- final_task/pycalc/converter.py | 75 +++++++++++++++ final_task/pycalc/expression_parser.py | 75 ++++----------- final_task/pycalc/pycalc.py | 127 ++----------------------- final_task/pycalc/stack_manager.py | 19 ++++ 5 files changed, 167 insertions(+), 181 deletions(-) create mode 100644 final_task/pycalc/converter.py create mode 100644 final_task/pycalc/stack_manager.py diff --git a/final_task/pycalc/check_manager.py b/final_task/pycalc/check_manager.py index affbf582..1bb7ce4b 100644 --- a/final_task/pycalc/check_manager.py +++ b/final_task/pycalc/check_manager.py @@ -1,11 +1,51 @@ -from . import operator_manager +from .operator_manager import operator_dict, function_dict, unary_dict -def check_expression(expression_line): - if not expression_line: +def check_expression(parsing_list): + if not parsing_list: raise SyntaxError('Expression cannot be empty') - if expression_line.count('(') < expression_line.count(')'): + if parsing_list.count('(') < parsing_list.count(')'): raise SyntaxError('Opening bracket required!') - elif expression_line.count('(') > expression_line.count(')'): + elif parsing_list.count('(') > parsing_list.count(')'): raise SyntaxError('Closing bracket required!') - return expression_line + if parsing_list[0] in operator_dict.keys(): + if parsing_list[0] is not '+' and parsing_list[0] is not '-': + raise SyntaxError('Expression cannot start with "{}"'.format(parsing_list[0])) + if len(parsing_list) == 1: + if type(parsing_list[0]) is int or type(parsing_list[0]) is float: + return True + raise SyntaxError('Expression must include at list one operand!') + if parsing_list[-1] in operator_dict.keys(): + raise SyntaxError('Extra operator "{}" at the end of an expression!'.format(parsing_list[-1])) + if parsing_list[-1] in function_dict.keys(): + raise SyntaxError('Function "{}" without argument'.format(parsing_list[-1])) + return parsing_list + + +def operator_check(operator_symbol): + if operator_symbol in operator_dict.keys(): + return operator_symbol + raise SyntaxError('Typo in math operator!') + + +def number_check(number): + try: + return int(number) + except ValueError: + return float(number) + + +def function_check(function_name): + if function_name == 'e' or function_name == 'pi': + return function_dict[function_name]['operator'] + elif function_name == 'tau': + if sys.version_info >= (3, 6): + return function_dict[function_name]['operator'] + else: + return 2 * function_dict['e']['operator'] + elif function_name in function_dict.keys(): + return function_name + else: + raise SyntaxError( + 'There is no function with this name {}!'.format(function_name) + ) diff --git a/final_task/pycalc/converter.py b/final_task/pycalc/converter.py new file mode 100644 index 00000000..231dd9e3 --- /dev/null +++ b/final_task/pycalc/converter.py @@ -0,0 +1,75 @@ +from .operator_manager import operator_dict, function_dict, unary_dict + + +class Converter: + + def __init__(self, parsing_list): + self.parsing_list = parsing_list + self.last_item = "" + self.converted_list = [] + + def _clean_add_sub_operators(self): + if self.last_item.count('-') % 2 == 0: + if self.converted_list[-1] == '(': + self.last_item = "" + else: + self.last_item = '+' + else: + self.last_item = '-' + + def _append_to_converted_list(self, *args): + [self.converted_list.append(i) for i in args] + self.last_item = "" + + def _number_converter(self, number): + if self.last_item == '-': + if self.converted_list[-1] == 0: + self._append_to_converted_list(unary_dict[self.last_item], number) + elif self.last_item == '-' and self.converted_list[-1] != '(' \ + and self.converted_list[-1] not in operator_dict.values(): + self._append_to_converted_list(operator_dict[self.last_item], number) + else: + self._append_to_converted_list(-number) + else: + self._append_to_converted_list(number) + + def _operator_converter(self, operator_str): + if operator_str == '-' or operator_str == '+': + self.last_item += operator_str + else: + try: + if self.converted_list[-1]['operator']: + raise SyntaxError('Missing operand between two math operators!') + except TypeError: + self._append_to_converted_list(operator_dict[operator_str]) + + def _function_converter(self, function): + if self.last_item: + if self.last_item == '-' and self.converted_list[-1] != '(': + self._append_to_converted_list('+', -1, '*', function) + elif self.last_item == '-' and self.converted_list[-1] == '(': + self._append_to_converted_list(-1, '*', function) + else: + self._append_to_converted_list(function_dict[function]) + + def converter(self): + if self.parsing_list[0] in operator_dict.keys(): + self.converted_list.append(0) + for i in self.parsing_list: + if i == " ": + continue + if i != '-' and i != '+' and self.last_item: + self._clean_add_sub_operators() + if self.last_item == '+': + self._append_to_converted_list(operator_dict[self.last_item]) + if isinstance(i, float) or isinstance(i, int): + self._number_converter(i) + elif i in operator_dict.keys(): + self._operator_converter(i) + elif i in function_dict.keys(): + self._function_converter(i) + else: + if self.last_item: + self._append_to_converted_list(operator_dict['-']) + self._append_to_converted_list(i) + return self.converted_list diff --git a/final_task/pycalc/expression_parser.py b/final_task/pycalc/expression_parser.py index d57c0e41..910d7a0c 100644 --- a/final_task/pycalc/expression_parser.py +++ b/final_task/pycalc/expression_parser.py @@ -1,15 +1,6 @@ import sys -from .operator_manager import * - - -def check_expression(expression_line): - if not expression_line: - raise SyntaxError('Expression cannot be empty') - if expression_line.count('(') < expression_line.count(')'): - raise SyntaxError('Opening bracket required!') - elif expression_line.count('(') > expression_line.count(')'): - raise SyntaxError('Closing bracket required!') - return True +from .operator_manager import operator_dict, function_dict, unary_dict +from .check_manager import check_expression, number_check, operator_check, function_check class SplitOperators: @@ -21,50 +12,24 @@ def __init__(self): self.last_symbol = "" self.blank_item = False - def operartor_check(self, operator_symbol): - if operator_symbol in operator_dict.keys(): - return operator_symbol - raise SyntaxError('Typo in math operator!') - - def number_check(self, number): - try: - return int(number) - except ValueError: - return float(number) - - def function_check(self, function_name): - if function_name == 'e' or function_name == 'pi': - return function_dict[function_name]['operator'] - elif function_name == 'tau': - if sys.version_info >= (3, 6): - return function_dict[function_name]['operator'] - else: - return 2 * function_dict['e']['operator'] - elif function_name in function_dict.keys(): - return function_name - else: - raise SyntaxError( - 'There is no function with this name {}!'.format(function_name) - ) - - def append_to_parsing_list(self): + def _append_to_parsing_list(self): if self.last_symbol: - self.parsing_list.append(self.operartor_check(self.last_symbol)) + self.parsing_list.append(operator_check(self.last_symbol)) self.last_symbol = "" elif self.last_number: - self.parsing_list.append(self.number_check(self.last_number)) + self.parsing_list.append(number_check(self.last_number)) self.last_number = "" elif self.last_letter: - self.parsing_list.append(self.function_check(self.last_letter)) + self.parsing_list.append(function_check(self.last_letter)) self.last_letter = "" - def number_parser(self, number): + def _number_parser(self, number): if self.blank_item and not isinstance(self.parsing_list[-1], str): raise SyntaxError('Blank symbol between two operands!') elif self.blank_item: self.blank_item = False if self.last_symbol: - self.append_to_parsing_list() + self._append_to_parsing_list() if self.last_letter: self.last_letter += number else: @@ -73,12 +38,12 @@ def number_parser(self, number): else: self.last_number += number - def function_parser(self, letter): + def _function_parser(self, letter): if self.last_symbol: - self.append_to_parsing_list() + self._append_to_parsing_list() self.last_letter += letter - def twice_operator_parser(self, symbol): + def _twice_operator_parser(self, symbol): if len(self.last_symbol) == 2: raise SyntaxError('Invalid operator "{}"'.format(self.last_symbol + symbol)) if self.blank_item and str(self.parsing_list[-1]) in "!=<>/*": @@ -86,11 +51,11 @@ def twice_operator_parser(self, symbol): elif self.blank_item: self.blank_item = False if not self.last_symbol: - self.append_to_parsing_list() + self._append_to_parsing_list() self.last_symbol += symbol - def simple_operator_bracket_parser(self, symbol): - self.append_to_parsing_list() + def _simple_operator_bracket_parser(self, symbol): + self._append_to_parsing_list() if symbol != ' ': self.parsing_list.append(symbol) @@ -99,18 +64,18 @@ def split_operators(self, expression_line): for i in expression_line: if i == " ": self.blank_item = True - self.append_to_parsing_list() + self._append_to_parsing_list() if i.isnumeric() or i == '.': - self.number_parser(i) + self._number_parser(i) elif i.isalpha(): - self.function_parser(i) + self._function_parser(i) elif i in "!=<>/*": - self.twice_operator_parser(i) + self._twice_operator_parser(i) else: - self.simple_operator_bracket_parser(i) + self._simple_operator_bracket_parser(i) if self.last_symbol: raise SyntaxError( 'Extra operator "{}" at the end of the expression!'.format(self.last_symbol) ) - self.append_to_parsing_list() + self._append_to_parsing_list() return self.parsing_list diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index c8d033de..a2476ee7 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -1,12 +1,11 @@ #!/usr/bin/python3 -import operator -import math -import sys import argparse -from .expression_parser import * -from .operator_manager import * -from .check_manager import * +from .expression_parser import SplitOperators +from .operator_manager import operator_dict, function_dict, unary_dict +from .check_manager import check_expression +from .stack_manager import OperandStack +from .converter import Converter def arg_parser(): @@ -24,116 +23,6 @@ def arg_parser(): return expression_line -def check_parsing_list(parsing_list): - if parsing_list[0] in operator_dict.keys(): - if parsing_list[0] is not '+' and parsing_list[0] is not '-': - raise SyntaxError('Expression cannot start with "{}"'.format(parsing_list[0])) - if len(parsing_list) == 1: - if type(parsing_list[0]) is int or type(parsing_list[0]) is float: - return True - raise SyntaxError('Expression must include at list one operand!') - if parsing_list[-1] in operator_dict.keys(): - raise SyntaxError('Extra operator "{}" at the end of an expression!'.format(parsing_list[-1])) - if parsing_list[-1] in function_dict.keys(): - raise SyntaxError('Function "{}" without argument'.format(parsing_list[-1])) - return True - - -def clean_add_sub_operators(last_item, converted_list): - if last_item.count('-') % 2 == 0: - if converted_list[-1] == '(': - last_item = "" - else: - last_item = '+' - else: - last_item = '-' - return last_item - - -def converter(parsing_list): - check_parsing_list(parsing_list) - if parsing_list[0] == "-" or parsing_list[0] == "+": - converted_list = [0] - else: - converted_list = [] - last_item = "" - for i in parsing_list: - if i == " ": - continue - if i != '-' and i != '+' and last_item: - last_item = clean_add_sub_operators(last_item, converted_list) - if last_item == '+': - converted_list.append(operator_dict[last_item]) - last_item = '' - if isinstance(i, float) or isinstance(i, int): - if last_item == '-': - if converted_list[-1] == 0: - converted_list.append(unary_dict[last_item]) - converted_list.append(i) - last_item = "" - elif last_item == '-' and converted_list[-1] != '(' \ - and converted_list[-1] not in operator_dict.values(): - converted_list.append(operator_dict[last_item]) - converted_list.append(i) - last_item = "" - else: - converted_list.append(-i) - last_item = "" - else: - converted_list.append(i) - elif i in operator_dict.keys(): - if i == '-' or i == '+': - last_item += i - else: - try: - if converted_list[-1]['operator']: - raise SyntaxError('Missing operand between two math operators!') - except TypeError: - converted_list.append(operator_dict[i]) - elif i in function_dict.keys(): - if last_item: - if last_item == '-' and converted_list[-1] != '(': - converted_list.append(operator_dict['+']) - converted_list.append(-1) - converted_list.append(operator_dict['*']) - converted_list.append(function_dict[i]) - last_item = "" - elif last_item == '-' and converted_list[-1] == '(': - converted_list.append(-1) - converted_list.append(operator_dict['*']) - converted_list.append(function_dict[i]) - last_item = "" - else: - converted_list.append(function_dict[i]) - else: - if last_item: - converted_list.append(operator_dict['-']) - last_item = "" - converted_list.append(i) - return converted_list - - -class OperandStack: - - def __init__(self): - self.stack = list() - - def put_on_stack(self, item): - self.stack.append(item) - - def top(self): - return self.stack[-1] - - def take_from_stack(self): - return self.stack.pop() - - def is_empty(self): - if len(self.stack) == 0: - return True - else: - return False - - def calc_on_stack(): operator_on_stack = function.take_from_stack() global func_arguments @@ -221,12 +110,10 @@ def calculate(converted_list): def main(): try: expression_line = arg_parser() - operands = OperandStack() - function = OperandStack() parser = SplitOperators().split_operators(expression_line) clear_parser = check_expression(parser) - converted_list = converter(clear_parser) - result = calculate(converted_list) + converted_list = Converter(clear_parser) + result = calculate(converted_list.converter()) print(result) except SyntaxError as err: print('ERROR: {}'.format(err)) diff --git a/final_task/pycalc/stack_manager.py b/final_task/pycalc/stack_manager.py new file mode 100644 index 00000000..e781a547 --- /dev/null +++ b/final_task/pycalc/stack_manager.py @@ -0,0 +1,19 @@ +class OperandStack: + + def __init__(self): + self.stack = list() + + def put_on_stack(self, item): + self.stack.append(item) + + def top(self): + return self.stack[-1] + + def take_from_stack(self): + return self.stack.pop() + + def is_empty(self): + if len(self.stack) == 0: + return True + else: + return False \ No newline at end of file From 0e6db61a1eef02846f9267bed6ef72f86b1ce443 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Thu, 16 May 2019 23:12:20 +0300 Subject: [PATCH 045/103] fix: divide function check_expressino into two function check_expression and check_parisng_list; call function check_parsing_list in the method converter in class Converter; send to function append_to_convert_list correct data from method _function_converter --- final_task/pycalc/check_manager.py | 12 ++++++++---- final_task/pycalc/converter.py | 17 ++++++++++++++--- final_task/pycalc/stack_manager.py | 2 +- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/final_task/pycalc/check_manager.py b/final_task/pycalc/check_manager.py index 1bb7ce4b..e95f0f34 100644 --- a/final_task/pycalc/check_manager.py +++ b/final_task/pycalc/check_manager.py @@ -1,13 +1,17 @@ from .operator_manager import operator_dict, function_dict, unary_dict -def check_expression(parsing_list): - if not parsing_list: +def check_expression(expression_list): + if not expression_list: raise SyntaxError('Expression cannot be empty') - if parsing_list.count('(') < parsing_list.count(')'): + if expression_list.count('(') < expression_list.count(')'): raise SyntaxError('Opening bracket required!') - elif parsing_list.count('(') > parsing_list.count(')'): + elif expression_list.count('(') > expression_list.count(')'): raise SyntaxError('Closing bracket required!') + return expression_list + + +def check_parsing_list(parsing_list): if parsing_list[0] in operator_dict.keys(): if parsing_list[0] is not '+' and parsing_list[0] is not '-': raise SyntaxError('Expression cannot start with "{}"'.format(parsing_list[0])) diff --git a/final_task/pycalc/converter.py b/final_task/pycalc/converter.py index 231dd9e3..e411d380 100644 --- a/final_task/pycalc/converter.py +++ b/final_task/pycalc/converter.py @@ -1,4 +1,5 @@ from .operator_manager import operator_dict, function_dict, unary_dict +from .check_manager import check_parsing_list class Converter: @@ -45,14 +46,24 @@ def _operator_converter(self, operator_str): def _function_converter(self, function): if self.last_item: - if self.last_item == '-' and self.converted_list[-1] != '(': - self._append_to_converted_list('+', -1, '*', function) + if self.last_item == '-' and self.converted_list[-1] not in '()': + self._append_to_converted_list( + operator_dict['+'], + -1, + operator_dict['*'], + function_dict[function] + ) elif self.last_item == '-' and self.converted_list[-1] == '(': - self._append_to_converted_list(-1, '*', function) + self._append_to_converted_list( + -1, + operator_dict['*'], + function_dict[function] + ) else: self._append_to_converted_list(function_dict[function]) def converter(self): + check_parsing_list(self.parsing_list) if self.parsing_list[0] in operator_dict.keys(): self.converted_list.append(0) for i in self.parsing_list: diff --git a/final_task/pycalc/stack_manager.py b/final_task/pycalc/stack_manager.py index e781a547..e03ab564 100644 --- a/final_task/pycalc/stack_manager.py +++ b/final_task/pycalc/stack_manager.py @@ -16,4 +16,4 @@ def is_empty(self): if len(self.stack) == 0: return True else: - return False \ No newline at end of file + return False From c415e3149a013a2a3926ea2e53ab1692ddab8152 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Thu, 16 May 2019 23:25:18 +0300 Subject: [PATCH 046/103] fix: changed if statment in method _function_converter to check when we need to add unary "-" before function --- final_task/pycalc/converter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/final_task/pycalc/converter.py b/final_task/pycalc/converter.py index e411d380..09da1619 100644 --- a/final_task/pycalc/converter.py +++ b/final_task/pycalc/converter.py @@ -46,14 +46,14 @@ def _operator_converter(self, operator_str): def _function_converter(self, function): if self.last_item: - if self.last_item == '-' and self.converted_list[-1] not in '()': + if self.last_item == '-' and self.converted_list[-1] != '(': self._append_to_converted_list( operator_dict['+'], -1, operator_dict['*'], function_dict[function] ) - elif self.last_item == '-' and self.converted_list[-1] == '(': + elif self.last_item == '-' and self.converted_list[-1] in '()': self._append_to_converted_list( -1, operator_dict['*'], From d4d85e0ee26e0fb9fa9088cda15de44f49db6f49 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Fri, 17 May 2019 22:53:06 +0300 Subject: [PATCH 047/103] feat: extract fucntion calculcate into module calculator, update SplitOperators with required argumetn in init method, rename OperandStack into Stack, remove unused imports --- final_task/pycalc/calculator.py | 93 +++++++++++++++++++++++++ final_task/pycalc/check_manager.py | 3 +- final_task/pycalc/expression_parser.py | 11 ++- final_task/pycalc/pycalc.py | 96 +------------------------- final_task/pycalc/stack_manager.py | 2 +- 5 files changed, 104 insertions(+), 101 deletions(-) create mode 100644 final_task/pycalc/calculator.py diff --git a/final_task/pycalc/calculator.py b/final_task/pycalc/calculator.py new file mode 100644 index 00000000..fab830a3 --- /dev/null +++ b/final_task/pycalc/calculator.py @@ -0,0 +1,93 @@ +from .stack_manager import Stack +from .operator_manager import operator_dict, function_dict, unary_dict +from .expression_parser import SplitOperators +from .check_manager import check_expression +from .converter import Converter + + +class Calculator: + def __init__(self, expression_line): + self.parser = SplitOperators(expression_line).split_operators() + self.converted_list = Converter(self.parser).converter() + self.current_result = "" + self.operands = Stack() + self.function = Stack() + self.func_argument = False + self.current_operator = "" + + def _calc_on_stack(self): + operator_on_stack = self.function.take_from_stack() + if operator_on_stack in function_dict.values(): + if self.func_argument: + second_operand = self.operands.take_from_stack() + first_operand = self.operands.take_from_stack() + self.current_result = operator_on_stack['operator'](first_operand, second_operand) + self.func_argument = False + else: + first_operand = self.operands.take_from_stack() + self.current_result = operator_on_stack['operator'](first_operand) + elif operator_on_stack in operator_dict.values() or operator_on_stack in unary_dict.values(): + if len(self.operands.stack) == 1: + second_operand = self.operands.take_from_stack() + first_operand = 0 + else: + second_operand = self.operands.take_from_stack() + first_operand = self.operands.take_from_stack() + self.current_result = operator_on_stack['operator'](first_operand, second_operand) + elif operator_on_stack == '(': + return self.current_result + self.operands.put_on_stack(self.current_result) + if len(self.function.stack) and self.function.top() is not '(': + if self.current_operator['priority'] >= self.function.top()['priority']: + self.current_result = self._calc_on_stack() + + def calculate(self): + for item in self.converted_list: + if isinstance(item, float) or isinstance(item, int): + self.operands.put_on_stack(item) + elif item in operator_dict.values() \ + or item in function_dict.values() \ + or item in unary_dict.values(): + self.current_operator = item + if self.function.is_empty(): + self.function.put_on_stack(self.current_operator) + else: + if self.function.top() is '(' \ + or self.current_operator['priority'] < self.function.top()['priority'] \ + or self.current_operator == operator_dict['^'] \ + and self.function.top() == operator_dict['^']: + self.function.put_on_stack(self.current_operator) + else: + self._calc_on_stack() + self.function.put_on_stack(self.current_operator) + elif item is '(': + self.function.put_on_stack(item) + elif item is ')' and self.function.top() == '(': + self.function.take_from_stack() + else: + for i in range(len(self.function.stack)): + if item is ',' and self.function.top() is '(': + if self.func_argument: + raise SyntaxError('This function can have only two arguments') + self.func_argument = True + break + elif self.func_argument: + self._calc_on_stack() + else: + self.func_argument = False + if len(self.function.stack): + self._calc_on_stack() + if item is ')' and len(self.function.stack): + if self.function.top() is '(': + self.function.take_from_stack() + break + if self.function.is_empty(): + self.current_result = self.operands.take_from_stack() + elif len(self.function.stack) == 1: + self._calc_on_stack() + else: + for i in range(len(self.function.stack)): + self._calc_on_stack() + if self.function.is_empty(): + break + return self.current_result diff --git a/final_task/pycalc/check_manager.py b/final_task/pycalc/check_manager.py index e95f0f34..e8eb732b 100644 --- a/final_task/pycalc/check_manager.py +++ b/final_task/pycalc/check_manager.py @@ -1,4 +1,5 @@ -from .operator_manager import operator_dict, function_dict, unary_dict +import sys +from .operator_manager import operator_dict, function_dict def check_expression(expression_list): diff --git a/final_task/pycalc/expression_parser.py b/final_task/pycalc/expression_parser.py index 910d7a0c..3e95eec2 100644 --- a/final_task/pycalc/expression_parser.py +++ b/final_task/pycalc/expression_parser.py @@ -1,11 +1,10 @@ -import sys -from .operator_manager import operator_dict, function_dict, unary_dict from .check_manager import check_expression, number_check, operator_check, function_check class SplitOperators: - def __init__(self): + def __init__(self, expression_line): + self.expression_line = expression_line self.parsing_list = [] self.last_number = "" self.last_letter = "" @@ -59,9 +58,9 @@ def _simple_operator_bracket_parser(self, symbol): if symbol != ' ': self.parsing_list.append(symbol) - def split_operators(self, expression_line): - if check_expression(expression_line): - for i in expression_line: + def split_operators(self): + if check_expression(self.expression_line): + for i in self.expression_line: if i == " ": self.blank_item = True self._append_to_parsing_list() diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index a2476ee7..6421a0be 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -1,11 +1,7 @@ #!/usr/bin/python3 import argparse -from .expression_parser import SplitOperators -from .operator_manager import operator_dict, function_dict, unary_dict -from .check_manager import check_expression -from .stack_manager import OperandStack -from .converter import Converter +from .calculator import Calculator def arg_parser(): @@ -23,97 +19,11 @@ def arg_parser(): return expression_line -def calc_on_stack(): - operator_on_stack = function.take_from_stack() - global func_arguments - if operator_on_stack in function_dict.values(): - if func_arguments: - second_operand = operands.take_from_stack() - first_operand = operands.take_from_stack() - current_result = operator_on_stack['operator'](first_operand, second_operand) - func_arguments = False - else: - first_operand = operands.take_from_stack() - current_result = operator_on_stack['operator'](first_operand) - elif operator_on_stack in operator_dict.values() or operator_on_stack in unary_dict.values(): - if len(operands.stack) == 1: - second_operand = operands.take_from_stack() - first_operand = 0 - else: - second_operand = operands.take_from_stack() - first_operand = operands.take_from_stack() - current_result = operator_on_stack['operator'](first_operand, second_operand) - elif operator_on_stack == '(': - return - operands.put_on_stack(current_result) - if len(function.stack) and function.top() is not '(': - if current_operator['priority'] >= function.top()['priority']: - current_result = calc_on_stack() - return current_result - - -def calculate(converted_list): - global operands, function, func_arguments, current_operator, current_result - operands = OperandStack() - function = OperandStack() - func_arguments = False - for item in converted_list: - if isinstance(item, float) or isinstance(item, int): - operands.put_on_stack(item) - elif item in operator_dict.values() \ - or item in function_dict.values() \ - or item in unary_dict.values(): - current_operator = item - if function.is_empty(): - function.put_on_stack(current_operator) - else: - if function.top() is '(' or current_operator['priority'] < function.top()['priority'] or \ - current_operator == operator_dict['^'] and function.top() == operator_dict['^']: - function.put_on_stack(current_operator) - else: - current_result = calc_on_stack() - function.put_on_stack(current_operator) - elif item is '(': - function.put_on_stack(item) - elif item is ')' and function.top() == '(': - function.take_from_stack() - else: - for i in range(len(function.stack)): - if item is ',' and function.top() is '(': - if func_arguments: - raise SyntaxError('This function can have only two arguments') - func_arguments = True - break - elif func_arguments: - current_result = calc_on_stack() - else: - func_arguments = False - if len(function.stack): - current_result = calc_on_stack() - if item is ')' and len(function.stack): - if function.top() is '(': - function.take_from_stack() - break - if function.is_empty(): - current_result = operands.take_from_stack() - elif len(function.stack) == 1: - current_result = calc_on_stack() - else: - for i in range(len(function.stack)): - current_operator = function.top() - current_result = calc_on_stack() - if not len(function.stack): - break - return current_result - - def main(): try: expression_line = arg_parser() - parser = SplitOperators().split_operators(expression_line) - clear_parser = check_expression(parser) - converted_list = Converter(clear_parser) - result = calculate(converted_list.converter()) + calc = Calculator(expression_line) + result = calc.calculate() print(result) except SyntaxError as err: print('ERROR: {}'.format(err)) diff --git a/final_task/pycalc/stack_manager.py b/final_task/pycalc/stack_manager.py index e03ab564..8632e031 100644 --- a/final_task/pycalc/stack_manager.py +++ b/final_task/pycalc/stack_manager.py @@ -1,4 +1,4 @@ -class OperandStack: +class Stack: def __init__(self): self.stack = list() From 011ba7a808c9a8750967170511b2e0830e0cfc40 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Fri, 17 May 2019 23:18:58 +0300 Subject: [PATCH 048/103] fix: include return to method calc_on_stack to avoid lost of the current_result during recursion --- final_task/pycalc/calculator.py | 1 + 1 file changed, 1 insertion(+) diff --git a/final_task/pycalc/calculator.py b/final_task/pycalc/calculator.py index fab830a3..85d90713 100644 --- a/final_task/pycalc/calculator.py +++ b/final_task/pycalc/calculator.py @@ -40,6 +40,7 @@ def _calc_on_stack(self): if len(self.function.stack) and self.function.top() is not '(': if self.current_operator['priority'] >= self.function.top()['priority']: self.current_result = self._calc_on_stack() + return self.current_result def calculate(self): for item in self.converted_list: From 75ac4b86cc6ae6867e998dde12b59b25c00e2822 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Sun, 19 May 2019 01:47:43 +0300 Subject: [PATCH 049/103] feat: extract agr_parser into module, rename module expression_parser to split_operators, add unit tests for module expression_parser --- final_task/pycalc/argument_parser.py | 16 ++ final_task/pycalc/calculator.py | 2 +- final_task/pycalc/operator_manager.py | 1 - final_task/pycalc/pycalc.py | 17 +- ...xpression_parser.py => split_operators.py} | 0 final_task/tests/__init__.py | 0 final_task/tests/test_split_operators.py | 165 ++++++++++++++++++ 7 files changed, 183 insertions(+), 18 deletions(-) create mode 100644 final_task/pycalc/argument_parser.py rename final_task/pycalc/{expression_parser.py => split_operators.py} (100%) create mode 100644 final_task/tests/__init__.py create mode 100644 final_task/tests/test_split_operators.py diff --git a/final_task/pycalc/argument_parser.py b/final_task/pycalc/argument_parser.py new file mode 100644 index 00000000..7bb33781 --- /dev/null +++ b/final_task/pycalc/argument_parser.py @@ -0,0 +1,16 @@ +import argparse + + +def arg_parser(): + parser = argparse.ArgumentParser(description='Pure-python command-line calculator.', prog='pycalc') + parser.add_argument( + '-m', + '--use-modules', + help='additional modules to use', + metavar='MODULE [MODULE ...]' + ) + parser.add_argument( + 'EXPRESSION', help='expression string to calculate' + ) + expression_line = parser.parse_args().EXPRESSION + return expression_line diff --git a/final_task/pycalc/calculator.py b/final_task/pycalc/calculator.py index 85d90713..cf0064a9 100644 --- a/final_task/pycalc/calculator.py +++ b/final_task/pycalc/calculator.py @@ -1,6 +1,6 @@ from .stack_manager import Stack from .operator_manager import operator_dict, function_dict, unary_dict -from .expression_parser import SplitOperators +from .split_operators import SplitOperators from .check_manager import check_expression from .converter import Converter diff --git a/final_task/pycalc/operator_manager.py b/final_task/pycalc/operator_manager.py index 83ab20fb..f009af35 100644 --- a/final_task/pycalc/operator_manager.py +++ b/final_task/pycalc/operator_manager.py @@ -1,7 +1,6 @@ import math import operator - function_dict = { 'abs': {'operator': abs, 'priority': 0}, 'round': {'operator': round, 'priority': 0} diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index 6421a0be..c63cca69 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -1,22 +1,7 @@ #!/usr/bin/python3 -import argparse from .calculator import Calculator - - -def arg_parser(): - parser = argparse.ArgumentParser(description='Pure-python command-line calculator.', prog='pycalc') - parser.add_argument( - '-m', - '--use-modules', - help='additional modules to use', - metavar='MODULE [MODULE ...]' - ) - parser.add_argument( - 'EXPRESSION', help='expression string to calculate' - ) - expression_line = parser.parse_args().EXPRESSION - return expression_line +from .argument_parser import arg_parser def main(): diff --git a/final_task/pycalc/expression_parser.py b/final_task/pycalc/split_operators.py similarity index 100% rename from final_task/pycalc/expression_parser.py rename to final_task/pycalc/split_operators.py diff --git a/final_task/tests/__init__.py b/final_task/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/final_task/tests/test_split_operators.py b/final_task/tests/test_split_operators.py new file mode 100644 index 00000000..9706a98e --- /dev/null +++ b/final_task/tests/test_split_operators.py @@ -0,0 +1,165 @@ +import unittest +from unittest.mock import patch, MagicMock +from pycalc.split_operators import SplitOperators + + +class TestSplitOperators(unittest.TestCase): + + def test_init_class_method(self): + expression = SplitOperators('1') + self.assertEqual('1', expression.expression_line) + self.assertEqual([], expression.parsing_list) + self.assertEqual("", expression.last_number) + self.assertEqual("", expression.last_letter) + self.assertEqual("", expression.last_symbol) + self.assertEqual(False, expression.blank_item) + + def test_uno_number(self): + expression = SplitOperators('1') + result = expression.split_operators() + self.assertEqual([1], result) + + def test_twice_number(self): + expression = SplitOperators('12') + result = expression.split_operators() + self.assertEqual([12], result) + + def test_symbol_append_to_parsing_list(self): + expression = SplitOperators('') + expression.last_symbol = '*' + expression._append_to_parsing_list() + self.assertEqual(['*'], expression.parsing_list) + self.assertEqual('', expression.last_symbol) + + def test_number_append_to_parsing_list(self): + expression = SplitOperators('12') + expression.last_number = '12' + expression._append_to_parsing_list() + self.assertEqual([12], expression.parsing_list) + self.assertEqual('', expression.last_number) + + def test_letter_append_to_parsing_list(self): + expression = SplitOperators('12') + expression.last_letter = 'sin' + expression._append_to_parsing_list() + self.assertEqual(['sin'], expression.parsing_list) + self.assertEqual('', expression.last_letter) + + def test_number_parser_with_symbol(self): + expression = SplitOperators('12') + expression.last_symbol = '+' + expression._number_parser('2') + self.assertEqual(['+'], expression.parsing_list) + self.assertEqual('2', expression.last_number) + + def test_number_parser_with_letter(self): + expression = SplitOperators('12') + expression.last_letter = 'log' + expression._number_parser('10') + self.assertEqual('log10', expression.last_letter) + self.assertEqual('', expression.last_number) + + def test_number_parser_with_first_number(self): + expression = SplitOperators('12') + expression._number_parser('1') + self.assertEqual('1', expression.last_number) + self.assertEqual([], expression.parsing_list) + + def test_number_parser_with_second_number(self): + expression = SplitOperators('12') + expression.last_number = '1' + expression._number_parser('2') + self.assertEqual('12', expression.last_number) + self.assertEqual([], expression.parsing_list) + + def test_number_parser_two_comma(self): + expression = SplitOperators('12') + expression.last_number = '1.0' + with self.assertRaises(SyntaxError): + expression._number_parser('.') + + def test_number_parser_space_into_operand(self): + expression = SplitOperators('12') + expression.blank_item = True + expression.parsing_list.append(1) + with self.assertRaises(SyntaxError): + expression._number_parser(2) + + def test_number_parser_blank_item_from_true_to_false(self): + expression = SplitOperators('12') + expression.blank_item = True + expression.parsing_list.append('+') + expression._number_parser('2') + self.assertEqual(False, expression.blank_item) + self.assertEqual(['+'], expression.parsing_list) + + def test_function_parser_with_symbol(self): + expression = SplitOperators('12') + expression.last_symbol = '+' + expression._function_parser('s') + self.assertEqual(['+'], expression.parsing_list) + self.assertEqual('s', expression.last_letter) + + def test_function_parser_second_letter(self): + expression = SplitOperators('12') + expression.last_letter = 's' + expression._function_parser('i') + self.assertEqual([], expression.parsing_list) + self.assertEqual('si', expression.last_letter) + + def test_twice_operator_parser_blank_item_from_true_to_false(self): + expression = SplitOperators('12') + expression.blank_item = True + expression.parsing_list.append(1) + expression._twice_operator_parser('*') + self.assertEqual(False, expression.blank_item) + self.assertEqual('*', expression.last_symbol) + self.assertEqual([1], expression.parsing_list) + + def test_twice_operator_add_second_symbol(self): + expression = SplitOperators('12') + expression.last_symbol = '*' + expression._twice_operator_parser('*') + self.assertEqual('**', expression.last_symbol) + self.assertEqual([], expression.parsing_list) + + # need to think about how to simulate func call + # def test_twice_operator_parser(self): + # expression = SplitOperators('12') + # expression.parsing_list.append(1) + # call_func = SplitOperators._append_to_parsing_list(expression) + # with patch.object(call_func, 'Clear') as mock: + # expression._twice_operator_parser('*') + # call_func.assert_called_once() + # self.assertEqual([1], expression.parsing_list) + + def test_twice_operator_parser_wrong_duble_operator(self): + expression = SplitOperators('12') + expression.last_symbol = '**' + with self.assertRaises(SyntaxError): + expression._twice_operator_parser('*') + + def test_twice_operator_parser_space_into_operator(self): + expression = SplitOperators('12') + expression.blank_item = True + expression.parsing_list.append('*') + with self.assertRaises(SyntaxError): + expression._twice_operator_parser('*') + + # need to think about how to simulate func call + def test_simple_operator(self): + expression = SplitOperators('12') + expression._simple_operator_bracket_parser('/') + self.assertEqual(['/'], expression.parsing_list) + + # need to think about how to simulate func call + def test_simple_operator_add_bracke(self): + expression = SplitOperators('12') + expression._simple_operator_bracket_parser('(') + self.assertEqual(['('], expression.parsing_list) + + # need to think about how to simulate func call + def test_simple_operator_blank_symbol(self): + expression = SplitOperators('12') + expression._simple_operator_bracket_parser(' ') + self.assertEqual([], expression.parsing_list) From 6f4df28855e694e6ff3ad20e383cbc4181f14960 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Sun, 19 May 2019 23:39:37 +0300 Subject: [PATCH 050/103] feat: add test module to test check_manager module, finish unittest in test_split_operators; fix: update check_manager with isintance method in check_parsing_list, changed return from True in parsing_list in this function --- final_task/pycalc/check_manager.py | 16 +-- final_task/tests/test_check_manager.py | 109 ++++++++++++++++++++ final_task/tests/test_split_operators.py | 122 ++++++++++++++--------- 3 files changed, 193 insertions(+), 54 deletions(-) create mode 100644 final_task/tests/test_check_manager.py diff --git a/final_task/pycalc/check_manager.py b/final_task/pycalc/check_manager.py index e8eb732b..68030a34 100644 --- a/final_task/pycalc/check_manager.py +++ b/final_task/pycalc/check_manager.py @@ -2,14 +2,14 @@ from .operator_manager import operator_dict, function_dict -def check_expression(expression_list): - if not expression_list: +def check_expression(expression_line): + if not expression_line: raise SyntaxError('Expression cannot be empty') - if expression_list.count('(') < expression_list.count(')'): + if expression_line.count('(') < expression_line.count(')'): raise SyntaxError('Opening bracket required!') - elif expression_list.count('(') > expression_list.count(')'): + elif expression_line.count('(') > expression_line.count(')'): raise SyntaxError('Closing bracket required!') - return expression_list + return expression_line def check_parsing_list(parsing_list): @@ -17,8 +17,8 @@ def check_parsing_list(parsing_list): if parsing_list[0] is not '+' and parsing_list[0] is not '-': raise SyntaxError('Expression cannot start with "{}"'.format(parsing_list[0])) if len(parsing_list) == 1: - if type(parsing_list[0]) is int or type(parsing_list[0]) is float: - return True + if isinstance(parsing_list[0], int) or isinstance(parsing_list[0], float): + return parsing_list raise SyntaxError('Expression must include at list one operand!') if parsing_list[-1] in operator_dict.keys(): raise SyntaxError('Extra operator "{}" at the end of an expression!'.format(parsing_list[-1])) @@ -47,7 +47,7 @@ def function_check(function_name): if sys.version_info >= (3, 6): return function_dict[function_name]['operator'] else: - return 2 * function_dict['e']['operator'] + return 2 * function_dict['pi']['operator'] elif function_name in function_dict.keys(): return function_name else: diff --git a/final_task/tests/test_check_manager.py b/final_task/tests/test_check_manager.py new file mode 100644 index 00000000..a894e948 --- /dev/null +++ b/final_task/tests/test_check_manager.py @@ -0,0 +1,109 @@ +import unittest +import operator +import math +from pycalc.check_manager import check_expression, number_check, operator_check, function_check + + +class TestCheckManager(unittest.TestCase): + + def test_check_expression_empty(self): + with self.assertRaises(SyntaxError): + check_expression('') + + def test_check_expression_extra_close_bracket(self): + with self.assertRaises(SyntaxError): + check_expression('())') + + def test_check_expression_extra_open_bracket(self): + with self.assertRaises(SyntaxError): + check_expression('(()') + + def test_check_expression_return_expression(self): + expression_line = check_expression('2+3') + self.assertEqual('2+3', expression_line) + + def test_check_parsing_list_start_with_operator_add(self): + parsing_list = check_parsing_list(['+', 42]) + self.assertEqual(['+', 42], parsing_list) + + def test_check_parsing_list_start_with_operator_sub(self): + parsing_list = check_parsing_list(['-', 42]) + self.assertEqual(['-', 42], parsing_list) + + def test_check_parsing_list_start_with_operator_not_add_not_sub(self): + with self.assertRaises(SyntaxError): + check_parsing_list(['*', 2]) + + def test_check_parsing_list_len_is_one_number(self): + parsing_list = check_parsing_list([42]) + self.assertEqual([42], parsing_list) + + def test_check_parsing_list_len_is_one_str(self): + with self.assertRaises(SyntaxError): + check_parsing_list(['+']) + + def test_check_parsing_list_ends_with_operator(self): + with self.assertRaises(SyntaxError): + check_parsing_list([2, '+']) + + def test_check_parsing_list_ends_with_function(self): + with self.assertRaises(SyntaxError): + check_parsing_list([2, 'sin']) + + def test_check_parsing_list_positive(self): + parsing_list = check_parsing_list([42, 'sin', '(', 2, '+', 3.2, ')']) + self.assertEqual([42, 'sin', '(', 2, '+', 3.2, ')'], parsing_list) + + def test_operator_check_opertors_name(self): + operator_str = ['+', '-', '/', '*', '**', '//', '%', '^', '==', '!=', '>', '>=', '<', '<='] + operator_func = [ + operator.add, + operator.sub, + operator.truediv, + operator.mul, + operator.pow, + operator.floordiv, + operator.mod, + operator.pow, + operator.eq, + operator.ne, + operator.gt, + operator.ge, + operator.lt, + operator.le + ] + for i in range(len(operator_str)): + self.assertEqual(operator_str[i], operator_check(operator_str[i])) + self.assertEqual(operator_func[i], operator_dict[operator_str[i]]['operator']) + + def test_operetor_check_negative(self): + with self.assertRaises(SyntaxError): + operator_check('/+') + + def test_numbert_check_int(self): + self.assertTrue(isinstance(number_check('42'), int)) + + def test_number_check_float(self): + self.assertTrue(isinstance(number_check('42.0'), float)) + + def test_function_check_constanta_e(self): + self.assertEqual(math.e, function_check('e')) + + def test_function_check_constanta_pi(self): + self.assertEqual(math.pi, function_check('pi')) + + def test_function_check_constanta_tau(self): + self.assertEqual(math.pi*2, function_check('tau')) + + def test_function_check_function_dict(self): + function_lst = ['abs', 'round'] + for key in math.__dict__.keys(): + if key.startswith('_') or key == 'e' or key == 'pi': + continue + function_lst.append(k) + for i in range(len(function_lst)): + self.assertEqual(function_lst[i], function_check(function_lst[i])) + + def test_function_chekc_not_function(self): + with self.assertRaises(SyntaxError): + function_check('log100') diff --git a/final_task/tests/test_split_operators.py b/final_task/tests/test_split_operators.py index 9706a98e..099e1fc9 100644 --- a/final_task/tests/test_split_operators.py +++ b/final_task/tests/test_split_operators.py @@ -1,5 +1,4 @@ import unittest -from unittest.mock import patch, MagicMock from pycalc.split_operators import SplitOperators @@ -14,16 +13,6 @@ def test_init_class_method(self): self.assertEqual("", expression.last_symbol) self.assertEqual(False, expression.blank_item) - def test_uno_number(self): - expression = SplitOperators('1') - result = expression.split_operators() - self.assertEqual([1], result) - - def test_twice_number(self): - expression = SplitOperators('12') - result = expression.split_operators() - self.assertEqual([12], result) - def test_symbol_append_to_parsing_list(self): expression = SplitOperators('') expression.last_symbol = '*' @@ -32,61 +21,61 @@ def test_symbol_append_to_parsing_list(self): self.assertEqual('', expression.last_symbol) def test_number_append_to_parsing_list(self): - expression = SplitOperators('12') - expression.last_number = '12' + expression = SplitOperators('42') + expression.last_number = '42' expression._append_to_parsing_list() - self.assertEqual([12], expression.parsing_list) + self.assertEqual([42], expression.parsing_list) self.assertEqual('', expression.last_number) def test_letter_append_to_parsing_list(self): - expression = SplitOperators('12') + expression = SplitOperators('42') expression.last_letter = 'sin' expression._append_to_parsing_list() self.assertEqual(['sin'], expression.parsing_list) self.assertEqual('', expression.last_letter) def test_number_parser_with_symbol(self): - expression = SplitOperators('12') + expression = SplitOperators('42') expression.last_symbol = '+' expression._number_parser('2') self.assertEqual(['+'], expression.parsing_list) self.assertEqual('2', expression.last_number) def test_number_parser_with_letter(self): - expression = SplitOperators('12') + expression = SplitOperators('42') expression.last_letter = 'log' expression._number_parser('10') self.assertEqual('log10', expression.last_letter) self.assertEqual('', expression.last_number) def test_number_parser_with_first_number(self): - expression = SplitOperators('12') + expression = SplitOperators('42') expression._number_parser('1') self.assertEqual('1', expression.last_number) self.assertEqual([], expression.parsing_list) def test_number_parser_with_second_number(self): - expression = SplitOperators('12') - expression.last_number = '1' + expression = SplitOperators('42') + expression.last_number = '4' expression._number_parser('2') - self.assertEqual('12', expression.last_number) + self.assertEqual('42', expression.last_number) self.assertEqual([], expression.parsing_list) def test_number_parser_two_comma(self): - expression = SplitOperators('12') + expression = SplitOperators('42') expression.last_number = '1.0' with self.assertRaises(SyntaxError): expression._number_parser('.') def test_number_parser_space_into_operand(self): - expression = SplitOperators('12') + expression = SplitOperators('42') expression.blank_item = True expression.parsing_list.append(1) with self.assertRaises(SyntaxError): expression._number_parser(2) def test_number_parser_blank_item_from_true_to_false(self): - expression = SplitOperators('12') + expression = SplitOperators('42') expression.blank_item = True expression.parsing_list.append('+') expression._number_parser('2') @@ -94,21 +83,21 @@ def test_number_parser_blank_item_from_true_to_false(self): self.assertEqual(['+'], expression.parsing_list) def test_function_parser_with_symbol(self): - expression = SplitOperators('12') + expression = SplitOperators('42') expression.last_symbol = '+' expression._function_parser('s') self.assertEqual(['+'], expression.parsing_list) self.assertEqual('s', expression.last_letter) def test_function_parser_second_letter(self): - expression = SplitOperators('12') + expression = SplitOperators('42') expression.last_letter = 's' expression._function_parser('i') self.assertEqual([], expression.parsing_list) self.assertEqual('si', expression.last_letter) def test_twice_operator_parser_blank_item_from_true_to_false(self): - expression = SplitOperators('12') + expression = SplitOperators('42') expression.blank_item = True expression.parsing_list.append(1) expression._twice_operator_parser('*') @@ -117,49 +106,90 @@ def test_twice_operator_parser_blank_item_from_true_to_false(self): self.assertEqual([1], expression.parsing_list) def test_twice_operator_add_second_symbol(self): - expression = SplitOperators('12') + expression = SplitOperators('42') expression.last_symbol = '*' expression._twice_operator_parser('*') self.assertEqual('**', expression.last_symbol) self.assertEqual([], expression.parsing_list) - # need to think about how to simulate func call - # def test_twice_operator_parser(self): - # expression = SplitOperators('12') - # expression.parsing_list.append(1) - # call_func = SplitOperators._append_to_parsing_list(expression) - # with patch.object(call_func, 'Clear') as mock: - # expression._twice_operator_parser('*') - # call_func.assert_called_once() - # self.assertEqual([1], expression.parsing_list) - def test_twice_operator_parser_wrong_duble_operator(self): - expression = SplitOperators('12') + expression = SplitOperators('42') expression.last_symbol = '**' with self.assertRaises(SyntaxError): expression._twice_operator_parser('*') def test_twice_operator_parser_space_into_operator(self): - expression = SplitOperators('12') + expression = SplitOperators('42') expression.blank_item = True expression.parsing_list.append('*') with self.assertRaises(SyntaxError): expression._twice_operator_parser('*') - # need to think about how to simulate func call def test_simple_operator(self): - expression = SplitOperators('12') + expression = SplitOperators('42') expression._simple_operator_bracket_parser('/') self.assertEqual(['/'], expression.parsing_list) - # need to think about how to simulate func call def test_simple_operator_add_bracke(self): - expression = SplitOperators('12') + expression = SplitOperators('42') expression._simple_operator_bracket_parser('(') self.assertEqual(['('], expression.parsing_list) - # need to think about how to simulate func call def test_simple_operator_blank_symbol(self): - expression = SplitOperators('12') + expression = SplitOperators('42') expression._simple_operator_bracket_parser(' ') self.assertEqual([], expression.parsing_list) + + def test_split_operators_number_and_blank_i(self): + expression = SplitOperators('42 +') + expression.split_operators() + self.assertEqual(True, expression.blank_item) + self.assertEqual([42, '+'], expression.parsing_list) + self.assertEqual('', expression.last_number) + self.assertEqual('', expression.last_letter) + self.assertEqual('', expression.last_symbol) + + def test_split_operators_number(self): + expression = SplitOperators('42') + expression.split_operators() + self.assertEqual('', expression.last_number) + self.assertEqual([42], expression.parsing_list) + self.assertEqual('', expression.last_letter) + self.assertEqual('', expression.last_symbol) + + def test_split_operators_function(self): + expression = SplitOperators('sin') + expression.split_operators() + self.assertEqual(['sin'], expression.parsing_list) + self.assertEqual('', expression.last_number) + self.assertEqual('', expression.last_letter) + self.assertEqual('', expression.last_symbol) + + def test_split_operators_twice_operator_one_symbol(self): + expression = SplitOperators('42/7') + expression.split_operators() + self.assertEqual([42, '/', 7], expression.parsing_list) + self.assertEqual('', expression.last_number) + self.assertEqual('', expression.last_letter) + self.assertEqual('', expression.last_symbol) + + def test_split_operators_twice_operator_two_symbol(self): + expression = SplitOperators('42//7') + expression.split_operators() + self.assertEqual([42, '//', 7], expression.parsing_list) + self.assertEqual('', expression.last_number) + self.assertEqual('', expression.last_letter) + self.assertEqual('', expression.last_symbol) + + def test_split_operators_brackets(self): + expression = SplitOperators('(42)') + expression.split_operators() + self.assertEqual(['(', 42, ')'], expression.parsing_list) + self.assertEqual('', expression.last_number) + self.assertEqual('', expression.last_letter) + self.assertEqual('', expression.last_symbol) + + def test_split_operators_extra_operator(self): + expression = SplitOperators('42//') + with self.assertRaises(SyntaxError): + expression.split_operators() From e5b71545d42719ec8595f1d6aa55c376e96ea4f8 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Sun, 19 May 2019 23:49:04 +0300 Subject: [PATCH 051/103] fix: in test_check_manager add import check_parsing_list, operator_dict and fucntion_dict --- final_task/tests/test_check_manager.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/final_task/tests/test_check_manager.py b/final_task/tests/test_check_manager.py index a894e948..bc7ad8cc 100644 --- a/final_task/tests/test_check_manager.py +++ b/final_task/tests/test_check_manager.py @@ -1,7 +1,9 @@ import unittest import operator import math -from pycalc.check_manager import check_expression, number_check, operator_check, function_check +from pycalc.check_manager import check_expression, number_check, operator_check, \ + function_check, check_parsing_list +from pycalc.operator_manager import operator_dict, function_dict class TestCheckManager(unittest.TestCase): @@ -100,7 +102,7 @@ def test_function_check_function_dict(self): for key in math.__dict__.keys(): if key.startswith('_') or key == 'e' or key == 'pi': continue - function_lst.append(k) + function_lst.append(key) for i in range(len(function_lst)): self.assertEqual(function_lst[i], function_check(function_lst[i])) From dadeec91ab4b27ebf277de10d9e5cf80f0a68aee Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Mon, 20 May 2019 00:07:01 +0300 Subject: [PATCH 052/103] fix: add check constant tau in test_function_check_function_dict; feat: add unittest for module stack_manager --- final_task/tests/test_check_manager.py | 2 +- final_task/tests/test_stack.py | 33 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 final_task/tests/test_stack.py diff --git a/final_task/tests/test_check_manager.py b/final_task/tests/test_check_manager.py index bc7ad8cc..68a30740 100644 --- a/final_task/tests/test_check_manager.py +++ b/final_task/tests/test_check_manager.py @@ -100,7 +100,7 @@ def test_function_check_constanta_tau(self): def test_function_check_function_dict(self): function_lst = ['abs', 'round'] for key in math.__dict__.keys(): - if key.startswith('_') or key == 'e' or key == 'pi': + if key.startswith('_') or key == 'e' or key == 'pi' or key == 'tau': continue function_lst.append(key) for i in range(len(function_lst)): diff --git a/final_task/tests/test_stack.py b/final_task/tests/test_stack.py new file mode 100644 index 00000000..259d9a52 --- /dev/null +++ b/final_task/tests/test_stack.py @@ -0,0 +1,33 @@ +import unittest +from pycalc.stack_manager import Stack + + +class TestStack(unittest.TestCase): + + def setUp(self): + self.stack = Stack() + + def test_init_class(self): + self.assertEqual([], self.stack.stack) + + def test_put_on_stack(self): + self.stack.put_on_stack(42) + self.assertEqual([42], self.stack.stack) + + def test_top(self): + self.stack.put_on_stack(42) + self.assertEqual(42, self.stack.top()) + self.assertEqual([42], self.stack.stack) + + def test_take_from_stack(self): + self.stack.put_on_stack(42) + result = self.stack.take_from_stack() + self.assertEqual(42, result) + self.assertEqual([], self.stack.stack) + + def test_is_empty_true(self): + self.assertTrue(self.stack.is_empty()) + + def test_is_empty_false(self): + self.stack.put_on_stack(42) + self.assertFalse(self.stack.is_empty()) From bb1eee889ec30d2972129883496310ef28ecbae1 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Tue, 21 May 2019 00:47:49 +0300 Subject: [PATCH 053/103] feat: add unittest for module converter, refactor: update method function_converter in module converter with replace eilf into else --- final_task/pycalc/converter.py | 6 +- final_task/tests/test_converter.py | 203 ++++++++++++++++++ .../{test_stack.py => test_stack_manager.py} | 0 3 files changed, 207 insertions(+), 2 deletions(-) create mode 100644 final_task/tests/test_converter.py rename final_task/tests/{test_stack.py => test_stack_manager.py} (100%) diff --git a/final_task/pycalc/converter.py b/final_task/pycalc/converter.py index 09da1619..c1fac6ab 100644 --- a/final_task/pycalc/converter.py +++ b/final_task/pycalc/converter.py @@ -46,14 +46,16 @@ def _operator_converter(self, operator_str): def _function_converter(self, function): if self.last_item: - if self.last_item == '-' and self.converted_list[-1] != '(': + if self.last_item == '-' and self.converted_list[-1] != '(' \ + and self.converted_list[-1] != ')': + self._append_to_converted_list( operator_dict['+'], -1, operator_dict['*'], function_dict[function] ) - elif self.last_item == '-' and self.converted_list[-1] in '()': + else: self._append_to_converted_list( -1, operator_dict['*'], diff --git a/final_task/tests/test_converter.py b/final_task/tests/test_converter.py new file mode 100644 index 00000000..dcdca577 --- /dev/null +++ b/final_task/tests/test_converter.py @@ -0,0 +1,203 @@ +import math +import operator +import unittest +from .pycalc.operator_manager import operator_dict, function_dict, unary_dict + + +class TestConverter(unittest.TestCase): + + def test_init_class(self): + converter = Converter([42, '+', 3]) + self.assertEqual([42, '+', 3], converter.parsing_list) + self.assertEqual("", converter.last_item) + self.assertEqual([], converter.converted_list) + + def test_clean_add_sub_operators_minus_into_plus_after_bracket(self): + converter = Converter([42]) + converter.last_item = '--' + converter.converted_list.append('(') + converter._clean_add_sub_operators() + self.assertEqual("", converter.last_item) + + def test_clean_add_sub_operators_minus_into_plus(self): + converter = Converter([42]) + converter.last_item = '--' + converter.converted_list.append(42) + converter._clean_add_sub_operators() + self.assertEqual('+', converter.last_item) + + def test_clean_add_sub_operators_minus_into_minus(self): + converter = Converter([42]) + converter.last_item = '---' + converter.converted_list.append(42) + converter._clean_add_sub_operators() + self.assertEqual('-', converter.last_item) + + def test_append_to_converted_list(self): + converter = Converter([42]) + converter.last_item = '+' + converter._append_to_converted_list(42, '+', 8) + self.assertEqual([42, '+', 8], converter.converted_list) + self.assertEqual("", converter.last_item) + + def test_number_converter_with_minus_after_zero(self): + converter = Converter([42]) + converter.last_item = '-' + converter.converted_list.append(0) + test_number = 42 + converter._number_converter(test_number) + self.assertEqual([0, {'operator': operator.sub, 'priority': 2}, 42], converter.converted_list) + + def test_number_converter_with_minus_after_close_bracket(self): + converter = Converter([42]) + converter.last_item = '-' + converter.converted_list.append(')') + test_number = 42 + converter._number_converter(test_number) + self.assertEqual([')', + {'operator': operator.sub, + 'priority': 4}, + 42], + converter.converted_list) + + def test_number_converter_with_minus_after_operand(self): + converter = Converter([42]) + converter.last_item = '-' + converter.converted_list.append('(') + test_number = 42 + converter._number_converter(test_number) + self.assertEqual(['(', -42], converter.converted_list) + + def test_number_converter_without_minus(self): + converter = Converter([42]) + converter.last_item = '+' + test_number = 42 + converter._number_converter(test_number) + self.assertEqual([42], converter.converted_list) + + def test_operator_converter_collect_all_plus_minus(self): + converter = Converter([42]) + operator_str = '-' + converter._operator_converter(operator_str) + self.assertEqual('-', converter.last_item) + operator_str = '+' + converter._operator_converter(operator_str) + self.assertEqual('-+', converter.last_item) + + def test_operator_converter_add_to_converted_list(self): + converter = Converter([42]) + operator_str = ['/', '*', '**', '//', '%', '^', '==', '!=', '>', '>=', '<', '<='] + operator_func = [ + operator.truediv, + operator.mul, + operator.pow, + operator.floordiv, + operator.mod, + operator.pow, + operator.eq, + operator.ne, + operator.gt, + operator.ge, + operator.lt, + operator.le + ] + converter.converted_list.append([42]) + for i in range(len(operator_str)): + converter._operator_converter(operator_str[i]) + self.assertEqual(operator_func[i], converter.converted_list[1]['operator']) + converter.converted_list.pop() + + def test_operator_converter_raise_exception_after_operator(self): + converter = Converter([42]) + converter.converted_list.append({'operator': operator.truediv}) + operator_str = '/' + with self.assertRaises(SyntaxError): + converter._operator_converter(operator_str) + + def test_function_converter_add_negative_function_after_operand(self): + converter = Converter([42]) + converter.last_item = '-' + converter.converted_list.append(42) + function_str = 'sin' + converter._function_converter(function_str) + self.assertEqual([42, + {'operator': operator.add, 'priority': 4}, + -1, + {'operator': operator.mul, 'priority': 3}, + {'operator': math.sin, 'priority': 0}], + converter.converted_list) + + def test_function_converter_add_negative_function_after_open_bracket(self): + converter = Converter(42) + converter.last_item = '-' + converter.converted_list.append('(') + function_str = 'sin' + converter._function_converter(function_str) + self.assertEqual(['(', + -1, + {'operator': operator.mul, 'priority': 3}, + {'operator': math.sin, 'priority': 0}], + converter.converted_list) + + def test_function_converter_add_negative_function_after_close_bracket(self): + converter = Converter([42]) + converter.last_item = '-' + converter.converted_list.append(')') + function_str = 'sin' + converter._function_converter(function_str) + self.assertEqual([')', + -1, + {'operator': operator.mul, 'priority': 3}, + {'operator': math.sin, 'priority': 0}], + converter.converted_list) + + def test_function_converter_add_positive_function(self): + converter = Converter([42]) + function_str = 'sin' + converter._function_converter(function_str) + self.assertEqual([{'operator': math.sin, 'priority': 0}], converter.converted_list) + + def test_converter_add_zero_into_beginning_with_unary_sub(self): + converter = Converter(['-', 42]) + converter.converter() + self.assertEqual([0, {'operator': operator.sub, 'priority': 2}, 42], + converter.converted_list) + + def test_converter_remove_space(self): + converter = Converter([42,' ']) + converter.converter() + self.assertEqual([42], converter.converted_list) + + def test_converter_append_append_clean_add_operator(self): + converter = Converter([42, '+', '-', '-', 8]) + converter.converter() + self.assertEqual([42, {'operator': operator.add, 'priority': 4}, 8], + converter.converted_list) + + def test_converter_append_int(self): + converter = Converter([42]) + converter.converter() + self.assertEqual([42], converter.converted_list) + + def test_converter_append_float(self): + converter = Converter([42.8]) + converter.converter() + self.assertEqual([42.8], converter.converted_list) + + def test_converter_append_operator(self): + converter = Converter([42, '*', 8]) + converter.converter() + self.assertEqual([42, {'operator': operator.mul, 'priority': 3}, 8], + converter.converted_list) + + def test_converter_append_function(self): + converter = Converter(['sin', '(', 42, ')']) + converter.converter() + self.assertEqual([{'operator': math.sin, 'priority': 0}, '(', 42, ')'], + converter.converted_list) + + def test_converter_append_bracket_with_minus(self): + converter = Converter(['(', '-', 42, ')']) + converter.converter() + self.assertEqual(['(', -42, ')'], + converter.converted_list) diff --git a/final_task/tests/test_stack.py b/final_task/tests/test_stack_manager.py similarity index 100% rename from final_task/tests/test_stack.py rename to final_task/tests/test_stack_manager.py From 4900fae76a8dad04274439448a7f94c7be411f4c Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Tue, 21 May 2019 00:54:34 +0300 Subject: [PATCH 054/103] fix: update import module --- final_task/tests/test_converter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/final_task/tests/test_converter.py b/final_task/tests/test_converter.py index dcdca577..6c63d3cf 100644 --- a/final_task/tests/test_converter.py +++ b/final_task/tests/test_converter.py @@ -1,7 +1,7 @@ import math import operator import unittest -from .pycalc.operator_manager import operator_dict, function_dict, unary_dict +from .pycalc.converter import Converter class TestConverter(unittest.TestCase): From fd104b02e3f5898b182a65b4292d20f4d89262e4 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Tue, 21 May 2019 01:10:45 +0300 Subject: [PATCH 055/103] fix: remove point in import module from pycalc; refactor: delete unuse import in calculator module --- final_task/pycalc/calculator.py | 1 - final_task/tests/test_converter.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/final_task/pycalc/calculator.py b/final_task/pycalc/calculator.py index cf0064a9..d576deb2 100644 --- a/final_task/pycalc/calculator.py +++ b/final_task/pycalc/calculator.py @@ -1,7 +1,6 @@ from .stack_manager import Stack from .operator_manager import operator_dict, function_dict, unary_dict from .split_operators import SplitOperators -from .check_manager import check_expression from .converter import Converter diff --git a/final_task/tests/test_converter.py b/final_task/tests/test_converter.py index 6c63d3cf..6f509c5d 100644 --- a/final_task/tests/test_converter.py +++ b/final_task/tests/test_converter.py @@ -1,7 +1,7 @@ import math import operator import unittest -from .pycalc.converter import Converter +from pycalc.converter import Converter class TestConverter(unittest.TestCase): From fd404abb375b615557ac5a38640c9038b1bc9060 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Tue, 21 May 2019 01:45:02 +0300 Subject: [PATCH 056/103] fix: update method function_converter: return if statment only with !="("; style: add space after comma --- final_task/pycalc/converter.py | 3 +-- final_task/tests/test_converter.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/final_task/pycalc/converter.py b/final_task/pycalc/converter.py index c1fac6ab..7c7f2d6d 100644 --- a/final_task/pycalc/converter.py +++ b/final_task/pycalc/converter.py @@ -46,8 +46,7 @@ def _operator_converter(self, operator_str): def _function_converter(self, function): if self.last_item: - if self.last_item == '-' and self.converted_list[-1] != '(' \ - and self.converted_list[-1] != ')': + if self.last_item == '-' and self.converted_list[-1] != '(': self._append_to_converted_list( operator_dict['+'], diff --git a/final_task/tests/test_converter.py b/final_task/tests/test_converter.py index 6f509c5d..2eb2e297 100644 --- a/final_task/tests/test_converter.py +++ b/final_task/tests/test_converter.py @@ -164,7 +164,7 @@ def test_converter_add_zero_into_beginning_with_unary_sub(self): converter.converted_list) def test_converter_remove_space(self): - converter = Converter([42,' ']) + converter = Converter([42, ' ']) converter.converter() self.assertEqual([42], converter.converted_list) From 006f4033197c2917b0b7ecbdd34539a1f50cdf5c Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Tue, 21 May 2019 02:03:04 +0300 Subject: [PATCH 057/103] fix: upadte unitest for funct i on_converter when we have minus after closed bracket it should be + -1 * function; style: remove empty line in converter module --- final_task/pycalc/converter.py | 1 - final_task/tests/test_converter.py | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/final_task/pycalc/converter.py b/final_task/pycalc/converter.py index 7c7f2d6d..c4720cdb 100644 --- a/final_task/pycalc/converter.py +++ b/final_task/pycalc/converter.py @@ -47,7 +47,6 @@ def _operator_converter(self, operator_str): def _function_converter(self, function): if self.last_item: if self.last_item == '-' and self.converted_list[-1] != '(': - self._append_to_converted_list( operator_dict['+'], -1, diff --git a/final_task/tests/test_converter.py b/final_task/tests/test_converter.py index 2eb2e297..1ed02f3e 100644 --- a/final_task/tests/test_converter.py +++ b/final_task/tests/test_converter.py @@ -146,6 +146,7 @@ def test_function_converter_add_negative_function_after_close_bracket(self): function_str = 'sin' converter._function_converter(function_str) self.assertEqual([')', + {'operator': operator.add, 'priority': 4}, -1, {'operator': operator.mul, 'priority': 3}, {'operator': math.sin, 'priority': 0}], From 3641d09eecd5029066a4d400fca10808501ed5dc Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 22 May 2019 01:29:55 +0300 Subject: [PATCH 058/103] feat: add unittests fot module calculator; update method calc_on_stack with try_except for extra arguments in the functions --- final_task/pycalc/calculator.py | 9 +- final_task/tests/test_calculator.py | 134 ++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+), 4 deletions(-) create mode 100644 final_task/tests/test_calculator.py diff --git a/final_task/pycalc/calculator.py b/final_task/pycalc/calculator.py index d576deb2..95128002 100644 --- a/final_task/pycalc/calculator.py +++ b/final_task/pycalc/calculator.py @@ -20,8 +20,11 @@ def _calc_on_stack(self): if self.func_argument: second_operand = self.operands.take_from_stack() first_operand = self.operands.take_from_stack() - self.current_result = operator_on_stack['operator'](first_operand, second_operand) - self.func_argument = False + try: + self.current_result = operator_on_stack['operator'](first_operand, second_operand) + self.func_argument = False + except TypeError as err: + raise SyntaxError(err) else: first_operand = self.operands.take_from_stack() self.current_result = operator_on_stack['operator'](first_operand) @@ -73,8 +76,6 @@ def calculate(self): break elif self.func_argument: self._calc_on_stack() - else: - self.func_argument = False if len(self.function.stack): self._calc_on_stack() if item is ')' and len(self.function.stack): diff --git a/final_task/tests/test_calculator.py b/final_task/tests/test_calculator.py new file mode 100644 index 00000000..36f1b4e6 --- /dev/null +++ b/final_task/tests/test_calculator.py @@ -0,0 +1,134 @@ +import unittest +import math +import operator +from pycalc.calculator import Calculator + + +class TestCalculator(unittest.TestCase): + + def test_init_class(self): + calc = Calculator('42+pi') + self.assertEqual([42, '+', math.pi], calc.parser) + self.assertEqual([42, {'operator': operator.add, 'priority': 4}, math.pi], + calc.converted_list) + self.assertEqual("", calc.current_result) + self.assertTrue(calc.operands.is_empty()) + self.assertTrue(calc.function.is_empty()) + self.assertEqual(False, calc.func_argument) + self.assertEqual("", calc.current_operator) + + def test_calc_on_stack_function_with_one_argument(self): + calc = Calculator('sin(pi/2)') + calc.function.put_on_stack({'operator': math.sin, 'priority': 0}) + calc.operands.put_on_stack(math.pi/2) + calc._calc_on_stack() + self.assertEqual(1.0, calc.current_result) + + def test_calc_on_stack_function_with_two_arguments(self): + calc = Calculator('log(16,4)') + calc.func_argument = True + calc.function.put_on_stack({'operator': math.log, 'priority': 0}) + calc.operands.put_on_stack(16) + calc.operands.put_on_stack(4) + calc._calc_on_stack() + self.assertEqual(2.0, calc.current_result) + self.assertEqual(False, calc.func_argument) + + def test_calc_on_stack_operator_with_two_operands(self): + calc = Calculator('42/7') + calc.function.put_on_stack({'operator': operator.truediv, 'priority': 3}) + calc.operands.put_on_stack(42) + calc.operands.put_on_stack(7) + calc._calc_on_stack() + self.assertEqual(6, calc.current_result) + + def test_calc_on_stack_operator_with_one_operand(self): + calc = Calculator('-42') + calc.function.put_on_stack({'operator': operator.sub, 'priority': 4}) + calc.operands.put_on_stack(42) + calc._calc_on_stack() + self.assertEqual(-42, calc.current_result) + + def test_calc_on_stack_unary_operator(self): + calc = Calculator('-42') + calc.function.put_on_stack({'operator': operator.sub, 'priority': 2}) + calc.operands.put_on_stack(42) + calc._calc_on_stack() + self.assertEqual(-42, calc.current_result) + + def test_calc_on_stack_open_bracket_at_the_top(self): + calc = Calculator('42') + calc.function.put_on_stack('(') + calc.operands.put_on_stack(42) + calc._calc_on_stack() + self.assertEqual("", calc.current_result) + + def test_clack_on_stack_recursion_function(self): + calc = Calculator('3*2^2+1') + calc.function.stack = [{'operator': operator.mul, 'priority': 3}, + {'operator': operator.pow, 'priority': 1}] + calc.operands.stack = [3, 2, 2] + calc.current_operator = {'operator': operator.add, 'priority': 4} + calc._calc_on_stack() + self.assertEqual(12, calc.current_result) + + def test_calculate_one_operand_int(self): + calc = Calculator('42') + calc.converted_list = [42] + self.assertEqual(42, calc.calculate()) + + def test_calculate_one_operand_float(self): + calc = Calculator('42.42') + calc.converted_list = [42.42] + self.assertEqual(42.42, calc.calculate()) + + def test_calculate_math_operator(self): + calc = Calculator('42+8') + calc.converted_list = [42, {'operator': operator.add, 'priority': 4}, 8] + self.assertEqual(50, calc.calculate()) + + def test_calculate_unary_operator(self): + calc = Calculator('42-8') + calc.converted_list = [42, {'operator': operator.sub, 'priority': 2}, 8] + self.assertEqual(34, calc.calculate()) + + def test_calculate_ad_operator_after_bracket(self): + calc = Calculator('(42-8)/2') + calc.converted_list = ['(', 42, {'operator': operator.sub, 'priority': 4}, 8, ')', + {'operator': operator.truediv, 'priority': 3}, 2] + self.assertEqual(17, calc.calculate()) + + def test_calculate_ad_operator_with_higher_class_priority(self): + calc = Calculator('42-8/2') + calc.converted_list = [42, {'operator': operator.sub, 'priority': 4}, 8, + {'operator': operator.truediv, 'priority': 3}, 2] + self.assertEqual(38, calc.calculate()) + + def test_calculate_ad_operator_power(self): + calc = Calculator('42-8^2') + calc.converted_list = [42, {'operator': operator.sub, 'priority': 4}, 8, + {'operator': operator.pow, 'priority': 1}, 2] + self.assertEqual(-22, calc.calculate()) + + def test_calculate_ad_operator_power_after_power(self): + calc = Calculator('2^4^2') + calc.converted_list = [2, {'operator': operator.pow, 'priority': 1}, 4, + {'operator': operator.pow, 'priority': 1}, 2] + self.assertEqual(65536, calc.calculate()) + + def test_calculate_too_many_arguments(self): + calc = Calculator('sin(pi,45.0)') + calc.converted_list = [{'operator': math.sin, 'priority': 0}, '(', math.pi, ',', + 45.0, ')'] + with self.assertRaises(SyntaxError): + calc.calculate() + + def test_calculate_expression_inside_brackets(self): + calc = Calculator('1+(1-4/2)*3') + calc.converted_list = [ + 1, {'operator': operator.add, 'priority': 4}, '(', 1, + {'operator': operator.sub, 'priority': 4}, 4, + {'operator': operator.truediv, 'priority': 3}, 2, ')', + {'operator': operator.mul, 'priority': 3}, 3 + ] + self.assertEqual(-2, calc.calculate()) From d9ad6a56ac8884897029e41d4b8e95e77a310e83 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 22 May 2019 01:37:16 +0300 Subject: [PATCH 059/103] style: align list parameters --- final_task/tests/test_calculator.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/final_task/tests/test_calculator.py b/final_task/tests/test_calculator.py index 36f1b4e6..fdacd526 100644 --- a/final_task/tests/test_calculator.py +++ b/final_task/tests/test_calculator.py @@ -127,8 +127,8 @@ def test_calculate_expression_inside_brackets(self): calc = Calculator('1+(1-4/2)*3') calc.converted_list = [ 1, {'operator': operator.add, 'priority': 4}, '(', 1, - {'operator': operator.sub, 'priority': 4}, 4, - {'operator': operator.truediv, 'priority': 3}, 2, ')', - {'operator': operator.mul, 'priority': 3}, 3 + {'operator': operator.sub, 'priority': 4}, 4, + {'operator': operator.truediv, 'priority': 3}, 2, ')', + {'operator': operator.mul, 'priority': 3}, 3 ] self.assertEqual(-2, calc.calculate()) From efc3b051717261b3619f73999478ff8d6a556a6a Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 22 May 2019 22:27:01 +0300 Subject: [PATCH 060/103] feat: extract function to creat a dict with math functions; feat: add unittests for operator_maneger and pycalc --- final_task/pycalc/operator_manager.py | 21 +++++++----- final_task/tests/test_operator_manager.py | 23 +++++++++++++ final_task/tests/test_pycalc.py | 39 +++++++++++++++++++++++ 3 files changed, 75 insertions(+), 8 deletions(-) create mode 100644 final_task/tests/test_operator_manager.py create mode 100644 final_task/tests/test_pycalc.py diff --git a/final_task/pycalc/operator_manager.py b/final_task/pycalc/operator_manager.py index f009af35..3fb42c7d 100644 --- a/final_task/pycalc/operator_manager.py +++ b/final_task/pycalc/operator_manager.py @@ -1,15 +1,20 @@ import math import operator -function_dict = { - 'abs': {'operator': abs, 'priority': 0}, - 'round': {'operator': round, 'priority': 0} -} -for k, v in math.__dict__.items(): - if k.startswith('_'): - continue - function_dict[k] = {'operator': v, 'priority': 0} +def create_func_dict(): + func_dict = { + 'abs': {'operator': abs, 'priority': 0}, + 'round': {'operator': round, 'priority': 0} + } + for k, v in math.__dict__.items(): + if k.startswith('_'): + continue + func_dict[k] = {'operator': v, 'priority': 0} + return func_dict + + +function_dict = create_func_dict() operator_dict = { '+': {'operator': operator.add, 'priority': 4}, diff --git a/final_task/tests/test_operator_manager.py b/final_task/tests/test_operator_manager.py new file mode 100644 index 00000000..af9497c6 --- /dev/null +++ b/final_task/tests/test_operator_manager.py @@ -0,0 +1,23 @@ +import unittest +import math + + +def create_func_dict(): + func_dict = { + 'abs': {'operator': abs, 'priority': 0}, + 'round': {'operator': round, 'priority': 0} + } + for k, v in math.__dict__.items(): + if k.startswith('_'): + continue + func_dict[k] = {'operator': v, 'priority': 0} + return func_dict + +class TestOperatorManager(unittest.TestCase): + + def test_create_func_dict(self): + test_func_name = dir(math) + ['round', 'abs'] + check_dict = create_func_dict() + for key in check_dict.keys(): + self.assertTrue(key in test_func_name) + diff --git a/final_task/tests/test_pycalc.py b/final_task/tests/test_pycalc.py new file mode 100644 index 00000000..38d38b04 --- /dev/null +++ b/final_task/tests/test_pycalc.py @@ -0,0 +1,39 @@ +import unittest +# from pycalc.pycalc import main + + +class TestPycalc(unittest.TestCase): + + def test_calc(self): + test_expression = 'round(-2+(tau^2-sin(pi/2)*2+6.5))' + calc = Calculator(test_expression) + self.assertEqual(42, calc.calculate()) + + def test_syntax_error_in_init_class(self): + test_expression = '15*' + with self.assertRaises(SyntaxError): + Calculator(test_expression) + + def test_syntax_error_in_class_method(self): + test_expression = 'sin(pi, 45)' + calc = Calculator(test_expression) + with self.assertRaises(SyntaxError): + calc.calculate() + + def test_zero_division_error(self): + test_expression = '42/0' + calc = Calculator(test_expression) + with self.assertRaises(ZeroDivisionError): + calc.calculate() + + def test_math_error(self): + test_expression = 'sqrt(-10)' + calc = Calculator(test_expression) + with self.assertRaises(ValueError): + calc.calculate() + + def test_overflow_error(self): + test_expression = 'exp(1000.0)' + calc = Calculator(test_expression) + with self.assertRaises(OverflowError): + calc.calculate() From bfb6f745649761a943b39e9707482bf4e682ec82 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 22 May 2019 22:30:58 +0300 Subject: [PATCH 061/103] fix: update imports --- final_task/tests/test_operator_manager.py | 13 +------------ final_task/tests/test_pycalc.py | 2 +- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/final_task/tests/test_operator_manager.py b/final_task/tests/test_operator_manager.py index af9497c6..384ede7d 100644 --- a/final_task/tests/test_operator_manager.py +++ b/final_task/tests/test_operator_manager.py @@ -1,17 +1,6 @@ import unittest import math - - -def create_func_dict(): - func_dict = { - 'abs': {'operator': abs, 'priority': 0}, - 'round': {'operator': round, 'priority': 0} - } - for k, v in math.__dict__.items(): - if k.startswith('_'): - continue - func_dict[k] = {'operator': v, 'priority': 0} - return func_dict +from pycalc.operator_manager import create_func_dict class TestOperatorManager(unittest.TestCase): diff --git a/final_task/tests/test_pycalc.py b/final_task/tests/test_pycalc.py index 38d38b04..aa83f836 100644 --- a/final_task/tests/test_pycalc.py +++ b/final_task/tests/test_pycalc.py @@ -1,5 +1,5 @@ import unittest -# from pycalc.pycalc import main +from pycalc.pycalc import main class TestPycalc(unittest.TestCase): From 0147aa32cb3f63054f3f8a3c8d2eb44266f21561 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 22 May 2019 22:58:07 +0300 Subject: [PATCH 062/103] fix: add import class Calculator --- final_task/tests/test_operator_manager.py | 2 +- final_task/tests/test_pycalc.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/final_task/tests/test_operator_manager.py b/final_task/tests/test_operator_manager.py index 384ede7d..c7741c6e 100644 --- a/final_task/tests/test_operator_manager.py +++ b/final_task/tests/test_operator_manager.py @@ -2,6 +2,7 @@ import math from pycalc.operator_manager import create_func_dict + class TestOperatorManager(unittest.TestCase): def test_create_func_dict(self): @@ -9,4 +10,3 @@ def test_create_func_dict(self): check_dict = create_func_dict() for key in check_dict.keys(): self.assertTrue(key in test_func_name) - diff --git a/final_task/tests/test_pycalc.py b/final_task/tests/test_pycalc.py index aa83f836..0111cf7a 100644 --- a/final_task/tests/test_pycalc.py +++ b/final_task/tests/test_pycalc.py @@ -1,13 +1,14 @@ import unittest from pycalc.pycalc import main +from pycalc.calculator import Calculator class TestPycalc(unittest.TestCase): def test_calc(self): - test_expression = 'round(-2+(tau^2-sin(pi/2)*2+6.5))' - calc = Calculator(test_expression) - self.assertEqual(42, calc.calculate()) + test_expression = 'round(-2+(tau^2-sin(pi/2)*2+6.5))' + calc = Calculator(test_expression) + self.assertEqual(42, calc.calculate()) def test_syntax_error_in_init_class(self): test_expression = '15*' From 107d675c9730ea1e2cdb5187c12b5da5ded30d6c Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Thu, 23 May 2019 01:24:36 +0300 Subject: [PATCH 063/103] style: add description for every functions in the app --- final_task/pycalc/argument_parser.py | 6 +++ final_task/pycalc/calculator.py | 25 +++++++++++++ final_task/pycalc/check_manager.py | 33 +++++++++++++++++ final_task/pycalc/converter.py | 42 ++++++++++++++++++++- final_task/pycalc/operator_manager.py | 13 +++++-- final_task/pycalc/pycalc.py | 6 +++ final_task/pycalc/split_operators.py | 53 ++++++++++++++++++++++++++- final_task/pycalc/stack_manager.py | 20 +++++++++- 8 files changed, 192 insertions(+), 6 deletions(-) diff --git a/final_task/pycalc/argument_parser.py b/final_task/pycalc/argument_parser.py index 7bb33781..3882a489 100644 --- a/final_task/pycalc/argument_parser.py +++ b/final_task/pycalc/argument_parser.py @@ -1,7 +1,13 @@ +"""Argument parser module""" + import argparse def arg_parser(): + """ + This function gather positional arguments from users + :return: expression_line as str + """ parser = argparse.ArgumentParser(description='Pure-python command-line calculator.', prog='pycalc') parser.add_argument( '-m', diff --git a/final_task/pycalc/calculator.py b/final_task/pycalc/calculator.py index 95128002..28756535 100644 --- a/final_task/pycalc/calculator.py +++ b/final_task/pycalc/calculator.py @@ -1,3 +1,5 @@ +"""Calculator module""" + from .stack_manager import Stack from .operator_manager import operator_dict, function_dict, unary_dict from .split_operators import SplitOperators @@ -5,7 +7,16 @@ class Calculator: + """Calculator class""" def __init__(self, expression_line): + """ + Generates an instance of the Calculator class, + take an expression_line from user, + create an instance of the SplitOperators, + create an instance of the Converter, + create a Stack to put all operands, + create a Stack to put all operations + """ self.parser = SplitOperators(expression_line).split_operators() self.converted_list = Converter(self.parser).converter() self.current_result = "" @@ -15,6 +26,14 @@ def __init__(self, expression_line): self.current_operator = "" def _calc_on_stack(self): + """ + Encapsulate function + Takes an item from function_stack and operands from the operands_stack + and performs calculation. + If it possible to make next calculation - make recursion + Returns result of calculation to the current_result. + Raise an exception if there is two many arguments for current function + """ operator_on_stack = self.function.take_from_stack() if operator_on_stack in function_dict.values(): if self.func_argument: @@ -45,6 +64,12 @@ def _calc_on_stack(self): return self.current_result def calculate(self): + """ + For each item in converted_list using Reverse Polish notation, method + check a type and put it on either operands or function stack, + and invokes calc_on_stack method to perform calculation itself. Returns result + of calculation received from calc_on_stack method + """ for item in self.converted_list: if isinstance(item, float) or isinstance(item, int): self.operands.put_on_stack(item) diff --git a/final_task/pycalc/check_manager.py b/final_task/pycalc/check_manager.py index 68030a34..9ae18d00 100644 --- a/final_task/pycalc/check_manager.py +++ b/final_task/pycalc/check_manager.py @@ -1,8 +1,16 @@ +"""Check manager module""" + import sys from .operator_manager import operator_dict, function_dict def check_expression(expression_line): + """ + Check if the expression_line is not empty and the brackets are correct, + otherwise raise an Exception + :param expression_line: string from the user + :return: clear expression line as str + """ if not expression_line: raise SyntaxError('Expression cannot be empty') if expression_line.count('(') < expression_line.count(')'): @@ -13,6 +21,14 @@ def check_expression(expression_line): def check_parsing_list(parsing_list): + """ + Check if the parsing list is valid otherwise raise an Exception for next reasons: + - expression starts with math operators (except "+" and "-") + - there is no operand in the expression line + - expression line ends with math operator or math function + :param parsing_list: list from instance of SplitOperators class + :return: clear parsing_list as str + """ if parsing_list[0] in operator_dict.keys(): if parsing_list[0] is not '+' and parsing_list[0] is not '-': raise SyntaxError('Expression cannot start with "{}"'.format(parsing_list[0])) @@ -28,12 +44,22 @@ def check_parsing_list(parsing_list): def operator_check(operator_symbol): + """ + Check if there is an operator with this symbol in the operators_dict + :param operator_symbol: str from instance of SplitOperators class + :return: clear operator_symbol as str + """ if operator_symbol in operator_dict.keys(): return operator_symbol raise SyntaxError('Typo in math operator!') def number_check(number): + """ + Check if number is int or float + :param number: str from instance of SplitOperators class + :return: number as int or float + """ try: return int(number) except ValueError: @@ -41,6 +67,13 @@ def number_check(number): def function_check(function_name): + """ + Check if function_name is a key in function_dict. + Check the python version to add constant "tau". + If function_name is "pi", "e" or "tau" convert it into float + :param function_name: str from instance of SplitOperators class + :return: float or clear function_name as str + """ if function_name == 'e' or function_name == 'pi': return function_dict[function_name]['operator'] elif function_name == 'tau': diff --git a/final_task/pycalc/converter.py b/final_task/pycalc/converter.py index c4720cdb..13f3bdc5 100644 --- a/final_task/pycalc/converter.py +++ b/final_task/pycalc/converter.py @@ -1,15 +1,27 @@ +"""Converter module""" + from .operator_manager import operator_dict, function_dict, unary_dict from .check_manager import check_parsing_list class Converter: - + """Converter class""" def __init__(self, parsing_list): + """ + Generates an instance of the Converter class, + take an parsing_list as list from instance of SplitOperators class + create an empty list to put the result of converting + """ self.parsing_list = parsing_list self.last_item = "" self.converted_list = [] def _clean_add_sub_operators(self): + """ + Encapsulate function + Take all the successive of "+" and "-" and + :return "-" if count("-") is odd or "+" if count is even + """ if self.last_item.count('-') % 2 == 0: if self.converted_list[-1] == '(': self.last_item = "" @@ -19,10 +31,21 @@ def _clean_add_sub_operators(self): self.last_item = '-' def _append_to_converted_list(self, *args): + """ + Encapsulate function + Append all converted elements into converted_list + and update successive of "+" and "-" to empty value + """ [self.converted_list.append(i) for i in args] self.last_item = "" def _number_converter(self, number): + """ + Encapsulate function + Add unary operator if number is the first element in the parsing_list, + add "-" to number if last_symbol is "-", + otherwise add "+" + """ if self.last_item == '-': if self.converted_list[-1] == 0: self._append_to_converted_list(unary_dict[self.last_item], number) @@ -35,6 +58,11 @@ def _number_converter(self, number): self._append_to_converted_list(number) def _operator_converter(self, operator_str): + """ + Encapsulate function + Convert math operator symbol into dictionary with built-in math operator + Raise an exception if there is no operand between two math operators + """ if operator_str == '-' or operator_str == '+': self.last_item += operator_str else: @@ -45,6 +73,11 @@ def _operator_converter(self, operator_str): self._append_to_converted_list(operator_dict[operator_str]) def _function_converter(self, function): + """ + Encapsulate function + Convert math function name into dictionary with built-in math function + Check necessary of "-" before the math function + """ if self.last_item: if self.last_item == '-' and self.converted_list[-1] != '(': self._append_to_converted_list( @@ -63,6 +96,13 @@ def _function_converter(self, function): self._append_to_converted_list(function_dict[function]) def converter(self): + """ + The main function of the Converter class + Call function to check if the parsing_list is valid + Go thought the parsing_list and call necessary encapsulate function + for every number, operator or function + :return: converted_list + """ check_parsing_list(self.parsing_list) if self.parsing_list[0] in operator_dict.keys(): self.converted_list.append(0) diff --git a/final_task/pycalc/operator_manager.py b/final_task/pycalc/operator_manager.py index 3fb42c7d..db168584 100644 --- a/final_task/pycalc/operator_manager.py +++ b/final_task/pycalc/operator_manager.py @@ -1,16 +1,23 @@ +"""Operators manager module""" + import math import operator def create_func_dict(): + """ + Returns dictionary where keys are the name of functions and constants from module math, + and values are a dictionary {'operator': , 'priority': number} + + """ func_dict = { 'abs': {'operator': abs, 'priority': 0}, 'round': {'operator': round, 'priority': 0} } - for k, v in math.__dict__.items(): - if k.startswith('_'): + for key, value in math.__dict__.items(): + if key.startswith('_'): continue - func_dict[k] = {'operator': v, 'priority': 0} + func_dict[key] = {'operator': value, 'priority': 0} return func_dict diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index c63cca69..e7d9d8b2 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -1,10 +1,16 @@ #!/usr/bin/python3 +"""pycalc module""" from .calculator import Calculator from .argument_parser import arg_parser def main(): + """ + This function take expression_line from users and invokes calculation. Wrapped in try block to catch error + messages while preparing expression and calculating itself + :return: result of calculation of the users expression + """ try: expression_line = arg_parser() calc = Calculator(expression_line) diff --git a/final_task/pycalc/split_operators.py b/final_task/pycalc/split_operators.py index 3e95eec2..759d0453 100644 --- a/final_task/pycalc/split_operators.py +++ b/final_task/pycalc/split_operators.py @@ -1,9 +1,16 @@ +"""Split operators module""" + from .check_manager import check_expression, number_check, operator_check, function_check class SplitOperators: - + """SplitOperators class""" def __init__(self, expression_line): + """ + Generates an instance of the SplitManager class, + take an expression line as string, + create an empty list to put the result of parsing line + """ self.expression_line = expression_line self.parsing_list = [] self.last_number = "" @@ -12,6 +19,12 @@ def __init__(self, expression_line): self.blank_item = False def _append_to_parsing_list(self): + """ + Encapsulate function + Check if there is a number, symbol of the math operator + or the name of the math function; call the check method of each element + and if the element is clear append it to the self.parsing_list + """ if self.last_symbol: self.parsing_list.append(operator_check(self.last_symbol)) self.last_symbol = "" @@ -23,6 +36,14 @@ def _append_to_parsing_list(self): self.last_letter = "" def _number_parser(self, number): + """ + Encapsulate function + Create a number from successive digits in expression line, + raise Exception when there is a space between two digits, + or when there is more than two comma. If there is symbol of math operator + or the name of math function call function to put this item into self.parsing_list + :param number: is one digit from expression_line + """ if self.blank_item and not isinstance(self.parsing_list[-1], str): raise SyntaxError('Blank symbol between two operands!') elif self.blank_item: @@ -38,11 +59,27 @@ def _number_parser(self, number): self.last_number += number def _function_parser(self, letter): + """ + Encapsulate function + Create a name of function from the successive letters. + If there is symbol of math operator call function + to put this item into self.parsing_list + :param letter: is one letter from expression_line + """ if self.last_symbol: self._append_to_parsing_list() self.last_letter += letter def _twice_operator_parser(self, symbol): + """ + Encapsulate function + Create a twice operator symbol from successive math symbols. + Raise an Exception when there is space between two symbols, + or if it try to create math operator with more than two elements + If there is symbol of math operator call function + to put this item into self.parsing_list + :param symbol: is one math symbol from expression_line + """ if len(self.last_symbol) == 2: raise SyntaxError('Invalid operator "{}"'.format(self.last_symbol + symbol)) if self.blank_item and str(self.parsing_list[-1]) in "!=<>/*": @@ -54,11 +91,25 @@ def _twice_operator_parser(self, symbol): self.last_symbol += symbol def _simple_operator_bracket_parser(self, symbol): + """ + Encapsulate function + At fist call function to put data into self.parsing_list if it necessary. + After put current symbol to the self.parsing_list + :param symbol: is one math symbol from expression_line or bracket or comma + """ self._append_to_parsing_list() if symbol != ' ': self.parsing_list.append(symbol) def split_operators(self): + """ + The main function of class SplitOperators + Go thought the expression_line and call necessary encapsulate function + for every digit, letter or symbol. + Raise an Exception when there is a twice operator in the end of line + :return: parsing_list as a list where each element is operand, math symbol + or name of math function + """ if check_expression(self.expression_line): for i in self.expression_line: if i == " ": diff --git a/final_task/pycalc/stack_manager.py b/final_task/pycalc/stack_manager.py index 8632e031..0ecf270f 100644 --- a/final_task/pycalc/stack_manager.py +++ b/final_task/pycalc/stack_manager.py @@ -1,18 +1,36 @@ -class Stack: +"""Stack module""" + +class Stack: + """Stack class""" def __init__(self): + """ + Generates an instance of the Stack object + """ self.stack = list() def put_on_stack(self, item): + """ + Appends an item to the container + """ self.stack.append(item) def top(self): + """ + Returns a last item from stack + """ return self.stack[-1] def take_from_stack(self): + """ + Remove and returns a last item from stack + """ return self.stack.pop() def is_empty(self): + """ + Returns True if no items on a stack, otherwise returns False + """ if len(self.stack) == 0: return True else: From d9e9781449b4da10f842168d42d417d421352fe1 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Mon, 27 May 2019 20:19:21 +0300 Subject: [PATCH 064/103] feat: add users_modules to ar g_parser, update class SplitOperators, Converter by adding one more required argument to init method - functions that give the dict of fucntion; create function find_user_functions in operator_mananeger to make import functions from users modules --- final_task/pycalc/argument_parser.py | 41 ++++++++++++++++++--------- final_task/pycalc/calculator.py | 14 +++++---- final_task/pycalc/check_manager.py | 13 ++++++--- final_task/pycalc/converter.py | 15 +++++----- final_task/pycalc/operator_manager.py | 29 +++++++++++++++---- final_task/pycalc/pycalc.py | 2 +- final_task/pycalc/split_operators.py | 7 +++-- 7 files changed, 81 insertions(+), 40 deletions(-) diff --git a/final_task/pycalc/argument_parser.py b/final_task/pycalc/argument_parser.py index 3882a489..40b9aa6c 100644 --- a/final_task/pycalc/argument_parser.py +++ b/final_task/pycalc/argument_parser.py @@ -1,22 +1,37 @@ """Argument parser module""" import argparse +from collections import namedtuple +from .operator_manager import create_func_dict, find_user_functions def arg_parser(): """ - This function gather positional arguments from users - :return: expression_line as str + This function gather positional arguments from users, + create a function_dict with users and built_in math functions if there is users_modules, + esle create a function_dict only with built_in math functions + :return: line as namedtuple(expression, function_dict) """ - parser = argparse.ArgumentParser(description='Pure-python command-line calculator.', prog='pycalc') + parser = argparse.ArgumentParser( + description='Pure-python command-line calculator.', + prog='pycalc_not_my' + ) parser.add_argument( - '-m', - '--use-modules', - help='additional modules to use', - metavar='MODULE [MODULE ...]' - ) - parser.add_argument( - 'EXPRESSION', help='expression string to calculate' - ) - expression_line = parser.parse_args().EXPRESSION - return expression_line + '-m', + '--use-modules', + help='additional modules to use', + metavar='MODULE [MODULE ...]' + ) + parser.add_argument('EXPRESSION', help='expression string to evaluate') + args = parser.parse_args() + expression = args.EXPRESSION + if args.use_modules: + user_functions = find_user_functions(args.use_modules) + function_dict = create_func_dict(user_functions) + expression_line = namedtuple('expression_line', 'expression functions') + line = expression_line(expression, function_dict) + else: + function_dict = create_func_dict() + expression_line = namedtuple('expression_line', 'expression functions') + line = expression_line(expression, function_dict) + return line diff --git a/final_task/pycalc/calculator.py b/final_task/pycalc/calculator.py index 28756535..36e94901 100644 --- a/final_task/pycalc/calculator.py +++ b/final_task/pycalc/calculator.py @@ -1,14 +1,14 @@ """Calculator module""" from .stack_manager import Stack -from .operator_manager import operator_dict, function_dict, unary_dict +from .operator_manager import operator_dict, unary_dict from .split_operators import SplitOperators from .converter import Converter class Calculator: """Calculator class""" - def __init__(self, expression_line): + def __init__(self, expression, functions): """ Generates an instance of the Calculator class, take an expression_line from user, @@ -17,8 +17,10 @@ def __init__(self, expression_line): create a Stack to put all operands, create a Stack to put all operations """ - self.parser = SplitOperators(expression_line).split_operators() - self.converted_list = Converter(self.parser).converter() + self.expression_line = expression + self.function_dict = functions + self.parser = SplitOperators(self.expression_line, self.function_dict).split_operators() + self.converted_list = Converter(self.parser, self.function_dict).converter() self.current_result = "" self.operands = Stack() self.function = Stack() @@ -35,7 +37,7 @@ def _calc_on_stack(self): Raise an exception if there is two many arguments for current function """ operator_on_stack = self.function.take_from_stack() - if operator_on_stack in function_dict.values(): + if operator_on_stack in self.function_dict.values(): if self.func_argument: second_operand = self.operands.take_from_stack() first_operand = self.operands.take_from_stack() @@ -74,7 +76,7 @@ def calculate(self): if isinstance(item, float) or isinstance(item, int): self.operands.put_on_stack(item) elif item in operator_dict.values() \ - or item in function_dict.values() \ + or item in self.function_dict.values() \ or item in unary_dict.values(): self.current_operator = item if self.function.is_empty(): diff --git a/final_task/pycalc/check_manager.py b/final_task/pycalc/check_manager.py index 9ae18d00..b39100b3 100644 --- a/final_task/pycalc/check_manager.py +++ b/final_task/pycalc/check_manager.py @@ -1,7 +1,7 @@ """Check manager module""" import sys -from .operator_manager import operator_dict, function_dict +from .operator_manager import operator_dict def check_expression(expression_line): @@ -20,13 +20,14 @@ def check_expression(expression_line): return expression_line -def check_parsing_list(parsing_list): +def check_parsing_list(parsing_list, function_dict): """ Check if the parsing list is valid otherwise raise an Exception for next reasons: - expression starts with math operators (except "+" and "-") - there is no operand in the expression line - expression line ends with math operator or math function :param parsing_list: list from instance of SplitOperators class + :param function_dict: dict with all functions {'operator': function, 'priority': 0} :return: clear parsing_list as str """ if parsing_list[0] in operator_dict.keys(): @@ -66,12 +67,13 @@ def number_check(number): return float(number) -def function_check(function_name): +def function_check(function_name, function_dict): """ Check if function_name is a key in function_dict. Check the python version to add constant "tau". If function_name is "pi", "e" or "tau" convert it into float :param function_name: str from instance of SplitOperators class + :param function_dict: dict with all functions {'operator': function, 'priority': 0} :return: float or clear function_name as str """ if function_name == 'e' or function_name == 'pi': @@ -80,8 +82,11 @@ def function_check(function_name): if sys.version_info >= (3, 6): return function_dict[function_name]['operator'] else: - return 2 * function_dict['pi']['operator'] + return 2 * function_dict['e']['operator'] elif function_name in function_dict.keys(): + if isinstance(function_dict[function_name]['operator'], int) \ + or isinstance(function_dict[function_name]['operator'], float): + return function_dict[function_name]['operator'] return function_name else: raise SyntaxError( diff --git a/final_task/pycalc/converter.py b/final_task/pycalc/converter.py index 13f3bdc5..62eb22c3 100644 --- a/final_task/pycalc/converter.py +++ b/final_task/pycalc/converter.py @@ -1,18 +1,19 @@ """Converter module""" -from .operator_manager import operator_dict, function_dict, unary_dict +from .operator_manager import operator_dict, unary_dict from .check_manager import check_parsing_list class Converter: """Converter class""" - def __init__(self, parsing_list): + def __init__(self, parsing_list, functions): """ Generates an instance of the Converter class, take an parsing_list as list from instance of SplitOperators class create an empty list to put the result of converting """ self.parsing_list = parsing_list + self.function_dict = functions self.last_item = "" self.converted_list = [] @@ -84,16 +85,16 @@ def _function_converter(self, function): operator_dict['+'], -1, operator_dict['*'], - function_dict[function] + self.function_dict[function] ) else: self._append_to_converted_list( -1, operator_dict['*'], - function_dict[function] + self.function_dict[function] ) else: - self._append_to_converted_list(function_dict[function]) + self._append_to_converted_list(self.function_dict[function]) def converter(self): """ @@ -103,7 +104,7 @@ def converter(self): for every number, operator or function :return: converted_list """ - check_parsing_list(self.parsing_list) + check_parsing_list(self.parsing_list, self.function_dict) if self.parsing_list[0] in operator_dict.keys(): self.converted_list.append(0) for i in self.parsing_list: @@ -117,7 +118,7 @@ def converter(self): self._number_converter(i) elif i in operator_dict.keys(): self._operator_converter(i) - elif i in function_dict.keys(): + elif i in self.function_dict.keys(): self._function_converter(i) else: if self.last_item: diff --git a/final_task/pycalc/operator_manager.py b/final_task/pycalc/operator_manager.py index db168584..1dadb2cd 100644 --- a/final_task/pycalc/operator_manager.py +++ b/final_task/pycalc/operator_manager.py @@ -2,26 +2,43 @@ import math import operator +import importlib -def create_func_dict(): +def create_func_dict(func_name=None): """ Returns dictionary where keys are the name of functions and constants from module math, and values are a dictionary {'operator': , 'priority': number} - """ func_dict = { - 'abs': {'operator': abs, 'priority': 0}, - 'round': {'operator': round, 'priority': 0} - } + 'abs': {'operator': abs, 'priority': 0}, + 'round': {'operator': round, 'priority': 0} + } for key, value in math.__dict__.items(): if key.startswith('_'): continue func_dict[key] = {'operator': value, 'priority': 0} + if func_name: + for key, value in func_name.items(): + func_dict[key] = {'operator': value, 'priority': 0} return func_dict -function_dict = create_func_dict() +def find_user_functions(module): + """ + Create a dict of functions and constants from user module, + if user module and pycaclc is located in the one package + :param module: name of the user module (optional argument) + :return: dict {function_name: function} + """ + try: + user_module = importlib.import_module('{}'.format(module)) + item = [i for i in dir(user_module) if not i.startswith('_')] + user_functions = {i: user_module.__dict__[i] for i in item} + except ImportError: + raise SyntaxError('There is no module with name {}'.format(module)) + return user_functions + operator_dict = { '+': {'operator': operator.add, 'priority': 4}, diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index e7d9d8b2..fdbe7742 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -13,7 +13,7 @@ def main(): """ try: expression_line = arg_parser() - calc = Calculator(expression_line) + calc = Calculator(expression_line.expression, expression_line.functions) result = calc.calculate() print(result) except SyntaxError as err: diff --git a/final_task/pycalc/split_operators.py b/final_task/pycalc/split_operators.py index 759d0453..6da3b264 100644 --- a/final_task/pycalc/split_operators.py +++ b/final_task/pycalc/split_operators.py @@ -5,13 +5,14 @@ class SplitOperators: """SplitOperators class""" - def __init__(self, expression_line): + def __init__(self, expression, functions): """ Generates an instance of the SplitManager class, take an expression line as string, create an empty list to put the result of parsing line """ - self.expression_line = expression_line + self.expression_line = expression + self.function_dict = functions self.parsing_list = [] self.last_number = "" self.last_letter = "" @@ -32,7 +33,7 @@ def _append_to_parsing_list(self): self.parsing_list.append(number_check(self.last_number)) self.last_number = "" elif self.last_letter: - self.parsing_list.append(function_check(self.last_letter)) + self.parsing_list.append(function_check(self.last_letter, self.function_dict)) self.last_letter = "" def _number_parser(self, number): From 7c225c97582673e4dce14e1b165fd99ab055631e Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Mon, 27 May 2019 20:27:24 +0300 Subject: [PATCH 065/103] style: made four spaces out --- final_task/pycalc/check_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/final_task/pycalc/check_manager.py b/final_task/pycalc/check_manager.py index b39100b3..80f9e7c2 100644 --- a/final_task/pycalc/check_manager.py +++ b/final_task/pycalc/check_manager.py @@ -86,7 +86,7 @@ def function_check(function_name, function_dict): elif function_name in function_dict.keys(): if isinstance(function_dict[function_name]['operator'], int) \ or isinstance(function_dict[function_name]['operator'], float): - return function_dict[function_name]['operator'] + return function_dict[function_name]['operator'] return function_name else: raise SyntaxError( From 1c1b87586d45b6274777f974bf18ec97bf9154b3 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Mon, 27 May 2019 20:36:38 +0300 Subject: [PATCH 066/103] fix: update unit tests incase adding one required arguments during initialization of the app classes --- final_task/tests/test_calculator.py | 40 ++++++++------- final_task/tests/test_check_manager.py | 38 +++++++++------ final_task/tests/test_converter.py | 51 ++++++++++--------- final_task/tests/test_pycalc.py | 15 +++--- final_task/tests/test_split_operators.py | 62 +++++++++++++----------- 5 files changed, 116 insertions(+), 90 deletions(-) diff --git a/final_task/tests/test_calculator.py b/final_task/tests/test_calculator.py index fdacd526..ea881866 100644 --- a/final_task/tests/test_calculator.py +++ b/final_task/tests/test_calculator.py @@ -2,12 +2,16 @@ import math import operator from pycalc.calculator import Calculator +from pycalc.operator_manager import create_func_dict + +function_dict = create_func_dict() class TestCalculator(unittest.TestCase): def test_init_class(self): - calc = Calculator('42+pi') + calc = Calculator('42+pi', function_dict) + self.assertEqual(function_dict, calc.function_dict) self.assertEqual([42, '+', math.pi], calc.parser) self.assertEqual([42, {'operator': operator.add, 'priority': 4}, math.pi], calc.converted_list) @@ -18,14 +22,14 @@ def test_init_class(self): self.assertEqual("", calc.current_operator) def test_calc_on_stack_function_with_one_argument(self): - calc = Calculator('sin(pi/2)') + calc = Calculator('sin(pi/2)', function_dict) calc.function.put_on_stack({'operator': math.sin, 'priority': 0}) calc.operands.put_on_stack(math.pi/2) calc._calc_on_stack() self.assertEqual(1.0, calc.current_result) def test_calc_on_stack_function_with_two_arguments(self): - calc = Calculator('log(16,4)') + calc = Calculator('log(16,4)', function_dict) calc.func_argument = True calc.function.put_on_stack({'operator': math.log, 'priority': 0}) calc.operands.put_on_stack(16) @@ -35,7 +39,7 @@ def test_calc_on_stack_function_with_two_arguments(self): self.assertEqual(False, calc.func_argument) def test_calc_on_stack_operator_with_two_operands(self): - calc = Calculator('42/7') + calc = Calculator('42/7', function_dict) calc.function.put_on_stack({'operator': operator.truediv, 'priority': 3}) calc.operands.put_on_stack(42) calc.operands.put_on_stack(7) @@ -43,28 +47,28 @@ def test_calc_on_stack_operator_with_two_operands(self): self.assertEqual(6, calc.current_result) def test_calc_on_stack_operator_with_one_operand(self): - calc = Calculator('-42') + calc = Calculator('-42', function_dict) calc.function.put_on_stack({'operator': operator.sub, 'priority': 4}) calc.operands.put_on_stack(42) calc._calc_on_stack() self.assertEqual(-42, calc.current_result) def test_calc_on_stack_unary_operator(self): - calc = Calculator('-42') + calc = Calculator('-42', function_dict) calc.function.put_on_stack({'operator': operator.sub, 'priority': 2}) calc.operands.put_on_stack(42) calc._calc_on_stack() self.assertEqual(-42, calc.current_result) def test_calc_on_stack_open_bracket_at_the_top(self): - calc = Calculator('42') + calc = Calculator('42', function_dict) calc.function.put_on_stack('(') calc.operands.put_on_stack(42) calc._calc_on_stack() self.assertEqual("", calc.current_result) def test_clack_on_stack_recursion_function(self): - calc = Calculator('3*2^2+1') + calc = Calculator('3*2^2+1', function_dict) calc.function.stack = [{'operator': operator.mul, 'priority': 3}, {'operator': operator.pow, 'priority': 1}] calc.operands.stack = [3, 2, 2] @@ -73,58 +77,58 @@ def test_clack_on_stack_recursion_function(self): self.assertEqual(12, calc.current_result) def test_calculate_one_operand_int(self): - calc = Calculator('42') + calc = Calculator('42', function_dict) calc.converted_list = [42] self.assertEqual(42, calc.calculate()) def test_calculate_one_operand_float(self): - calc = Calculator('42.42') + calc = Calculator('42.42', function_dict) calc.converted_list = [42.42] self.assertEqual(42.42, calc.calculate()) def test_calculate_math_operator(self): - calc = Calculator('42+8') + calc = Calculator('42+8', function_dict) calc.converted_list = [42, {'operator': operator.add, 'priority': 4}, 8] self.assertEqual(50, calc.calculate()) def test_calculate_unary_operator(self): - calc = Calculator('42-8') + calc = Calculator('42-8', function_dict) calc.converted_list = [42, {'operator': operator.sub, 'priority': 2}, 8] self.assertEqual(34, calc.calculate()) def test_calculate_ad_operator_after_bracket(self): - calc = Calculator('(42-8)/2') + calc = Calculator('(42-8)/2', function_dict) calc.converted_list = ['(', 42, {'operator': operator.sub, 'priority': 4}, 8, ')', {'operator': operator.truediv, 'priority': 3}, 2] self.assertEqual(17, calc.calculate()) def test_calculate_ad_operator_with_higher_class_priority(self): - calc = Calculator('42-8/2') + calc = Calculator('42-8/2', function_dict) calc.converted_list = [42, {'operator': operator.sub, 'priority': 4}, 8, {'operator': operator.truediv, 'priority': 3}, 2] self.assertEqual(38, calc.calculate()) def test_calculate_ad_operator_power(self): - calc = Calculator('42-8^2') + calc = Calculator('42-8^2', function_dict) calc.converted_list = [42, {'operator': operator.sub, 'priority': 4}, 8, {'operator': operator.pow, 'priority': 1}, 2] self.assertEqual(-22, calc.calculate()) def test_calculate_ad_operator_power_after_power(self): - calc = Calculator('2^4^2') + calc = Calculator('2^4^2', function_dict) calc.converted_list = [2, {'operator': operator.pow, 'priority': 1}, 4, {'operator': operator.pow, 'priority': 1}, 2] self.assertEqual(65536, calc.calculate()) def test_calculate_too_many_arguments(self): - calc = Calculator('sin(pi,45.0)') + calc = Calculator('sin(pi,45.0)', function_dict) calc.converted_list = [{'operator': math.sin, 'priority': 0}, '(', math.pi, ',', 45.0, ')'] with self.assertRaises(SyntaxError): calc.calculate() def test_calculate_expression_inside_brackets(self): - calc = Calculator('1+(1-4/2)*3') + calc = Calculator('1+(1-4/2)*3', function_dict) calc.converted_list = [ 1, {'operator': operator.add, 'priority': 4}, '(', 1, {'operator': operator.sub, 'priority': 4}, 4, diff --git a/final_task/tests/test_check_manager.py b/final_task/tests/test_check_manager.py index 68a30740..26c4f9bf 100644 --- a/final_task/tests/test_check_manager.py +++ b/final_task/tests/test_check_manager.py @@ -3,7 +3,9 @@ import math from pycalc.check_manager import check_expression, number_check, operator_check, \ function_check, check_parsing_list -from pycalc.operator_manager import operator_dict, function_dict +from pycalc.operator_manager import operator_dict, create_func_dict + +function_dict = create_func_dict() class TestCheckManager(unittest.TestCase): @@ -25,35 +27,35 @@ def test_check_expression_return_expression(self): self.assertEqual('2+3', expression_line) def test_check_parsing_list_start_with_operator_add(self): - parsing_list = check_parsing_list(['+', 42]) + parsing_list = check_parsing_list(['+', 42], function_dict) self.assertEqual(['+', 42], parsing_list) def test_check_parsing_list_start_with_operator_sub(self): - parsing_list = check_parsing_list(['-', 42]) + parsing_list = check_parsing_list(['-', 42], function_dict) self.assertEqual(['-', 42], parsing_list) def test_check_parsing_list_start_with_operator_not_add_not_sub(self): with self.assertRaises(SyntaxError): - check_parsing_list(['*', 2]) + check_parsing_list(['*', 2], function_dict) def test_check_parsing_list_len_is_one_number(self): - parsing_list = check_parsing_list([42]) + parsing_list = check_parsing_list([42], function_dict) self.assertEqual([42], parsing_list) def test_check_parsing_list_len_is_one_str(self): with self.assertRaises(SyntaxError): - check_parsing_list(['+']) + check_parsing_list(['+'], function_dict) def test_check_parsing_list_ends_with_operator(self): with self.assertRaises(SyntaxError): - check_parsing_list([2, '+']) + check_parsing_list([2, '+'], function_dict) def test_check_parsing_list_ends_with_function(self): with self.assertRaises(SyntaxError): - check_parsing_list([2, 'sin']) + check_parsing_list([2, 'sin'], function_dict) def test_check_parsing_list_positive(self): - parsing_list = check_parsing_list([42, 'sin', '(', 2, '+', 3.2, ')']) + parsing_list = check_parsing_list([42, 'sin', '(', 2, '+', 3.2, ')'], function_dict) self.assertEqual([42, 'sin', '(', 2, '+', 3.2, ')'], parsing_list) def test_operator_check_opertors_name(self): @@ -89,13 +91,13 @@ def test_number_check_float(self): self.assertTrue(isinstance(number_check('42.0'), float)) def test_function_check_constanta_e(self): - self.assertEqual(math.e, function_check('e')) + self.assertEqual(math.e, function_check('e', function_dict)) def test_function_check_constanta_pi(self): - self.assertEqual(math.pi, function_check('pi')) + self.assertEqual(math.pi, function_check('pi', function_dict)) def test_function_check_constanta_tau(self): - self.assertEqual(math.pi*2, function_check('tau')) + self.assertEqual(math.pi*2, function_check('tau', function_dict)) def test_function_check_function_dict(self): function_lst = ['abs', 'round'] @@ -104,8 +106,16 @@ def test_function_check_function_dict(self): continue function_lst.append(key) for i in range(len(function_lst)): - self.assertEqual(function_lst[i], function_check(function_lst[i])) + self.assertEqual(function_lst[i], function_check(function_lst[i], function_dict)) def test_function_chekc_not_function(self): with self.assertRaises(SyntaxError): - function_check('log100') + function_check('log100', function_dict) + + def test_fucntion_check_user_constant(self): + function_dict['user_constant']['operator'] = 42 + self.assertEqual(42, function_check('user_constant', function_dict)) + + def test_fucntion_check_user_function(self): + function_dict['user_function']['operator'] = 'user_function' + self.assertEqual('user_function', function_check('user_function', function_dict)) diff --git a/final_task/tests/test_converter.py b/final_task/tests/test_converter.py index 1ed02f3e..b2ae082a 100644 --- a/final_task/tests/test_converter.py +++ b/final_task/tests/test_converter.py @@ -2,46 +2,49 @@ import operator import unittest from pycalc.converter import Converter +from pycalc.operator_manager import create_func_dict + +function_dict = create_func_dict() class TestConverter(unittest.TestCase): def test_init_class(self): - converter = Converter([42, '+', 3]) + converter = Converter([42, '+', 3], function_dict) self.assertEqual([42, '+', 3], converter.parsing_list) self.assertEqual("", converter.last_item) self.assertEqual([], converter.converted_list) def test_clean_add_sub_operators_minus_into_plus_after_bracket(self): - converter = Converter([42]) + converter = Converter([42], function_dict) converter.last_item = '--' converter.converted_list.append('(') converter._clean_add_sub_operators() self.assertEqual("", converter.last_item) def test_clean_add_sub_operators_minus_into_plus(self): - converter = Converter([42]) + converter = Converter([42], function_dict) converter.last_item = '--' converter.converted_list.append(42) converter._clean_add_sub_operators() self.assertEqual('+', converter.last_item) def test_clean_add_sub_operators_minus_into_minus(self): - converter = Converter([42]) + converter = Converter([42], function_dict) converter.last_item = '---' converter.converted_list.append(42) converter._clean_add_sub_operators() self.assertEqual('-', converter.last_item) def test_append_to_converted_list(self): - converter = Converter([42]) + converter = Converter([42], function_dict) converter.last_item = '+' converter._append_to_converted_list(42, '+', 8) self.assertEqual([42, '+', 8], converter.converted_list) self.assertEqual("", converter.last_item) def test_number_converter_with_minus_after_zero(self): - converter = Converter([42]) + converter = Converter([42], function_dict) converter.last_item = '-' converter.converted_list.append(0) test_number = 42 @@ -49,7 +52,7 @@ def test_number_converter_with_minus_after_zero(self): self.assertEqual([0, {'operator': operator.sub, 'priority': 2}, 42], converter.converted_list) def test_number_converter_with_minus_after_close_bracket(self): - converter = Converter([42]) + converter = Converter([42], function_dict) converter.last_item = '-' converter.converted_list.append(')') test_number = 42 @@ -61,7 +64,7 @@ def test_number_converter_with_minus_after_close_bracket(self): converter.converted_list) def test_number_converter_with_minus_after_operand(self): - converter = Converter([42]) + converter = Converter([42], function_dict) converter.last_item = '-' converter.converted_list.append('(') test_number = 42 @@ -69,14 +72,14 @@ def test_number_converter_with_minus_after_operand(self): self.assertEqual(['(', -42], converter.converted_list) def test_number_converter_without_minus(self): - converter = Converter([42]) + converter = Converter([42], function_dict) converter.last_item = '+' test_number = 42 converter._number_converter(test_number) self.assertEqual([42], converter.converted_list) def test_operator_converter_collect_all_plus_minus(self): - converter = Converter([42]) + converter = Converter([42], function_dict) operator_str = '-' converter._operator_converter(operator_str) self.assertEqual('-', converter.last_item) @@ -85,7 +88,7 @@ def test_operator_converter_collect_all_plus_minus(self): self.assertEqual('-+', converter.last_item) def test_operator_converter_add_to_converted_list(self): - converter = Converter([42]) + converter = Converter([42], function_dict) operator_str = ['/', '*', '**', '//', '%', '^', '==', '!=', '>', '>=', '<', '<='] operator_func = [ operator.truediv, @@ -108,14 +111,14 @@ def test_operator_converter_add_to_converted_list(self): converter.converted_list.pop() def test_operator_converter_raise_exception_after_operator(self): - converter = Converter([42]) + converter = Converter([42], function_dict) converter.converted_list.append({'operator': operator.truediv}) operator_str = '/' with self.assertRaises(SyntaxError): converter._operator_converter(operator_str) def test_function_converter_add_negative_function_after_operand(self): - converter = Converter([42]) + converter = Converter([42], function_dict) converter.last_item = '-' converter.converted_list.append(42) function_str = 'sin' @@ -128,7 +131,7 @@ def test_function_converter_add_negative_function_after_operand(self): converter.converted_list) def test_function_converter_add_negative_function_after_open_bracket(self): - converter = Converter(42) + converter = Converter(42, function_dict) converter.last_item = '-' converter.converted_list.append('(') function_str = 'sin' @@ -140,7 +143,7 @@ def test_function_converter_add_negative_function_after_open_bracket(self): converter.converted_list) def test_function_converter_add_negative_function_after_close_bracket(self): - converter = Converter([42]) + converter = Converter([42], function_dict) converter.last_item = '-' converter.converted_list.append(')') function_str = 'sin' @@ -153,52 +156,52 @@ def test_function_converter_add_negative_function_after_close_bracket(self): converter.converted_list) def test_function_converter_add_positive_function(self): - converter = Converter([42]) + converter = Converter([42], function_dict) function_str = 'sin' converter._function_converter(function_str) self.assertEqual([{'operator': math.sin, 'priority': 0}], converter.converted_list) def test_converter_add_zero_into_beginning_with_unary_sub(self): - converter = Converter(['-', 42]) + converter = Converter(['-', 42], function_dict) converter.converter() self.assertEqual([0, {'operator': operator.sub, 'priority': 2}, 42], converter.converted_list) def test_converter_remove_space(self): - converter = Converter([42, ' ']) + converter = Converter([42, ' '], function_dict) converter.converter() self.assertEqual([42], converter.converted_list) def test_converter_append_append_clean_add_operator(self): - converter = Converter([42, '+', '-', '-', 8]) + converter = Converter([42, '+', '-', '-', 8], function_dict) converter.converter() self.assertEqual([42, {'operator': operator.add, 'priority': 4}, 8], converter.converted_list) def test_converter_append_int(self): - converter = Converter([42]) + converter = Converter([42], function_dict) converter.converter() self.assertEqual([42], converter.converted_list) def test_converter_append_float(self): - converter = Converter([42.8]) + converter = Converter([42.8], function_dict) converter.converter() self.assertEqual([42.8], converter.converted_list) def test_converter_append_operator(self): - converter = Converter([42, '*', 8]) + converter = Converter([42, '*', 8], function_dict) converter.converter() self.assertEqual([42, {'operator': operator.mul, 'priority': 3}, 8], converter.converted_list) def test_converter_append_function(self): - converter = Converter(['sin', '(', 42, ')']) + converter = Converter(['sin', '(', 42, ')'], function_dict) converter.converter() self.assertEqual([{'operator': math.sin, 'priority': 0}, '(', 42, ')'], converter.converted_list) def test_converter_append_bracket_with_minus(self): - converter = Converter(['(', '-', 42, ')']) + converter = Converter(['(', '-', 42, ')'], function_dict) converter.converter() self.assertEqual(['(', -42, ')'], converter.converted_list) diff --git a/final_task/tests/test_pycalc.py b/final_task/tests/test_pycalc.py index 0111cf7a..bab61146 100644 --- a/final_task/tests/test_pycalc.py +++ b/final_task/tests/test_pycalc.py @@ -1,40 +1,43 @@ import unittest from pycalc.pycalc import main from pycalc.calculator import Calculator +from pycalc.operator_manager import create_func_dict + +function_dict = create_func_dict() class TestPycalc(unittest.TestCase): def test_calc(self): test_expression = 'round(-2+(tau^2-sin(pi/2)*2+6.5))' - calc = Calculator(test_expression) + calc = Calculator(test_expression, function_dict) self.assertEqual(42, calc.calculate()) def test_syntax_error_in_init_class(self): test_expression = '15*' with self.assertRaises(SyntaxError): - Calculator(test_expression) + Calculator(test_expression, function_dict) def test_syntax_error_in_class_method(self): test_expression = 'sin(pi, 45)' - calc = Calculator(test_expression) + calc = Calculator(test_expression, function_dict) with self.assertRaises(SyntaxError): calc.calculate() def test_zero_division_error(self): test_expression = '42/0' - calc = Calculator(test_expression) + calc = Calculator(test_expression, function_dict) with self.assertRaises(ZeroDivisionError): calc.calculate() def test_math_error(self): test_expression = 'sqrt(-10)' - calc = Calculator(test_expression) + calc = Calculator(test_expression, function_dict) with self.assertRaises(ValueError): calc.calculate() def test_overflow_error(self): test_expression = 'exp(1000.0)' - calc = Calculator(test_expression) + calc = Calculator(test_expression, function_dict) with self.assertRaises(OverflowError): calc.calculate() diff --git a/final_task/tests/test_split_operators.py b/final_task/tests/test_split_operators.py index 099e1fc9..b94670ba 100644 --- a/final_task/tests/test_split_operators.py +++ b/final_task/tests/test_split_operators.py @@ -1,12 +1,18 @@ import unittest +import math from pycalc.split_operators import SplitOperators +from pycalc.operator_manager import create_func_dict + + +function_dict = create_func_dict() class TestSplitOperators(unittest.TestCase): def test_init_class_method(self): - expression = SplitOperators('1') + expression = SplitOperators('1', {'tan': {'operator': math.tan, 'priority': 0}}) self.assertEqual('1', expression.expression_line) + self.assertEqual({'tan': {'operator': math.tan, 'priority': 0}}, expression.fucntion_dict) self.assertEqual([], expression.parsing_list) self.assertEqual("", expression.last_number) self.assertEqual("", expression.last_letter) @@ -14,68 +20,68 @@ def test_init_class_method(self): self.assertEqual(False, expression.blank_item) def test_symbol_append_to_parsing_list(self): - expression = SplitOperators('') + expression = SplitOperators('', function_dict) expression.last_symbol = '*' expression._append_to_parsing_list() self.assertEqual(['*'], expression.parsing_list) self.assertEqual('', expression.last_symbol) def test_number_append_to_parsing_list(self): - expression = SplitOperators('42') + expression = SplitOperators('42', function_dict) expression.last_number = '42' expression._append_to_parsing_list() self.assertEqual([42], expression.parsing_list) self.assertEqual('', expression.last_number) def test_letter_append_to_parsing_list(self): - expression = SplitOperators('42') + expression = SplitOperators('42', function_dict) expression.last_letter = 'sin' expression._append_to_parsing_list() self.assertEqual(['sin'], expression.parsing_list) self.assertEqual('', expression.last_letter) def test_number_parser_with_symbol(self): - expression = SplitOperators('42') + expression = SplitOperators('42', function_dict) expression.last_symbol = '+' expression._number_parser('2') self.assertEqual(['+'], expression.parsing_list) self.assertEqual('2', expression.last_number) def test_number_parser_with_letter(self): - expression = SplitOperators('42') + expression = SplitOperators('42', function_dict) expression.last_letter = 'log' expression._number_parser('10') self.assertEqual('log10', expression.last_letter) self.assertEqual('', expression.last_number) def test_number_parser_with_first_number(self): - expression = SplitOperators('42') + expression = SplitOperators('42', function_dict) expression._number_parser('1') self.assertEqual('1', expression.last_number) self.assertEqual([], expression.parsing_list) def test_number_parser_with_second_number(self): - expression = SplitOperators('42') + expression = SplitOperators('42', function_dict) expression.last_number = '4' expression._number_parser('2') self.assertEqual('42', expression.last_number) self.assertEqual([], expression.parsing_list) def test_number_parser_two_comma(self): - expression = SplitOperators('42') + expression = SplitOperators('42', function_dict) expression.last_number = '1.0' with self.assertRaises(SyntaxError): expression._number_parser('.') def test_number_parser_space_into_operand(self): - expression = SplitOperators('42') + expression = SplitOperators('42', function_dict) expression.blank_item = True expression.parsing_list.append(1) with self.assertRaises(SyntaxError): expression._number_parser(2) def test_number_parser_blank_item_from_true_to_false(self): - expression = SplitOperators('42') + expression = SplitOperators('42', function_dict) expression.blank_item = True expression.parsing_list.append('+') expression._number_parser('2') @@ -83,21 +89,21 @@ def test_number_parser_blank_item_from_true_to_false(self): self.assertEqual(['+'], expression.parsing_list) def test_function_parser_with_symbol(self): - expression = SplitOperators('42') + expression = SplitOperators('42', function_dict) expression.last_symbol = '+' expression._function_parser('s') self.assertEqual(['+'], expression.parsing_list) self.assertEqual('s', expression.last_letter) def test_function_parser_second_letter(self): - expression = SplitOperators('42') + expression = SplitOperators('42', function_dict) expression.last_letter = 's' expression._function_parser('i') self.assertEqual([], expression.parsing_list) self.assertEqual('si', expression.last_letter) def test_twice_operator_parser_blank_item_from_true_to_false(self): - expression = SplitOperators('42') + expression = SplitOperators('42', function_dict) expression.blank_item = True expression.parsing_list.append(1) expression._twice_operator_parser('*') @@ -106,42 +112,42 @@ def test_twice_operator_parser_blank_item_from_true_to_false(self): self.assertEqual([1], expression.parsing_list) def test_twice_operator_add_second_symbol(self): - expression = SplitOperators('42') + expression = SplitOperators('42', function_dict) expression.last_symbol = '*' expression._twice_operator_parser('*') self.assertEqual('**', expression.last_symbol) self.assertEqual([], expression.parsing_list) def test_twice_operator_parser_wrong_duble_operator(self): - expression = SplitOperators('42') + expression = SplitOperators('42', function_dict) expression.last_symbol = '**' with self.assertRaises(SyntaxError): expression._twice_operator_parser('*') def test_twice_operator_parser_space_into_operator(self): - expression = SplitOperators('42') + expression = SplitOperators('42', function_dict) expression.blank_item = True expression.parsing_list.append('*') with self.assertRaises(SyntaxError): expression._twice_operator_parser('*') def test_simple_operator(self): - expression = SplitOperators('42') + expression = SplitOperators('42', function_dict) expression._simple_operator_bracket_parser('/') self.assertEqual(['/'], expression.parsing_list) - def test_simple_operator_add_bracke(self): - expression = SplitOperators('42') + def test_simple_operator_add_bracket(self): + expression = SplitOperators('42', function_dict) expression._simple_operator_bracket_parser('(') self.assertEqual(['('], expression.parsing_list) def test_simple_operator_blank_symbol(self): - expression = SplitOperators('42') + expression = SplitOperators('42', function_dict) expression._simple_operator_bracket_parser(' ') self.assertEqual([], expression.parsing_list) def test_split_operators_number_and_blank_i(self): - expression = SplitOperators('42 +') + expression = SplitOperators('42 +', function_dict) expression.split_operators() self.assertEqual(True, expression.blank_item) self.assertEqual([42, '+'], expression.parsing_list) @@ -150,7 +156,7 @@ def test_split_operators_number_and_blank_i(self): self.assertEqual('', expression.last_symbol) def test_split_operators_number(self): - expression = SplitOperators('42') + expression = SplitOperators('42', function_dict) expression.split_operators() self.assertEqual('', expression.last_number) self.assertEqual([42], expression.parsing_list) @@ -158,7 +164,7 @@ def test_split_operators_number(self): self.assertEqual('', expression.last_symbol) def test_split_operators_function(self): - expression = SplitOperators('sin') + expression = SplitOperators('sin', function_dict) expression.split_operators() self.assertEqual(['sin'], expression.parsing_list) self.assertEqual('', expression.last_number) @@ -166,7 +172,7 @@ def test_split_operators_function(self): self.assertEqual('', expression.last_symbol) def test_split_operators_twice_operator_one_symbol(self): - expression = SplitOperators('42/7') + expression = SplitOperators('42/7', function_dict) expression.split_operators() self.assertEqual([42, '/', 7], expression.parsing_list) self.assertEqual('', expression.last_number) @@ -174,7 +180,7 @@ def test_split_operators_twice_operator_one_symbol(self): self.assertEqual('', expression.last_symbol) def test_split_operators_twice_operator_two_symbol(self): - expression = SplitOperators('42//7') + expression = SplitOperators('42//7', function_dict) expression.split_operators() self.assertEqual([42, '//', 7], expression.parsing_list) self.assertEqual('', expression.last_number) @@ -182,7 +188,7 @@ def test_split_operators_twice_operator_two_symbol(self): self.assertEqual('', expression.last_symbol) def test_split_operators_brackets(self): - expression = SplitOperators('(42)') + expression = SplitOperators('(42)', function_dict) expression.split_operators() self.assertEqual(['(', 42, ')'], expression.parsing_list) self.assertEqual('', expression.last_number) @@ -190,6 +196,6 @@ def test_split_operators_brackets(self): self.assertEqual('', expression.last_symbol) def test_split_operators_extra_operator(self): - expression = SplitOperators('42//') + expression = SplitOperators('42//', function_dict) with self.assertRaises(SyntaxError): expression.split_operators() From bf531a9a5384db557b18ccebe29925ea885b9c63 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Mon, 27 May 2019 21:02:05 +0300 Subject: [PATCH 067/103] fix: update function_check by removing the excess fist if statment; add "inf" and "NaN" to constants in unitest for function_check; fix tupo in unittest for split_operators --- final_task/pycalc/check_manager.py | 4 +--- final_task/tests/test_check_manager.py | 7 ++++--- final_task/tests/test_split_operators.py | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/final_task/pycalc/check_manager.py b/final_task/pycalc/check_manager.py index 80f9e7c2..89faeba7 100644 --- a/final_task/pycalc/check_manager.py +++ b/final_task/pycalc/check_manager.py @@ -76,9 +76,7 @@ def function_check(function_name, function_dict): :param function_dict: dict with all functions {'operator': function, 'priority': 0} :return: float or clear function_name as str """ - if function_name == 'e' or function_name == 'pi': - return function_dict[function_name]['operator'] - elif function_name == 'tau': + if function_name == 'tau': if sys.version_info >= (3, 6): return function_dict[function_name]['operator'] else: diff --git a/final_task/tests/test_check_manager.py b/final_task/tests/test_check_manager.py index 26c4f9bf..c235aa67 100644 --- a/final_task/tests/test_check_manager.py +++ b/final_task/tests/test_check_manager.py @@ -101,8 +101,9 @@ def test_function_check_constanta_tau(self): def test_function_check_function_dict(self): function_lst = ['abs', 'round'] + constants = ['e', 'pi', 'tau', 'inf', 'nan'] for key in math.__dict__.keys(): - if key.startswith('_') or key == 'e' or key == 'pi' or key == 'tau': + if key.startswith('_') or key in constants: continue function_lst.append(key) for i in range(len(function_lst)): @@ -113,9 +114,9 @@ def test_function_chekc_not_function(self): function_check('log100', function_dict) def test_fucntion_check_user_constant(self): - function_dict['user_constant']['operator'] = 42 + function_dict['user_constant'] = {'operator': 42, 'priority': 0} self.assertEqual(42, function_check('user_constant', function_dict)) def test_fucntion_check_user_function(self): - function_dict['user_function']['operator'] = 'user_function' + function_dict['user_function'] = {'operator': 'user_function', 'priority': 0} self.assertEqual('user_function', function_check('user_function', function_dict)) diff --git a/final_task/tests/test_split_operators.py b/final_task/tests/test_split_operators.py index b94670ba..6210b6ae 100644 --- a/final_task/tests/test_split_operators.py +++ b/final_task/tests/test_split_operators.py @@ -12,7 +12,7 @@ class TestSplitOperators(unittest.TestCase): def test_init_class_method(self): expression = SplitOperators('1', {'tan': {'operator': math.tan, 'priority': 0}}) self.assertEqual('1', expression.expression_line) - self.assertEqual({'tan': {'operator': math.tan, 'priority': 0}}, expression.fucntion_dict) + self.assertEqual({'tan': {'operator': math.tan, 'priority': 0}}, expression.function_dict) self.assertEqual([], expression.parsing_list) self.assertEqual("", expression.last_number) self.assertEqual("", expression.last_letter) From b9b3e6592db28b77fd7e4ece908009f579c2fea3 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Mon, 27 May 2019 21:22:49 +0300 Subject: [PATCH 068/103] feat: add chechikg if there is closed bracket before open bracket --- final_task/pycalc/split_operators.py | 7 ++++++- final_task/tests/test_split_operators.py | 13 ++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/final_task/pycalc/split_operators.py b/final_task/pycalc/split_operators.py index 6da3b264..48423cca 100644 --- a/final_task/pycalc/split_operators.py +++ b/final_task/pycalc/split_operators.py @@ -18,6 +18,7 @@ def __init__(self, expression, functions): self.last_letter = "" self.last_symbol = "" self.blank_item = False + self.brackets = "" def _append_to_parsing_list(self): """ @@ -99,7 +100,11 @@ def _simple_operator_bracket_parser(self, symbol): :param symbol: is one math symbol from expression_line or bracket or comma """ self._append_to_parsing_list() - if symbol != ' ': + if symbol in '()': + self.brackets += symbol + if self.brackets.count(')') > self.brackets.count('('): + raise SyntaxError('Position of brackets is wrong') + elif symbol != ' ': self.parsing_list.append(symbol) def split_operators(self): diff --git a/final_task/tests/test_split_operators.py b/final_task/tests/test_split_operators.py index 6210b6ae..81516592 100644 --- a/final_task/tests/test_split_operators.py +++ b/final_task/tests/test_split_operators.py @@ -18,6 +18,7 @@ def test_init_class_method(self): self.assertEqual("", expression.last_letter) self.assertEqual("", expression.last_symbol) self.assertEqual(False, expression.blank_item) + self.assertEqual("", expression.brackets) def test_symbol_append_to_parsing_list(self): expression = SplitOperators('', function_dict) @@ -136,11 +137,21 @@ def test_simple_operator(self): expression._simple_operator_bracket_parser('/') self.assertEqual(['/'], expression.parsing_list) - def test_simple_operator_add_bracket(self): + def test_simple_operator_add_open_bracket(self): expression = SplitOperators('42', function_dict) expression._simple_operator_bracket_parser('(') self.assertEqual(['('], expression.parsing_list) + def test_simple_operator_add_closed_bracket(self): + expression = SplitOperators('42', function_dict) + expression._simple_operator_bracket_parser(')') + self.assertEqual([')'], expression.parsing_list) + + def test_simple_operator_add_wrong_bracket(self): + expression = SplitOperators('42', function_dict) + with self.assertRaises(SyntaxError): + expression._simple_operator_bracket_parser(')') + def test_simple_operator_blank_symbol(self): expression = SplitOperators('42', function_dict) expression._simple_operator_bracket_parser(' ') From 5969be83149260fba1b40bfa0fe99a42a619f350 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Mon, 27 May 2019 21:28:28 +0300 Subject: [PATCH 069/103] fix: change elif to if statment to append brackets into parsing_list --- final_task/pycalc/split_operators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/final_task/pycalc/split_operators.py b/final_task/pycalc/split_operators.py index 48423cca..108957e2 100644 --- a/final_task/pycalc/split_operators.py +++ b/final_task/pycalc/split_operators.py @@ -104,7 +104,7 @@ def _simple_operator_bracket_parser(self, symbol): self.brackets += symbol if self.brackets.count(')') > self.brackets.count('('): raise SyntaxError('Position of brackets is wrong') - elif symbol != ' ': + if symbol != ' ': self.parsing_list.append(symbol) def split_operators(self): From 9fcc8c462c37103813a07821c4e2e9bdd75d5b1e Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Mon, 27 May 2019 21:33:04 +0300 Subject: [PATCH 070/103] fix: add "(" to self.brackets in the test_simple_operator_add_closed_bracket --- final_task/tests/test_split_operators.py | 1 + 1 file changed, 1 insertion(+) diff --git a/final_task/tests/test_split_operators.py b/final_task/tests/test_split_operators.py index 81516592..be8df08e 100644 --- a/final_task/tests/test_split_operators.py +++ b/final_task/tests/test_split_operators.py @@ -144,6 +144,7 @@ def test_simple_operator_add_open_bracket(self): def test_simple_operator_add_closed_bracket(self): expression = SplitOperators('42', function_dict) + expression.brackets = '(' expression._simple_operator_bracket_parser(')') self.assertEqual([')'], expression.parsing_list) From 352d09cd0f593aa30b6755b071dd44ee785cecc2 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Tue, 28 May 2019 23:55:57 +0300 Subject: [PATCH 071/103] feat: add collect_function_arguments method into class SplitOperators; style: upgrade function documentation --- final_task/pycalc/split_operators.py | 83 +++++++++++++++++++++++++--- 1 file changed, 74 insertions(+), 9 deletions(-) diff --git a/final_task/pycalc/split_operators.py b/final_task/pycalc/split_operators.py index 108957e2..13d4a510 100644 --- a/final_task/pycalc/split_operators.py +++ b/final_task/pycalc/split_operators.py @@ -10,6 +10,17 @@ def __init__(self, expression, functions): Generates an instance of the SplitManager class, take an expression line as string, create an empty list to put the result of parsing line + self.last_number is a variable to generate number from sequence of numbers + self.last_letter is a variable to generate function name from sequence of letters + self.last_symbol is a variable to generate math operator from one or sequence of symbols + self.blank_item is changed into True when we found whitespace + self.brackets is a place to put all brackets to check if the positions of brackets is right + self.arguments_needs is changed into True when we have a math function, + and return into False when we have collected all the arguments for current function + self.function_arguments is a place to put all arguments for current function, + and it become an empty str again when we have collected all the arguments for current function + self.brackets_in_arguments is a place to all brackets inside arguments + to check when the last element of arguments was added """ self.expression_line = expression self.function_dict = functions @@ -19,13 +30,17 @@ def __init__(self, expression, functions): self.last_symbol = "" self.blank_item = False self.brackets = "" + self.arguments_needs = False + self.function_arguments = "" + self.brackets_in_arguments = "" def _append_to_parsing_list(self): """ Encapsulate function Check if there is a number, symbol of the math operator or the name of the math function; call the check method of each element - and if the element is clear append it to the self.parsing_list + and if the element is clear append it to the self.parsing_list. + Change the value of self.arguments_needs if math function was added """ if self.last_symbol: self.parsing_list.append(operator_check(self.last_symbol)) @@ -35,6 +50,8 @@ def _append_to_parsing_list(self): self.last_number = "" elif self.last_letter: self.parsing_list.append(function_check(self.last_letter, self.function_dict)) + if self.parsing_list[-1] in self.function_dict.keys(): + self.arguments_needs = True self.last_letter = "" def _number_parser(self, number): @@ -96,7 +113,9 @@ def _simple_operator_bracket_parser(self, symbol): """ Encapsulate function At fist call function to put data into self.parsing_list if it necessary. - After put current symbol to the self.parsing_list + After put current symbol to the self.parsing_list. + If the first bracket is closed Raise an Exception. + If we need to collect function arguments it add bracket to the self.brackets_in_arguments :param symbol: is one math symbol from expression_line or bracket or comma """ self._append_to_parsing_list() @@ -104,24 +123,70 @@ def _simple_operator_bracket_parser(self, symbol): self.brackets += symbol if self.brackets.count(')') > self.brackets.count('('): raise SyntaxError('Position of brackets is wrong') - if symbol != ' ': + elif self.arguments_needs: + self.brackets_in_arguments += symbol + else: + self.parsing_list.append(symbol) + + elif symbol != ' ': self.parsing_list.append(symbol) + def _collect_function_arguments(self, item): + """ + Encapsulate function + Collect all the arguments for last function in the parsing list. + If the math function have more than one argument it will make + separations of the arguments by "," and check if this split was correct + and it should be separated by comma after (at first calculate one by one arguments). + If argument is an expression this function will add it like a string, + that will be calculated after in recursion way. + All arguments are strings in tuple. + When the last item of the arguments was added it changed self.arguments_need to False, + returns the self.function_arguments and self.brackets_in_the_arguments to empty stings + :param item: is one by one item from expression_line, when we start + to collect function_arguments + """ + if item in '()': + self.brackets_in_arguments += item + if self.brackets_in_arguments.count('(') == self.brackets_in_arguments.count(')'): + current_arguments = tuple(self.function_arguments.split(',')) + need_to_split_arguments = 0 + for i in current_arguments: + if i.count('(') != i.count(')'): + need_to_split_arguments += 0 + else: + need_to_split_arguments += 1 + if need_to_split_arguments: + self.parsing_list.append(tuple(current_arguments)) + else: + self.parsing_list.append(tuple([self.function_arguments])) + self.arguments_needs = False + self.function_arguments = "" + self.brackets_in_arguments = "" + else: + self.function_arguments += item + else: + self.function_arguments += item + def split_operators(self): """ The main function of class SplitOperators Go thought the expression_line and call necessary encapsulate function - for every digit, letter or symbol. + for every digit, letter, symbol or to collect function_arguments Raise an Exception when there is a twice operator in the end of line - :return: parsing_list as a list where each element is operand, math symbol - or name of math function + :return: parsing_list as a list where each element is operand, math symbol, + name of math function or a tuple of function arguments """ if check_expression(self.expression_line): for i in self.expression_line: if i == " ": + if not self.parsing_list: + continue self.blank_item = True self._append_to_parsing_list() - if i.isnumeric() or i == '.': + elif self.arguments_needs: + self._collect_function_arguments(i) + elif i.isnumeric() or i == '.': self._number_parser(i) elif i.isalpha(): self._function_parser(i) @@ -132,6 +197,6 @@ def split_operators(self): if self.last_symbol: raise SyntaxError( 'Extra operator "{}" at the end of the expression!'.format(self.last_symbol) - ) + ) self._append_to_parsing_list() - return self.parsing_list + return self.parsing_list \ No newline at end of file From 7730f181cd37094741488c15b7d22a3fa42f33eb Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 29 May 2019 00:00:01 +0300 Subject: [PATCH 072/103] style: add empty line in the end --- final_task/pycalc/split_operators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/final_task/pycalc/split_operators.py b/final_task/pycalc/split_operators.py index 13d4a510..6babb273 100644 --- a/final_task/pycalc/split_operators.py +++ b/final_task/pycalc/split_operators.py @@ -199,4 +199,4 @@ def split_operators(self): 'Extra operator "{}" at the end of the expression!'.format(self.last_symbol) ) self._append_to_parsing_list() - return self.parsing_list \ No newline at end of file + return self.parsing_list From beb70250fad677b458c5c075162e4ea2c0c863ca Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 29 May 2019 00:44:46 +0300 Subject: [PATCH 073/103] feat: creaer unittests for method collect_function_arguments; update unittests for methods appended_to_parsing_list, init method, split_operators method --- final_task/tests/test_split_operators.py | 67 ++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/final_task/tests/test_split_operators.py b/final_task/tests/test_split_operators.py index be8df08e..687fca4f 100644 --- a/final_task/tests/test_split_operators.py +++ b/final_task/tests/test_split_operators.py @@ -19,6 +19,9 @@ def test_init_class_method(self): self.assertEqual("", expression.last_symbol) self.assertEqual(False, expression.blank_item) self.assertEqual("", expression.brackets) + self.assertEqual(False, expression.arguments_needs) + self.assertEqual("", expression.function_arguments) + self.assertEqual("", expression.brackets_in_arguments) def test_symbol_append_to_parsing_list(self): expression = SplitOperators('', function_dict) @@ -40,6 +43,7 @@ def test_letter_append_to_parsing_list(self): expression._append_to_parsing_list() self.assertEqual(['sin'], expression.parsing_list) self.assertEqual('', expression.last_letter) + self.assertEqual(True, expression.arguments_needs) def test_number_parser_with_symbol(self): expression = SplitOperators('42', function_dict) @@ -158,6 +162,13 @@ def test_simple_operator_blank_symbol(self): expression._simple_operator_bracket_parser(' ') self.assertEqual([], expression.parsing_list) + def test_simple_operator_bracket_for_function_argument(self): + expression = SplitOperators('42', function_dict) + expression.arguments_needs = True + expression._simple_operator_bracket_parser('(') + self.assertEqual('(', expression.brackets_in_arguments) + self.assertEqual(True, expression.arguments_needs) + def test_split_operators_number_and_blank_i(self): expression = SplitOperators('42 +', function_dict) expression.split_operators() @@ -211,3 +222,59 @@ def test_split_operators_extra_operator(self): expression = SplitOperators('42//', function_dict) with self.assertRaises(SyntaxError): expression.split_operators() + + def test_split_operators_with_function_arguments(self): + expression = SplitOperators('log(2, 2*e)', function_dict) + expression.split_operators() + self.assertEqual(['log', ('2', '2*e')], expression.parsing_list) + self.assertEqual('', expression.last_number) + self.assertEqual('', expression.last_letter) + self.assertEqual('', expression.last_symbol) + + def test_split_operators_whitespace_in_the_begining(self): + expression = SplitOperators(' 42', function_dict) + expression.split_operators() + self.assertEqual([42], expression.parsing_list) + self.assertEqual('', expression.last_number) + self.assertEqual('', expression.last_letter) + self.assertEqual('', expression.last_symbol) + + def test_collect_function_arguments_first_add_to_arguments(self): + expression = SplitOperators('42', function_dict) + expression._collect_function_arguments('42') + self.assertEqual('42', expression.function_arguments) + self.assertEqual(True, expression.arguments_needs) + + def test_collect_function_arguments_add_bracket_to_arguments(self): + expression = SplitOperators('42', function_dict) + expression._collect_function_arguments('(') + self.assertEqual('(', expression.function_arguments) + self.assertEqual(True, expression.arguments_needs) + self.assertEqual('(', expression.brackets_in_arguments) + + def test_collect_function_arguments_add_last_bracket_to_arguments_with_split(self): + expression = SplitOperators('42', function_dict) + expression.brackets_in_arguments = '(' + expression.function_arguments = '42, 8' + expression._collect_function_arguments(')') + self.assertEqual(('42', '8'), expression.function_arguments) + self.assertEqual(False, expression.arguments_needs) + self.assertEqual('', expression.brackets_in_arguments) + + def test_collect_function_arguments_add_last_bracket_to_arguments_without_split(self): + expression = SplitOperators('42', function_dict) + expression.brackets_in_arguments = '(' + expression.function_arguments = '42' + expression._collect_function_arguments(')') + self.assertEqual(('42',), expression.function_arguments) + self.assertEqual(False, expression.arguments_needs) + self.assertEqual('', expression.brackets_in_arguments) + + def test_collect_function_arguments_add_arguments_as_functions(self): + expression = SplitOperators('42', function_dict) + expression.brackets_in_arguments = '(' + expression.function_arguments = 'log(42,8)*pi' + expression._collect_function_arguments(')') + self.assertEqual(('log(42,8)*pi',), expression.function_arguments) + self.assertEqual(False, expression.arguments_needs) + self.assertEqual('', expression.brackets_in_arguments) From e976ce0603456df4d64d09cf5b6805e5ff712d70 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 29 May 2019 21:04:39 +0300 Subject: [PATCH 074/103] feat: add logic to calculate function arguments into module calculator; fix: collect last bracket in arguments to the main brackets, return checking the whitespace; style: add aditional docstrings for main functions --- final_task/pycalc/calculator.py | 58 ++++++++++++++-------------- final_task/pycalc/split_operators.py | 12 +++--- 2 files changed, 33 insertions(+), 37 deletions(-) diff --git a/final_task/pycalc/calculator.py b/final_task/pycalc/calculator.py index 36e94901..69edfc91 100644 --- a/final_task/pycalc/calculator.py +++ b/final_task/pycalc/calculator.py @@ -16,6 +16,10 @@ def __init__(self, expression, functions): create an instance of the Converter, create a Stack to put all operands, create a Stack to put all operations + self.current_operator need to check the priority of current_operator + and the operator on stack after one calculation + self.arg_result_lst need to put calculated arguments and to make a tuple + of arguments """ self.expression_line = expression self.function_dict = functions @@ -24,8 +28,8 @@ def __init__(self, expression, functions): self.current_result = "" self.operands = Stack() self.function = Stack() - self.func_argument = False self.current_operator = "" + self.arg_result_lst = [] def _calc_on_stack(self): """ @@ -38,17 +42,11 @@ def _calc_on_stack(self): """ operator_on_stack = self.function.take_from_stack() if operator_on_stack in self.function_dict.values(): - if self.func_argument: - second_operand = self.operands.take_from_stack() - first_operand = self.operands.take_from_stack() - try: - self.current_result = operator_on_stack['operator'](first_operand, second_operand) - self.func_argument = False - except TypeError as err: - raise SyntaxError(err) - else: - first_operand = self.operands.take_from_stack() - self.current_result = operator_on_stack['operator'](first_operand) + func_args = self.operands.take_from_stack() + try: + self.current_result = operator_on_stack['operator'](*func_args) + except TypeError as err: + raise SyntaxError(err) elif operator_on_stack in operator_dict.values() or operator_on_stack in unary_dict.values(): if len(self.operands.stack) == 1: second_operand = self.operands.take_from_stack() @@ -57,8 +55,6 @@ def _calc_on_stack(self): second_operand = self.operands.take_from_stack() first_operand = self.operands.take_from_stack() self.current_result = operator_on_stack['operator'](first_operand, second_operand) - elif operator_on_stack == '(': - return self.current_result self.operands.put_on_stack(self.current_result) if len(self.function.stack) and self.function.top() is not '(': if self.current_operator['priority'] >= self.function.top()['priority']: @@ -69,11 +65,21 @@ def calculate(self): """ For each item in converted_list using Reverse Polish notation, method check a type and put it on either operands or function stack, - and invokes calc_on_stack method to perform calculation itself. Returns result - of calculation received from calc_on_stack method + and invokes calc_on_stack method to perform calculation itself. + If item is tuple (function arguments) generate a new instance of + Calculator to calculate it and after that put results as tuple to + the operand stack. After calculating current arguments arg_result_lst become + an empty list. Returns result of calculation received from calc_on_stack method """ for item in self.converted_list: - if isinstance(item, float) or isinstance(item, int): + if isinstance(item, tuple): + for argument in item: + arg_calculate = Calculator(argument, self.function_dict) + arg_result = arg_calculate.calculate() + self.arg_result_lst.append(arg_result) + self.operands.put_on_stack(tuple(self.arg_result_lst)) + self.arg_result_lst = [] + elif isinstance(item, float) or isinstance(item, int): self.operands.put_on_stack(item) elif item in operator_dict.values() \ or item in self.function_dict.values() \ @@ -96,19 +102,11 @@ def calculate(self): self.function.take_from_stack() else: for i in range(len(self.function.stack)): - if item is ',' and self.function.top() is '(': - if self.func_argument: - raise SyntaxError('This function can have only two arguments') - self.func_argument = True - break - elif self.func_argument: - self._calc_on_stack() - if len(self.function.stack): - self._calc_on_stack() - if item is ')' and len(self.function.stack): - if self.function.top() is '(': - self.function.take_from_stack() - break + self._calc_on_stack() + if item is ')' and not self.function.is_empty(): + if self.function.top() is '(': + self.function.take_from_stack() + break if self.function.is_empty(): self.current_result = self.operands.take_from_stack() elif len(self.function.stack) == 1: diff --git a/final_task/pycalc/split_operators.py b/final_task/pycalc/split_operators.py index 6babb273..1525f4b8 100644 --- a/final_task/pycalc/split_operators.py +++ b/final_task/pycalc/split_operators.py @@ -138,23 +138,23 @@ def _collect_function_arguments(self, item): If the math function have more than one argument it will make separations of the arguments by "," and check if this split was correct and it should be separated by comma after (at first calculate one by one arguments). - If argument is an expression this function will add it like a string, - that will be calculated after in recursion way. + If argument is an expression with embedded functions and arguments (nesting doll) + this function will add it like a string, that will be calculated after in recursion way. All arguments are strings in tuple. When the last item of the arguments was added it changed self.arguments_need to False, returns the self.function_arguments and self.brackets_in_the_arguments to empty stings + and last bracket will be added to self.brackets :param item: is one by one item from expression_line, when we start to collect function_arguments """ if item in '()': self.brackets_in_arguments += item if self.brackets_in_arguments.count('(') == self.brackets_in_arguments.count(')'): + self.brackets += item current_arguments = tuple(self.function_arguments.split(',')) need_to_split_arguments = 0 for i in current_arguments: - if i.count('(') != i.count(')'): - need_to_split_arguments += 0 - else: + if i.count('(') == i.count(')'): need_to_split_arguments += 1 if need_to_split_arguments: self.parsing_list.append(tuple(current_arguments)) @@ -180,8 +180,6 @@ def split_operators(self): if check_expression(self.expression_line): for i in self.expression_line: if i == " ": - if not self.parsing_list: - continue self.blank_item = True self._append_to_parsing_list() elif self.arguments_needs: From 3ad8c0694b35aa97d8bd2831e487c1fbf3687a29 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 29 May 2019 21:37:17 +0300 Subject: [PATCH 075/103] feat: update unittests for module calculator acording new functionality of work whit arguments --- final_task/tests/test_calculator.py | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/final_task/tests/test_calculator.py b/final_task/tests/test_calculator.py index ea881866..22882463 100644 --- a/final_task/tests/test_calculator.py +++ b/final_task/tests/test_calculator.py @@ -11,6 +11,7 @@ class TestCalculator(unittest.TestCase): def test_init_class(self): calc = Calculator('42+pi', function_dict) + self.assertEqual('42+pi', calc.expression_line) self.assertEqual(function_dict, calc.function_dict) self.assertEqual([42, '+', math.pi], calc.parser) self.assertEqual([42, {'operator': operator.add, 'priority': 4}, math.pi], @@ -18,13 +19,13 @@ def test_init_class(self): self.assertEqual("", calc.current_result) self.assertTrue(calc.operands.is_empty()) self.assertTrue(calc.function.is_empty()) - self.assertEqual(False, calc.func_argument) self.assertEqual("", calc.current_operator) + self.assertEqual([], calc.arg_result_lst) def test_calc_on_stack_function_with_one_argument(self): calc = Calculator('sin(pi/2)', function_dict) calc.function.put_on_stack({'operator': math.sin, 'priority': 0}) - calc.operands.put_on_stack(math.pi/2) + calc.operands.put_on_stack((math.pi/2,)) calc._calc_on_stack() self.assertEqual(1.0, calc.current_result) @@ -32,12 +33,18 @@ def test_calc_on_stack_function_with_two_arguments(self): calc = Calculator('log(16,4)', function_dict) calc.func_argument = True calc.function.put_on_stack({'operator': math.log, 'priority': 0}) - calc.operands.put_on_stack(16) - calc.operands.put_on_stack(4) + calc.operands.put_on_stack((16, 4)) calc._calc_on_stack() self.assertEqual(2.0, calc.current_result) self.assertEqual(False, calc.func_argument) + def test_calculate_too_many_arguments(self): + calc = Calculator('sin(pi,42)', function_dict) + calc.function.put_on_stack({'operator': math.sin, 'priority': 0}) + calc.operands.put_on_stack((math.pi, 42)) + with self.assertRaises(SyntaxError): + calc.calculate() + def test_calc_on_stack_operator_with_two_operands(self): calc = Calculator('42/7', function_dict) calc.function.put_on_stack({'operator': operator.truediv, 'priority': 3}) @@ -60,13 +67,6 @@ def test_calc_on_stack_unary_operator(self): calc._calc_on_stack() self.assertEqual(-42, calc.current_result) - def test_calc_on_stack_open_bracket_at_the_top(self): - calc = Calculator('42', function_dict) - calc.function.put_on_stack('(') - calc.operands.put_on_stack(42) - calc._calc_on_stack() - self.assertEqual("", calc.current_result) - def test_clack_on_stack_recursion_function(self): calc = Calculator('3*2^2+1', function_dict) calc.function.stack = [{'operator': operator.mul, 'priority': 3}, @@ -120,13 +120,6 @@ def test_calculate_ad_operator_power_after_power(self): {'operator': operator.pow, 'priority': 1}, 2] self.assertEqual(65536, calc.calculate()) - def test_calculate_too_many_arguments(self): - calc = Calculator('sin(pi,45.0)', function_dict) - calc.converted_list = [{'operator': math.sin, 'priority': 0}, '(', math.pi, ',', - 45.0, ')'] - with self.assertRaises(SyntaxError): - calc.calculate() - def test_calculate_expression_inside_brackets(self): calc = Calculator('1+(1-4/2)*3', function_dict) calc.converted_list = [ From e3d0d6de93fb74a817f990b8a413952d93b83530 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 29 May 2019 21:54:35 +0300 Subject: [PATCH 076/103] fix: update unittests acording functions in modules --- final_task/tests/test_calculator.py | 2 -- final_task/tests/test_split_operators.py | 20 +++++++++----------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/final_task/tests/test_calculator.py b/final_task/tests/test_calculator.py index 22882463..9ad7418f 100644 --- a/final_task/tests/test_calculator.py +++ b/final_task/tests/test_calculator.py @@ -31,12 +31,10 @@ def test_calc_on_stack_function_with_one_argument(self): def test_calc_on_stack_function_with_two_arguments(self): calc = Calculator('log(16,4)', function_dict) - calc.func_argument = True calc.function.put_on_stack({'operator': math.log, 'priority': 0}) calc.operands.put_on_stack((16, 4)) calc._calc_on_stack() self.assertEqual(2.0, calc.current_result) - self.assertEqual(False, calc.func_argument) def test_calculate_too_many_arguments(self): calc = Calculator('sin(pi,42)', function_dict) diff --git a/final_task/tests/test_split_operators.py b/final_task/tests/test_split_operators.py index 687fca4f..cab1a8ae 100644 --- a/final_task/tests/test_split_operators.py +++ b/final_task/tests/test_split_operators.py @@ -231,22 +231,16 @@ def test_split_operators_with_function_arguments(self): self.assertEqual('', expression.last_letter) self.assertEqual('', expression.last_symbol) - def test_split_operators_whitespace_in_the_begining(self): - expression = SplitOperators(' 42', function_dict) - expression.split_operators() - self.assertEqual([42], expression.parsing_list) - self.assertEqual('', expression.last_number) - self.assertEqual('', expression.last_letter) - self.assertEqual('', expression.last_symbol) - def test_collect_function_arguments_first_add_to_arguments(self): expression = SplitOperators('42', function_dict) + expression.arguments_needs = True expression._collect_function_arguments('42') self.assertEqual('42', expression.function_arguments) self.assertEqual(True, expression.arguments_needs) def test_collect_function_arguments_add_bracket_to_arguments(self): expression = SplitOperators('42', function_dict) + expression.arguments_needs = True expression._collect_function_arguments('(') self.assertEqual('(', expression.function_arguments) self.assertEqual(True, expression.arguments_needs) @@ -257,7 +251,8 @@ def test_collect_function_arguments_add_last_bracket_to_arguments_with_split(sel expression.brackets_in_arguments = '(' expression.function_arguments = '42, 8' expression._collect_function_arguments(')') - self.assertEqual(('42', '8'), expression.function_arguments) + self.assertEqual(('42', '8'), expression.parsing_list[-1]) + self.assertEqual((''), expression.function_arguments) self.assertEqual(False, expression.arguments_needs) self.assertEqual('', expression.brackets_in_arguments) @@ -266,7 +261,8 @@ def test_collect_function_arguments_add_last_bracket_to_arguments_without_split( expression.brackets_in_arguments = '(' expression.function_arguments = '42' expression._collect_function_arguments(')') - self.assertEqual(('42',), expression.function_arguments) + self.assertEqual('', expression.function_arguments) + self.assertEqual(('42',), expression.parsing_list[-1]) self.assertEqual(False, expression.arguments_needs) self.assertEqual('', expression.brackets_in_arguments) @@ -275,6 +271,8 @@ def test_collect_function_arguments_add_arguments_as_functions(self): expression.brackets_in_arguments = '(' expression.function_arguments = 'log(42,8)*pi' expression._collect_function_arguments(')') - self.assertEqual(('log(42,8)*pi',), expression.function_arguments) + self.assertEqual(('log(42,8)*pi',), expression.parsing_list[-1]) + self.assertEqual('', expression.function_arguments) self.assertEqual(False, expression.arguments_needs) self.assertEqual('', expression.brackets_in_arguments) + From ed3f7e9c06e89b0b67384608244df5ed02039b93 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 29 May 2019 21:59:12 +0300 Subject: [PATCH 077/103] fix: add whitespace to actula result, remove blank lines in the end of code --- final_task/tests/test_split_operators.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/final_task/tests/test_split_operators.py b/final_task/tests/test_split_operators.py index cab1a8ae..f1ac9307 100644 --- a/final_task/tests/test_split_operators.py +++ b/final_task/tests/test_split_operators.py @@ -251,7 +251,7 @@ def test_collect_function_arguments_add_last_bracket_to_arguments_with_split(sel expression.brackets_in_arguments = '(' expression.function_arguments = '42, 8' expression._collect_function_arguments(')') - self.assertEqual(('42', '8'), expression.parsing_list[-1]) + self.assertEqual(('42', ' 8'), expression.parsing_list[-1]) self.assertEqual((''), expression.function_arguments) self.assertEqual(False, expression.arguments_needs) self.assertEqual('', expression.brackets_in_arguments) @@ -275,4 +275,3 @@ def test_collect_function_arguments_add_arguments_as_functions(self): self.assertEqual('', expression.function_arguments) self.assertEqual(False, expression.arguments_needs) self.assertEqual('', expression.brackets_in_arguments) - From 6a123b5b36d19f8d50b39b33105f0323e3dfdefd Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Thu, 30 May 2019 22:01:21 +0300 Subject: [PATCH 078/103] feat: add unittest with mock for arg_parser function --- final_task/tests/test_pycalc.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/final_task/tests/test_pycalc.py b/final_task/tests/test_pycalc.py index bab61146..c698a107 100644 --- a/final_task/tests/test_pycalc.py +++ b/final_task/tests/test_pycalc.py @@ -1,4 +1,5 @@ import unittest +from mock import patch from pycalc.pycalc import main from pycalc.calculator import Calculator from pycalc.operator_manager import create_func_dict @@ -41,3 +42,10 @@ def test_overflow_error(self): calc = Calculator(test_expression, function_dict) with self.assertRaises(OverflowError): calc.calculate() + + @patch('.argument_parser.arg_parser') + def test_arg_parser_called(self, mock): + main() + self.assertTrue(mock.called) + + From c44cb80f98710484b8ea59347683d0c717475e23 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Thu, 30 May 2019 22:09:43 +0300 Subject: [PATCH 079/103] fix: update path to arg_parser in test_pycalc --- final_task/tests/test_pycalc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/final_task/tests/test_pycalc.py b/final_task/tests/test_pycalc.py index c698a107..1111ef7b 100644 --- a/final_task/tests/test_pycalc.py +++ b/final_task/tests/test_pycalc.py @@ -43,7 +43,7 @@ def test_overflow_error(self): with self.assertRaises(OverflowError): calc.calculate() - @patch('.argument_parser.arg_parser') + @patch('pycalc.argument_parser.arg_parser') def test_arg_parser_called(self, mock): main() self.assertTrue(mock.called) From a4d0cfdcb7ba7e4c84604f0c92acc734613235d0 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Thu, 30 May 2019 22:42:37 +0300 Subject: [PATCH 080/103] fix: upade import for patch in unittest, feat: add posibility to send iterible argument for math functions; style: update docstings for calc_on_stack; correct some typos in arg_parser --- final_task/pycalc/argument_parser.py | 2 +- final_task/pycalc/calculator.py | 6 +++++- final_task/tests/test_pycalc.py | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/final_task/pycalc/argument_parser.py b/final_task/pycalc/argument_parser.py index 40b9aa6c..ededcb7f 100644 --- a/final_task/pycalc/argument_parser.py +++ b/final_task/pycalc/argument_parser.py @@ -14,7 +14,7 @@ def arg_parser(): """ parser = argparse.ArgumentParser( description='Pure-python command-line calculator.', - prog='pycalc_not_my' + prog='pycalc' ) parser.add_argument( '-m', diff --git a/final_task/pycalc/calculator.py b/final_task/pycalc/calculator.py index 69edfc91..48620745 100644 --- a/final_task/pycalc/calculator.py +++ b/final_task/pycalc/calculator.py @@ -36,15 +36,19 @@ def _calc_on_stack(self): Encapsulate function Takes an item from function_stack and operands from the operands_stack and performs calculation. + Try to send arguments to math function as variables, + or if it impossible send an iterable (e.g. for math.fsum), + or Raise an Exception if there is two many arguments for current function If it possible to make next calculation - make recursion Returns result of calculation to the current_result. - Raise an exception if there is two many arguments for current function """ operator_on_stack = self.function.take_from_stack() if operator_on_stack in self.function_dict.values(): func_args = self.operands.take_from_stack() try: self.current_result = operator_on_stack['operator'](*func_args) + except TypeError: + self.current_result = operator_on_stack['operator'](func_args) except TypeError as err: raise SyntaxError(err) elif operator_on_stack in operator_dict.values() or operator_on_stack in unary_dict.values(): diff --git a/final_task/tests/test_pycalc.py b/final_task/tests/test_pycalc.py index 1111ef7b..a6626a52 100644 --- a/final_task/tests/test_pycalc.py +++ b/final_task/tests/test_pycalc.py @@ -1,5 +1,5 @@ import unittest -from mock import patch +from unittest.mock import patch from pycalc.pycalc import main from pycalc.calculator import Calculator from pycalc.operator_manager import create_func_dict From 8df0444da476cd0146a09579b0b8f590675405b9 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Thu, 30 May 2019 22:53:11 +0300 Subject: [PATCH 081/103] fix: add try"/"except to send iterable to math function; delete blank lines in the end of code --- final_task/pycalc/calculator.py | 7 ++++--- final_task/tests/test_pycalc.py | 2 -- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/final_task/pycalc/calculator.py b/final_task/pycalc/calculator.py index 48620745..a613a934 100644 --- a/final_task/pycalc/calculator.py +++ b/final_task/pycalc/calculator.py @@ -48,9 +48,10 @@ def _calc_on_stack(self): try: self.current_result = operator_on_stack['operator'](*func_args) except TypeError: - self.current_result = operator_on_stack['operator'](func_args) - except TypeError as err: - raise SyntaxError(err) + try: + self.current_result = operator_on_stack['operator'](func_args) + except TypeError as err: + raise SyntaxError(err) elif operator_on_stack in operator_dict.values() or operator_on_stack in unary_dict.values(): if len(self.operands.stack) == 1: second_operand = self.operands.take_from_stack() diff --git a/final_task/tests/test_pycalc.py b/final_task/tests/test_pycalc.py index a6626a52..5c705b93 100644 --- a/final_task/tests/test_pycalc.py +++ b/final_task/tests/test_pycalc.py @@ -47,5 +47,3 @@ def test_overflow_error(self): def test_arg_parser_called(self, mock): main() self.assertTrue(mock.called) - - From 374662c26aff45a819e6f81fe328d69d289df1ef Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Thu, 30 May 2019 23:43:09 +0300 Subject: [PATCH 082/103] fix: add MagicMock to unittest of calling arg_parser --- final_task/tests/test_pycalc.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/final_task/tests/test_pycalc.py b/final_task/tests/test_pycalc.py index 5c705b93..72672c13 100644 --- a/final_task/tests/test_pycalc.py +++ b/final_task/tests/test_pycalc.py @@ -1,8 +1,10 @@ import unittest -from unittest.mock import patch +from unittest.mock import patch, MagicMock +from argparse import Namespace from pycalc.pycalc import main from pycalc.calculator import Calculator from pycalc.operator_manager import create_func_dict +from pycalc import ardument_parser function_dict = create_func_dict() @@ -45,5 +47,7 @@ def test_overflow_error(self): @patch('pycalc.argument_parser.arg_parser') def test_arg_parser_called(self, mock): + test_expression = Namespace(EXPRESSION='42*8') + argument_parser.arg_parser = MagicMock(return_value=test_expression) main() self.assertTrue(mock.called) From 5662e5b4e649a5f50cb577912c101e208fe9feeb Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Thu, 30 May 2019 23:47:20 +0300 Subject: [PATCH 083/103] fix: correct typo in module naname --- final_task/tests/test_pycalc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/final_task/tests/test_pycalc.py b/final_task/tests/test_pycalc.py index 72672c13..5923aee0 100644 --- a/final_task/tests/test_pycalc.py +++ b/final_task/tests/test_pycalc.py @@ -4,7 +4,7 @@ from pycalc.pycalc import main from pycalc.calculator import Calculator from pycalc.operator_manager import create_func_dict -from pycalc import ardument_parser +from pycalc import argument_parser function_dict = create_func_dict() From 361f325055c6bae20e64187d5dd188cbbe35826b Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Thu, 30 May 2019 23:53:40 +0300 Subject: [PATCH 084/103] fix: remove mock from unittests --- final_task/tests/test_pycalc.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/final_task/tests/test_pycalc.py b/final_task/tests/test_pycalc.py index 5923aee0..554acec2 100644 --- a/final_task/tests/test_pycalc.py +++ b/final_task/tests/test_pycalc.py @@ -1,10 +1,6 @@ import unittest -from unittest.mock import patch, MagicMock -from argparse import Namespace -from pycalc.pycalc import main from pycalc.calculator import Calculator from pycalc.operator_manager import create_func_dict -from pycalc import argument_parser function_dict = create_func_dict() @@ -44,10 +40,3 @@ def test_overflow_error(self): calc = Calculator(test_expression, function_dict) with self.assertRaises(OverflowError): calc.calculate() - - @patch('pycalc.argument_parser.arg_parser') - def test_arg_parser_called(self, mock): - test_expression = Namespace(EXPRESSION='42*8') - argument_parser.arg_parser = MagicMock(return_value=test_expression) - main() - self.assertTrue(mock.called) From ff1771c4aff82fb3a95a17cc2f32ab18415f3b5a Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Fri, 31 May 2019 00:00:42 +0300 Subject: [PATCH 085/103] fix: add import main function to unittest --- final_task/tests/test_pycalc.py | 1 + 1 file changed, 1 insertion(+) diff --git a/final_task/tests/test_pycalc.py b/final_task/tests/test_pycalc.py index 554acec2..bab61146 100644 --- a/final_task/tests/test_pycalc.py +++ b/final_task/tests/test_pycalc.py @@ -1,4 +1,5 @@ import unittest +from pycalc.pycalc import main from pycalc.calculator import Calculator from pycalc.operator_manager import create_func_dict From b5f09dca4530d7c71deb46ff0d3cd2c81e2f2327 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Mon, 3 Jun 2019 21:02:42 +0300 Subject: [PATCH 086/103] style: add some additional commetns to the docstings --- final_task/pycalc/argument_parser.py | 2 +- final_task/pycalc/check_manager.py | 3 ++- final_task/pycalc/converter.py | 20 +++++++++++++------- final_task/pycalc/operator_manager.py | 2 +- final_task/pycalc/split_operators.py | 4 ++-- 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/final_task/pycalc/argument_parser.py b/final_task/pycalc/argument_parser.py index ededcb7f..0b0a7878 100644 --- a/final_task/pycalc/argument_parser.py +++ b/final_task/pycalc/argument_parser.py @@ -9,7 +9,7 @@ def arg_parser(): """ This function gather positional arguments from users, create a function_dict with users and built_in math functions if there is users_modules, - esle create a function_dict only with built_in math functions + otherwise create a function_dict only with built_in math functions :return: line as namedtuple(expression, function_dict) """ parser = argparse.ArgumentParser( diff --git a/final_task/pycalc/check_manager.py b/final_task/pycalc/check_manager.py index 89faeba7..94f83639 100644 --- a/final_task/pycalc/check_manager.py +++ b/final_task/pycalc/check_manager.py @@ -71,7 +71,8 @@ def function_check(function_name, function_dict): """ Check if function_name is a key in function_dict. Check the python version to add constant "tau". - If function_name is "pi", "e" or "tau" convert it into float + If function_name is "pi", "e", "tau", "inf" or "nan" convert it into float + If there is no such name in function_dict Raise an Exception :param function_name: str from instance of SplitOperators class :param function_dict: dict with all functions {'operator': function, 'priority': 0} :return: float or clear function_name as str diff --git a/final_task/pycalc/converter.py b/final_task/pycalc/converter.py index 62eb22c3..603d176c 100644 --- a/final_task/pycalc/converter.py +++ b/final_task/pycalc/converter.py @@ -11,6 +11,9 @@ def __init__(self, parsing_list, functions): Generates an instance of the Converter class, take an parsing_list as list from instance of SplitOperators class create an empty list to put the result of converting + self.last_item is a place to put all successive "+" and "-" + and after appending any other element into self.converted_list + self.last_item become an empty str """ self.parsing_list = parsing_list self.function_dict = functions @@ -20,8 +23,8 @@ def __init__(self, parsing_list, functions): def _clean_add_sub_operators(self): """ Encapsulate function - Take all the successive of "+" and "-" and - :return "-" if count("-") is odd or "+" if count is even + Take all the successive of "+" and "-" (self.last)item) + and return "-" if count("-") is odd or "+" if count is even """ if self.last_item.count('-') % 2 == 0: if self.converted_list[-1] == '(': @@ -34,7 +37,7 @@ def _clean_add_sub_operators(self): def _append_to_converted_list(self, *args): """ Encapsulate function - Append all converted elements into converted_list + Append all converted elements into self.converted_list and update successive of "+" and "-" to empty value """ [self.converted_list.append(i) for i in args] @@ -43,9 +46,10 @@ def _append_to_converted_list(self, *args): def _number_converter(self, number): """ Encapsulate function - Add unary operator if number is the first element in the parsing_list, - add "-" to number if last_symbol is "-", - otherwise add "+" + Add unary operator if number is the first element in the self.parsing_list, + add "-" to number if self.last_symbol is "-", otherwise add "+" + And if there is no self.last_symbol + :param number: is int or float object from self.parsing_list """ if self.last_item == '-': if self.converted_list[-1] == 0: @@ -63,6 +67,7 @@ def _operator_converter(self, operator_str): Encapsulate function Convert math operator symbol into dictionary with built-in math operator Raise an exception if there is no operand between two math operators + :param operator_str: is one math symbol from self.parsing_list """ if operator_str == '-' or operator_str == '+': self.last_item += operator_str @@ -78,6 +83,7 @@ def _function_converter(self, function): Encapsulate function Convert math function name into dictionary with built-in math function Check necessary of "-" before the math function + :param function_str: is math function name from self.parsing_list """ if self.last_item: if self.last_item == '-' and self.converted_list[-1] != '(': @@ -105,7 +111,7 @@ def converter(self): :return: converted_list """ check_parsing_list(self.parsing_list, self.function_dict) - if self.parsing_list[0] in operator_dict.keys(): + if self.parsing_list[0] in operator_dict.keys(): # parsing_list[0] == '+' or '-' self.converted_list.append(0) for i in self.parsing_list: if i == " ": diff --git a/final_task/pycalc/operator_manager.py b/final_task/pycalc/operator_manager.py index 1dadb2cd..8e259003 100644 --- a/final_task/pycalc/operator_manager.py +++ b/final_task/pycalc/operator_manager.py @@ -28,7 +28,7 @@ def find_user_functions(module): """ Create a dict of functions and constants from user module, if user module and pycaclc is located in the one package - :param module: name of the user module (optional argument) + :param module: name of the user module (optional argument in argparser) :return: dict {function_name: function} """ try: diff --git a/final_task/pycalc/split_operators.py b/final_task/pycalc/split_operators.py index 1525f4b8..b75f0304 100644 --- a/final_task/pycalc/split_operators.py +++ b/final_task/pycalc/split_operators.py @@ -113,7 +113,7 @@ def _simple_operator_bracket_parser(self, symbol): """ Encapsulate function At fist call function to put data into self.parsing_list if it necessary. - After put current symbol to the self.parsing_list. + After this it put current symbol to the self.parsing_list. If the first bracket is closed Raise an Exception. If we need to collect function arguments it add bracket to the self.brackets_in_arguments :param symbol: is one math symbol from expression_line or bracket or comma @@ -137,7 +137,7 @@ def _collect_function_arguments(self, item): Collect all the arguments for last function in the parsing list. If the math function have more than one argument it will make separations of the arguments by "," and check if this split was correct - and it should be separated by comma after (at first calculate one by one arguments). + or it should be separated by comma after (at first calculate one by one arguments). If argument is an expression with embedded functions and arguments (nesting doll) this function will add it like a string, that will be calculated after in recursion way. All arguments are strings in tuple. From 0e661c6bc21acbbc0f5f3cc39e3c31f7d4cc97ed Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Tue, 4 Jun 2019 22:38:48 +0300 Subject: [PATCH 087/103] refactor: replaced three "if" statements in the end of calculate function to "while" cycle; replaced range(len(self.function.stack)) to self.function.stack in "for" cycle and "not self.function.is_empty" to "self.fucntion.stack"; style: update some docstrings in check_manager module --- final_task/pycalc/calculator.py | 16 +++++----------- final_task/pycalc/check_manager.py | 6 +++--- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/final_task/pycalc/calculator.py b/final_task/pycalc/calculator.py index a613a934..fe5420f0 100644 --- a/final_task/pycalc/calculator.py +++ b/final_task/pycalc/calculator.py @@ -61,7 +61,7 @@ def _calc_on_stack(self): first_operand = self.operands.take_from_stack() self.current_result = operator_on_stack['operator'](first_operand, second_operand) self.operands.put_on_stack(self.current_result) - if len(self.function.stack) and self.function.top() is not '(': + if self.function.stack and self.function.top() is not '(': if self.current_operator['priority'] >= self.function.top()['priority']: self.current_result = self._calc_on_stack() return self.current_result @@ -106,19 +106,13 @@ def calculate(self): elif item is ')' and self.function.top() == '(': self.function.take_from_stack() else: - for i in range(len(self.function.stack)): + for i in self.function.stack: self._calc_on_stack() - if item is ')' and not self.function.is_empty(): + if item is ')' and self.function.stack: if self.function.top() is '(': self.function.take_from_stack() break - if self.function.is_empty(): - self.current_result = self.operands.take_from_stack() - elif len(self.function.stack) == 1: + while self.function.stack: self._calc_on_stack() - else: - for i in range(len(self.function.stack)): - self._calc_on_stack() - if self.function.is_empty(): - break + self.current_result = self.operands.take_from_stack() return self.current_result diff --git a/final_task/pycalc/check_manager.py b/final_task/pycalc/check_manager.py index 94f83639..14523797 100644 --- a/final_task/pycalc/check_manager.py +++ b/final_task/pycalc/check_manager.py @@ -47,7 +47,7 @@ def check_parsing_list(parsing_list, function_dict): def operator_check(operator_symbol): """ Check if there is an operator with this symbol in the operators_dict - :param operator_symbol: str from instance of SplitOperators class + :param operator_symbol: last_symbol (str) from instance of SplitOperators class :return: clear operator_symbol as str """ if operator_symbol in operator_dict.keys(): @@ -58,7 +58,7 @@ def operator_check(operator_symbol): def number_check(number): """ Check if number is int or float - :param number: str from instance of SplitOperators class + :param number: last_number (str) from instance of SplitOperators class :return: number as int or float """ try: @@ -73,7 +73,7 @@ def function_check(function_name, function_dict): Check the python version to add constant "tau". If function_name is "pi", "e", "tau", "inf" or "nan" convert it into float If there is no such name in function_dict Raise an Exception - :param function_name: str from instance of SplitOperators class + :param function_name: last_letter (str) from instance of SplitOperators class :param function_dict: dict with all functions {'operator': function, 'priority': 0} :return: float or clear function_name as str """ From 2bff82d5636998052715c4c899ca92842ac4a52f Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Tue, 4 Jun 2019 22:41:54 +0300 Subject: [PATCH 088/103] feat: update FUNCIONS_AND_CONSTANTS tests with iteriable arguments "fsum([1,2,3,4])"; update ERROR_CASES test with wrong position of brackets (closed brackets is first) --- __init__.py | 0 pycalc_checker.py | 2 ++ 2 files changed, 2 insertions(+) create mode 100644 __init__.py diff --git a/__init__.py b/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pycalc_checker.py b/pycalc_checker.py index c1696c84..bf2cb9ad 100644 --- a/pycalc_checker.py +++ b/pycalc_checker.py @@ -37,6 +37,7 @@ "pow(2, 3)": pow(2, 3), "abs(-5)": abs(-5), "round(123.4567890)": round(123.4567890), + "fsum([1,2,3,4])": fsum([1,2,3,4]), } ASSOCIATIVE = { @@ -92,6 +93,7 @@ "2+/3", "/2", "2***2", + "sin)pi(", ] From d531321e792493d90c3c11d2bdce5d5fe09f1801 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Tue, 4 Jun 2019 23:11:24 +0300 Subject: [PATCH 089/103] feat: add unittest for math function with iterable arguments in test_calculato --- final_task/tests/test_calculator.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/final_task/tests/test_calculator.py b/final_task/tests/test_calculator.py index 9ad7418f..44b42603 100644 --- a/final_task/tests/test_calculator.py +++ b/final_task/tests/test_calculator.py @@ -36,6 +36,13 @@ def test_calc_on_stack_function_with_two_arguments(self): calc._calc_on_stack() self.assertEqual(2.0, calc.current_result) + def test_calc_on_stack_function_with_iterable_argument(self): + calc = Calculator('fsum([1,2,3,4])', function_dict) + calc.function.put_on_stack({'operator': math.fsum, 'priority': 0}) + calc.operands.put_on_stack([1, 2, 3, 4]) + calc._calc_on_stack + self.assertEqual(10.0, calc.current_result) + def test_calculate_too_many_arguments(self): calc = Calculator('sin(pi,42)', function_dict) calc.function.put_on_stack({'operator': math.sin, 'priority': 0}) From a72cf151088784feb4d4d2b27401d518be0e9b04 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Tue, 4 Jun 2019 23:19:30 +0300 Subject: [PATCH 090/103] fix: add brackets "()" to call _calc_on_stack function in test_calculator --- final_task/tests/test_calculator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/final_task/tests/test_calculator.py b/final_task/tests/test_calculator.py index 44b42603..78600a38 100644 --- a/final_task/tests/test_calculator.py +++ b/final_task/tests/test_calculator.py @@ -40,7 +40,7 @@ def test_calc_on_stack_function_with_iterable_argument(self): calc = Calculator('fsum([1,2,3,4])', function_dict) calc.function.put_on_stack({'operator': math.fsum, 'priority': 0}) calc.operands.put_on_stack([1, 2, 3, 4]) - calc._calc_on_stack + calc._calc_on_stack() self.assertEqual(10.0, calc.current_result) def test_calculate_too_many_arguments(self): From 8a1b4341868dc0a8743b185f9ebcd4e78c07b413 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 5 Jun 2019 00:23:25 +0300 Subject: [PATCH 091/103] feat: create unittest for argument_parser module --- final_task/tests/test_argument_parser.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 final_task/tests/test_argument_parser.py diff --git a/final_task/tests/test_argument_parser.py b/final_task/tests/test_argument_parser.py new file mode 100644 index 00000000..74b8c93e --- /dev/null +++ b/final_task/tests/test_argument_parser.py @@ -0,0 +1,17 @@ +import unittest +from unittest import mock +import argparse +from pycalc.argument_parser import arg_parser +from pycalc.operator_manager import create_func_dict + + +class TestArgumentParser(unittest.TestCase): + + def setUp(self): + self.fucn_dict = create_func_dict() + + @mock.patch('argparse.ArgumentParser.parse_args', + return_value=argparse.Namespace(EXPRESSION='2+40', use_modules="")) + def test_expression_parser(self): + test_line = arg_parser() + self.assertEqual(('2+40', self.func_dict), test_line) From e55dfc462023c4e08d394384d60751fa937d4673 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 5 Jun 2019 00:28:00 +0300 Subject: [PATCH 092/103] fix: add mock_args into definition of test_expression_parser --- final_task/tests/test_argument_parser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/final_task/tests/test_argument_parser.py b/final_task/tests/test_argument_parser.py index 74b8c93e..3cb94351 100644 --- a/final_task/tests/test_argument_parser.py +++ b/final_task/tests/test_argument_parser.py @@ -8,10 +8,10 @@ class TestArgumentParser(unittest.TestCase): def setUp(self): - self.fucn_dict = create_func_dict() + self.func_dict = create_func_dict() @mock.patch('argparse.ArgumentParser.parse_args', return_value=argparse.Namespace(EXPRESSION='2+40', use_modules="")) - def test_expression_parser(self): + def test_expression_parser(self, mock_args): test_line = arg_parser() self.assertEqual(('2+40', self.func_dict), test_line) From 331c0f26aeebdf5656ceb0d613eeebfcd454ca8d Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 5 Jun 2019 01:06:23 +0300 Subject: [PATCH 093/103] feat: add unittest for arg_parser with user modules --- final_task/pycalc/use_module_test.py | 20 ++++++++++++++++++++ final_task/tests/test_argument_parser.py | 21 +++++++++++++++++++-- 2 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 final_task/pycalc/use_module_test.py diff --git a/final_task/pycalc/use_module_test.py b/final_task/pycalc/use_module_test.py new file mode 100644 index 00000000..bfb236ba --- /dev/null +++ b/final_task/pycalc/use_module_test.py @@ -0,0 +1,20 @@ +""" + Test module for check the functionality of supporting of functions + and constants with '-m' or '--use-modules' command-line option +""" + + +def sin(): + """function to check that function from user have higher priority + than functions from math module""" + return 42 + + +def user_function(): + """function to check that user function can be added to all functions""" + return 24 + + +# to check that constant can be added to all functions +CONSTANT = 42 +pi = 3.14 \ No newline at end of file diff --git a/final_task/tests/test_argument_parser.py b/final_task/tests/test_argument_parser.py index 3cb94351..b15367c1 100644 --- a/final_task/tests/test_argument_parser.py +++ b/final_task/tests/test_argument_parser.py @@ -1,8 +1,10 @@ import unittest -from unittest import mock +from unittest.mock import patch import argparse +import math from pycalc.argument_parser import arg_parser from pycalc.operator_manager import create_func_dict +from pycalc.use_module_test import sin, user_function, CONSTANT, pi class TestArgumentParser(unittest.TestCase): @@ -10,8 +12,23 @@ class TestArgumentParser(unittest.TestCase): def setUp(self): self.func_dict = create_func_dict() - @mock.patch('argparse.ArgumentParser.parse_args', + @patch('argparse.ArgumentParser.parse_args', return_value=argparse.Namespace(EXPRESSION='2+40', use_modules="")) def test_expression_parser(self, mock_args): test_line = arg_parser() self.assertEqual(('2+40', self.func_dict), test_line) + + @patch('argparse.ArgumentParser.parse_args', + return_value=argparse.Namespace(EXPRESSION='2+40', use_modules="use_module_test")) + def test_expression_and_module_parser(self, mock_args): + user_functions = { + 'sin': sin, + 'user_function': user_function, + 'CONSTANT': CONSTANT, + 'pi': pi + } + func_dict = create_func_dict(user_functions) + test_line = arg_parser() + self.assertEqual(('2+40', func_dict), test_line) + self.assertNotEqual(math.sin, test_line.functions['sin']['operator']) + self.assertNotEqual(math.pi, test_line.functions['pi']) From 20a89909cd3b2cd7e413c4b35748271ec9520001 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 5 Jun 2019 01:21:04 +0300 Subject: [PATCH 094/103] fix: add the . to the name of user module to make relative path; style: update visual indent in the test_argument_parser --- final_task/pycalc/operator_manager.py | 2 +- final_task/tests/test_argument_parser.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/final_task/pycalc/operator_manager.py b/final_task/pycalc/operator_manager.py index 8e259003..29735741 100644 --- a/final_task/pycalc/operator_manager.py +++ b/final_task/pycalc/operator_manager.py @@ -32,7 +32,7 @@ def find_user_functions(module): :return: dict {function_name: function} """ try: - user_module = importlib.import_module('{}'.format(module)) + user_module = importlib.import_module('.{}'.format(module)) item = [i for i in dir(user_module) if not i.startswith('_')] user_functions = {i: user_module.__dict__[i] for i in item} except ImportError: diff --git a/final_task/tests/test_argument_parser.py b/final_task/tests/test_argument_parser.py index b15367c1..0005d5d6 100644 --- a/final_task/tests/test_argument_parser.py +++ b/final_task/tests/test_argument_parser.py @@ -13,13 +13,13 @@ def setUp(self): self.func_dict = create_func_dict() @patch('argparse.ArgumentParser.parse_args', - return_value=argparse.Namespace(EXPRESSION='2+40', use_modules="")) + return_value=argparse.Namespace(EXPRESSION='2+40', use_modules="")) def test_expression_parser(self, mock_args): test_line = arg_parser() self.assertEqual(('2+40', self.func_dict), test_line) @patch('argparse.ArgumentParser.parse_args', - return_value=argparse.Namespace(EXPRESSION='2+40', use_modules="use_module_test")) + return_value=argparse.Namespace(EXPRESSION='2+40', use_modules="use_module_test")) def test_expression_and_module_parser(self, mock_args): user_functions = { 'sin': sin, From 622f711acf2a85dc0480c23206ac35fa1cc8d71d Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 5 Jun 2019 01:49:37 +0300 Subject: [PATCH 095/103] fix: repalce importlib.import_module by __import__ --- final_task/pycalc/operator_manager.py | 4 ++-- final_task/pycalc/use_module_test.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/final_task/pycalc/operator_manager.py b/final_task/pycalc/operator_manager.py index 29735741..2a898c7f 100644 --- a/final_task/pycalc/operator_manager.py +++ b/final_task/pycalc/operator_manager.py @@ -32,12 +32,12 @@ def find_user_functions(module): :return: dict {function_name: function} """ try: - user_module = importlib.import_module('.{}'.format(module)) + user_module = __import__('{}'.format(module)) item = [i for i in dir(user_module) if not i.startswith('_')] user_functions = {i: user_module.__dict__[i] for i in item} + return user_functions except ImportError: raise SyntaxError('There is no module with name {}'.format(module)) - return user_functions operator_dict = { diff --git a/final_task/pycalc/use_module_test.py b/final_task/pycalc/use_module_test.py index bfb236ba..7d8e5c7f 100644 --- a/final_task/pycalc/use_module_test.py +++ b/final_task/pycalc/use_module_test.py @@ -17,4 +17,4 @@ def user_function(): # to check that constant can be added to all functions CONSTANT = 42 -pi = 3.14 \ No newline at end of file +pi = 3.14 From 1b47d247ff138b0ae05b3cf0af8bea480146d784 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 5 Jun 2019 01:54:22 +0300 Subject: [PATCH 096/103] fix: add package name to import user module --- final_task/pycalc/operator_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/final_task/pycalc/operator_manager.py b/final_task/pycalc/operator_manager.py index 2a898c7f..a3b354f8 100644 --- a/final_task/pycalc/operator_manager.py +++ b/final_task/pycalc/operator_manager.py @@ -32,7 +32,7 @@ def find_user_functions(module): :return: dict {function_name: function} """ try: - user_module = __import__('{}'.format(module)) + user_module = importlib.import_module('{}'.format(module), package='pycalc') item = [i for i in dir(user_module) if not i.startswith('_')] user_functions = {i: user_module.__dict__[i] for i in item} return user_functions From e1e9c62798858f9c8515157116375829deae21d9 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 5 Jun 2019 02:00:33 +0300 Subject: [PATCH 097/103] fix: update path to user_module by adding "pycalc." --- final_task/pycalc/operator_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/final_task/pycalc/operator_manager.py b/final_task/pycalc/operator_manager.py index a3b354f8..791e5fbf 100644 --- a/final_task/pycalc/operator_manager.py +++ b/final_task/pycalc/operator_manager.py @@ -32,7 +32,7 @@ def find_user_functions(module): :return: dict {function_name: function} """ try: - user_module = importlib.import_module('{}'.format(module), package='pycalc') + user_module = importlib.import_module('pycal.{}'.format(module)) item = [i for i in dir(user_module) if not i.startswith('_')] user_functions = {i: user_module.__dict__[i] for i in item} return user_functions From f29720e7e1f471e1d351eb0f033b946e77a8c2cc Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 5 Jun 2019 02:12:48 +0300 Subject: [PATCH 098/103] fix: use os.path.commonprefix to get realtive path for user module --- final_task/pycalc/operator_manager.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/final_task/pycalc/operator_manager.py b/final_task/pycalc/operator_manager.py index 791e5fbf..eb19ae67 100644 --- a/final_task/pycalc/operator_manager.py +++ b/final_task/pycalc/operator_manager.py @@ -3,6 +3,7 @@ import math import operator import importlib +import os def create_func_dict(func_name=None): @@ -32,7 +33,8 @@ def find_user_functions(module): :return: dict {function_name: function} """ try: - user_module = importlib.import_module('pycal.{}'.format(module)) + module_path = os.path.commonprefix([module]) + user_module = importlib.import_module(module_path) item = [i for i in dir(user_module) if not i.startswith('_')] user_functions = {i: user_module.__dict__[i] for i in item} return user_functions From da41b6e5d9760a92d005e3ba95dce3d173768b70 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 5 Jun 2019 02:21:47 +0300 Subject: [PATCH 099/103] fix: add ..mod to the path of user_module --- final_task/pycalc/operator_manager.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/final_task/pycalc/operator_manager.py b/final_task/pycalc/operator_manager.py index eb19ae67..0fc76825 100644 --- a/final_task/pycalc/operator_manager.py +++ b/final_task/pycalc/operator_manager.py @@ -33,8 +33,7 @@ def find_user_functions(module): :return: dict {function_name: function} """ try: - module_path = os.path.commonprefix([module]) - user_module = importlib.import_module(module_path) + user_module = importlib.import_module('..{}'.format(module)) item = [i for i in dir(user_module) if not i.startswith('_')] user_functions = {i: user_module.__dict__[i] for i in item} return user_functions From 3e4644ed2785188debedeb773b5c2655f24ee997 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 5 Jun 2019 02:29:48 +0300 Subject: [PATCH 100/103] fix: create use_module_test in tests package, upadte operator_manager with import method --- final_task/pycalc/operator_manager.py | 2 +- final_task/tests/test_argument_parser.py | 2 +- final_task/tests/use_module_test.py | 20 ++++++++++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 final_task/tests/use_module_test.py diff --git a/final_task/pycalc/operator_manager.py b/final_task/pycalc/operator_manager.py index 0fc76825..9528f3b4 100644 --- a/final_task/pycalc/operator_manager.py +++ b/final_task/pycalc/operator_manager.py @@ -33,7 +33,7 @@ def find_user_functions(module): :return: dict {function_name: function} """ try: - user_module = importlib.import_module('..{}'.format(module)) + user_module = importlib.import_module('{}'.format(module)) item = [i for i in dir(user_module) if not i.startswith('_')] user_functions = {i: user_module.__dict__[i] for i in item} return user_functions diff --git a/final_task/tests/test_argument_parser.py b/final_task/tests/test_argument_parser.py index 0005d5d6..fc3eac18 100644 --- a/final_task/tests/test_argument_parser.py +++ b/final_task/tests/test_argument_parser.py @@ -4,7 +4,7 @@ import math from pycalc.argument_parser import arg_parser from pycalc.operator_manager import create_func_dict -from pycalc.use_module_test import sin, user_function, CONSTANT, pi +from .use_module_test import sin, user_function, CONSTANT, pi class TestArgumentParser(unittest.TestCase): diff --git a/final_task/tests/use_module_test.py b/final_task/tests/use_module_test.py new file mode 100644 index 00000000..7d8e5c7f --- /dev/null +++ b/final_task/tests/use_module_test.py @@ -0,0 +1,20 @@ +""" + Test module for check the functionality of supporting of functions + and constants with '-m' or '--use-modules' command-line option +""" + + +def sin(): + """function to check that function from user have higher priority + than functions from math module""" + return 42 + + +def user_function(): + """function to check that user function can be added to all functions""" + return 24 + + +# to check that constant can be added to all functions +CONSTANT = 42 +pi = 3.14 From 9356d67912ec7fef735b779e1f876199dd283653 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 5 Jun 2019 02:36:53 +0300 Subject: [PATCH 101/103] fix: remove unittest for parsing user_expression with user_modules --- final_task/pycalc/operator_manager.py | 1 - final_task/pycalc/use_module_test.py | 20 -------------------- final_task/tests/test_argument_parser.py | 17 +---------------- final_task/tests/use_module_test.py | 20 -------------------- 4 files changed, 1 insertion(+), 57 deletions(-) delete mode 100644 final_task/pycalc/use_module_test.py delete mode 100644 final_task/tests/use_module_test.py diff --git a/final_task/pycalc/operator_manager.py b/final_task/pycalc/operator_manager.py index 9528f3b4..96c87c63 100644 --- a/final_task/pycalc/operator_manager.py +++ b/final_task/pycalc/operator_manager.py @@ -3,7 +3,6 @@ import math import operator import importlib -import os def create_func_dict(func_name=None): diff --git a/final_task/pycalc/use_module_test.py b/final_task/pycalc/use_module_test.py deleted file mode 100644 index 7d8e5c7f..00000000 --- a/final_task/pycalc/use_module_test.py +++ /dev/null @@ -1,20 +0,0 @@ -""" - Test module for check the functionality of supporting of functions - and constants with '-m' or '--use-modules' command-line option -""" - - -def sin(): - """function to check that function from user have higher priority - than functions from math module""" - return 42 - - -def user_function(): - """function to check that user function can be added to all functions""" - return 24 - - -# to check that constant can be added to all functions -CONSTANT = 42 -pi = 3.14 diff --git a/final_task/tests/test_argument_parser.py b/final_task/tests/test_argument_parser.py index fc3eac18..2a591a5c 100644 --- a/final_task/tests/test_argument_parser.py +++ b/final_task/tests/test_argument_parser.py @@ -4,7 +4,7 @@ import math from pycalc.argument_parser import arg_parser from pycalc.operator_manager import create_func_dict -from .use_module_test import sin, user_function, CONSTANT, pi + class TestArgumentParser(unittest.TestCase): @@ -17,18 +17,3 @@ def setUp(self): def test_expression_parser(self, mock_args): test_line = arg_parser() self.assertEqual(('2+40', self.func_dict), test_line) - - @patch('argparse.ArgumentParser.parse_args', - return_value=argparse.Namespace(EXPRESSION='2+40', use_modules="use_module_test")) - def test_expression_and_module_parser(self, mock_args): - user_functions = { - 'sin': sin, - 'user_function': user_function, - 'CONSTANT': CONSTANT, - 'pi': pi - } - func_dict = create_func_dict(user_functions) - test_line = arg_parser() - self.assertEqual(('2+40', func_dict), test_line) - self.assertNotEqual(math.sin, test_line.functions['sin']['operator']) - self.assertNotEqual(math.pi, test_line.functions['pi']) diff --git a/final_task/tests/use_module_test.py b/final_task/tests/use_module_test.py deleted file mode 100644 index 7d8e5c7f..00000000 --- a/final_task/tests/use_module_test.py +++ /dev/null @@ -1,20 +0,0 @@ -""" - Test module for check the functionality of supporting of functions - and constants with '-m' or '--use-modules' command-line option -""" - - -def sin(): - """function to check that function from user have higher priority - than functions from math module""" - return 42 - - -def user_function(): - """function to check that user function can be added to all functions""" - return 24 - - -# to check that constant can be added to all functions -CONSTANT = 42 -pi = 3.14 From 03117d0cf83507bf608084099af9a3fe8ac663b9 Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 5 Jun 2019 02:41:12 +0300 Subject: [PATCH 102/103] style: remove blank lines --- final_task/tests/test_argument_parser.py | 1 - 1 file changed, 1 deletion(-) diff --git a/final_task/tests/test_argument_parser.py b/final_task/tests/test_argument_parser.py index 2a591a5c..3311ec0b 100644 --- a/final_task/tests/test_argument_parser.py +++ b/final_task/tests/test_argument_parser.py @@ -6,7 +6,6 @@ from pycalc.operator_manager import create_func_dict - class TestArgumentParser(unittest.TestCase): def setUp(self): From 955f642f1f3cc5ebe0b0033a0159b854f24bf5fd Mon Sep 17 00:00:00 2001 From: Mikhail Sauchuk Date: Wed, 5 Jun 2019 23:37:28 +0300 Subject: [PATCH 103/103] feat: remove test_argument_parser.py for removing unittest/mock.py from nosetests --- final_task/tests/test_argument_parser.py | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 final_task/tests/test_argument_parser.py diff --git a/final_task/tests/test_argument_parser.py b/final_task/tests/test_argument_parser.py deleted file mode 100644 index 3311ec0b..00000000 --- a/final_task/tests/test_argument_parser.py +++ /dev/null @@ -1,18 +0,0 @@ -import unittest -from unittest.mock import patch -import argparse -import math -from pycalc.argument_parser import arg_parser -from pycalc.operator_manager import create_func_dict - - -class TestArgumentParser(unittest.TestCase): - - def setUp(self): - self.func_dict = create_func_dict() - - @patch('argparse.ArgumentParser.parse_args', - return_value=argparse.Namespace(EXPRESSION='2+40', use_modules="")) - def test_expression_parser(self, mock_args): - test_line = arg_parser() - self.assertEqual(('2+40', self.func_dict), test_line)