From 28af8168d96d830ebc832dd7cbcddccd8d659123 Mon Sep 17 00:00:00 2001 From: "Kaleko Nikita, 650501" <36746354+Shalynishka@users.noreply.github.com> Date: Fri, 14 Dec 2018 21:00:55 +0300 Subject: [PATCH 01/17] Create pycalc.py v 0.3a --- work/pycalc.py | 218 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 work/pycalc.py diff --git a/work/pycalc.py b/work/pycalc.py new file mode 100644 index 0000000..8f03ce6 --- /dev/null +++ b/work/pycalc.py @@ -0,0 +1,218 @@ +import argparse +import math +import operator +import importlib.util +from sys import path + + +class Calc: + """ + Pretty class for cmd evaluating + Attributes: + const -> dict - contains math constant values + func -> dict - contains tuples from math funcs and priority 4 + bin_op -> dict - contains tuples from binary funcs and their priority + cmp -> dict - contains compare operations + + eval_list -> list - contains reverse polish entry + op_st -> list - operations stack + cmp_op -> list - contains compare operations from Expression to evaluate + + implicit_mul -> bool - flag of implicit multiplication + neg -> bool - variable negation flag + + lim -> int - length limit of operation names + Methods: + my_eval(self, st) - evaluate Expression and return value or bool + make_note(self, st, impl=False) - make reverse polish entry and write it to eval_list + eval_not(self) - evaluate RPE using eval_list as source + """ + const = {'pi': math.pi, 'e': math.e, 'q': math.pi * math.e} + func = {'round': (round, 4), 'abs': (abs, 4)} + + users_mod = {} + + bin_op = {'+': (operator.add, 1), '-': (operator.neg, 1), '*': (operator.mul, 2), + '/': (operator.truediv, 2), '%': (operator.mod, 2), '$': (operator.floordiv, 2), '^': (operator.pow, 3)} + + cmp = {'<': operator.lt, '>': operator.gt, '==': operator.eq, '<=': operator.le, '>=': operator.ge} + + eval_list = [] # список ОПЗ + op_st = [] # стек с операциями + + # для унарных операций. суть такова: все вычитания заменяем на сложение + # при этом вычитатель - это просто слагаемое с минусом. На случай нескольких минусов предусмотрена сложная логика + neg = False + implicit_mul = False # флаг на случай неявного умножения "(2+3)4" + + # лимит на длину буквенного выражения. Если его превысить -> raise ValueError + lim = max([len(i) for i in dir(math) if '_' not in i]) + + cmp_op = [] + + def __init__(self, users): + for u in [u + '.py' for u in users if '.py' not in u]: + for p in path: + try: + spec = importlib.util.spec_from_file_location('', location=p + r'\\' + u) + foo = importlib.util.module_from_spec(spec) + spec.loader.exec_module(foo) + except: + continue + else: + self.users_mod[foo] = dir(foo) + + def my_eval(self, st): + for k in self.cmp.keys(): + if k in st: + self.cmp_op.append(self.cmp[k]) + st = st.replace(k, ' ') + if not self.cmp_op: + self.make_note(st) # сборка ОПЗ + return self.eval_note() # вычисление ОПЗ + else: + st = st.split(' ') + res = [] + for string in st: + self.make_note(string) # сборка ОПЗ + res.insert(0, self.eval_note()) # вычисление ОПЗ + self.eval_list = [] + for op in self.cmp_op: + if not op(res[0], res[1]): + return False + del res[0] + return True + + def make_note(self, st, impl=False): + egg = '' # содержит операцию + temp_num = '' # содержит операнд + for c in st: + if c.isdigit(): # формируем строку с числом если текущий элемент - цифра + if self.implicit_mul is True: # между цифрой и предыдущем элементом есть неявное умножение + self.make_note('*', True) + temp_num += c + else: # текущий элемент не цифра + if temp_num: # заносим сформированное число в выходной лист ОПЗ + if self.neg: # перед этим операндом был минус + self.eval_list.append(-int(temp_num)) + self.neg = False + else: + self.eval_list.append(int(temp_num)) + temp_num = '' + if c not in self.bin_op.keys() and c != ')': # после числа неявное умножение + self.make_note('*', True) + + if c == ',': # функция round через ',' может получить второй параметр + continue + + egg += c # формируем строку с буквами - sin/pi/epi и тд. Из этого потом сформируем функции или const + # попытка вытащить текущий egg из пользовательского модуля + for u, d in self.users_mod.items(): + if egg in d: # получилось вытащить + temp = getattr(u, egg) + if callable(temp): # функция + self.op_st.insert(0, (temp, 4)) + else: # константа + self.check_neg(temp) + egg = '' + break + else: + if egg == '(': + if self.implicit_mul is True: # перед скобкой вставляем неявное умножение + self.make_note('*', True) + self.op_st.insert(0, (egg, 0)) + + elif egg == ')': + for o in self.op_st: # выгружаем все операции до открывающей скобки + if o[0] == '(': + self.op_st = self.op_st[self.op_st.index(o) + 1:] + break + self.eval_list.append(o[0]) + self.implicit_mul = True # после закрывающей скобки может быть неявное умножение + + # константа + elif egg in self.const: + self.check_neg(self.const[egg]) + + # выбор из math + elif egg in dir(math): + self.op_st.insert(0, (getattr(math, egg), 4)) + + elif egg in self.bin_op: + if egg == '-': # сложная логика для унарных отрицаний. + self.neg = False if self.neg else True + egg = '+' + for o in self.op_st: # выталкиваем приоритетные, префиксные операции + if o[1] >= self.bin_op[egg][1]: + self.eval_list.append(self.op_st.pop(0)[0]) + else: + break + self.op_st.insert(0, self.bin_op[egg]) + self.implicit_mul = False # наличие бинарной операции исключает неявное умножение + + else: + if self.lim <= len(egg): + raise ValueError('unknown function or constant!') + continue + egg = '' + if egg: + raise ValueError('unknown function or constant!') + if impl is True: # обработка неявного умножения. Чисел нет, лист операций трогать нельзя - выход из функции + self.implicit_mul = False + return + if temp_num: # осталось еще число + if self.neg: # перед этим операндом был минус + self.eval_list.append(-int(temp_num)) + self.neg = False + else: + self.eval_list.append(int(temp_num)) + for o in self.op_st: # выгрузить все оставшиеся операции + self.eval_list.append(o[0]) + + def check_neg(self, const): # отрицание константы + if self.neg: + self.eval_list.append(-const) + self.neg = False + else: + self.eval_list.append(const) + self.implicit_mul = True # после константы может быть неявное умножение + + def eval_note(self): + num_stack = [] + for i in self.eval_list: + if not callable(i): # данный элемент не функция, т.е., число -> поместить в стек чисел + num_stack.insert(0, i) + else: # операция -> применить к двум выше лежащим элементам в стеке + try: + egg = i(*num_stack[1::-1]) + num_stack[:2] = [egg] + except TypeError as ex: + if '2 given' in str(ex): + num_stack[0] = i(num_stack[0]) + + return num_stack[0] + + +try: + parser = argparse.ArgumentParser(description='Pure-python command-line calculator.') + + parser.add_argument('EXPRESSION', help='Expression string to evaluate') + parser.add_argument('-m', '--use-modules', action='store', nargs='*', + dest='user', help='Using your own module', default=None) + pr = parser.parse_args().__dict__ + s = pr['EXPRESSION'] + user = [] + pr['user'] + s = s.replace(' ', '') + s = s[1:-1] + if s.count('(') != s.count(')'): + raise ValueError('brackets are not balanced!') + if '$' in s or 'q' in s: + raise ValueError('incorrect symbols!') + else: + s = s.replace('//', '$') + s = s.replace('epi', 'q') + s = s.replace('pie', 'q') + cd = Calc(user) + print(cd.my_eval(s)) +except Exception as e: + print('PROBLEM', e, sep='\n') From 4ec8dbfaae826f1c344040dd01e63ed8a7d72f9e Mon Sep 17 00:00:00 2001 From: "Kaleko Nikita, 650501" <36746354+Shalynishka@users.noreply.github.com> Date: Fri, 14 Dec 2018 21:05:54 +0300 Subject: [PATCH 02/17] Add files via upload Distribution package --- work/pycalc.tar.gz | Bin 0 -> 627 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 work/pycalc.tar.gz diff --git a/work/pycalc.tar.gz b/work/pycalc.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..024ad5b25f0f4d7e3d8b306f9c5fe517d9092bb1 GIT binary patch literal 627 zcmV-(0*w71iwFp_@Dp4D|72-%bX;(GV_|G#Eif)IE_7jX0PR>^Z<{a_pSYsm8yb1L{IkrA@YkYT7+7mL2)d9*v(;T6rd}RCM{t>86{b!f& z-H(%Z)05OcilUwM4??n4e;-H0LKy0Qa(rf*CR9`RhUG;n#Rv^N?AQ~Uu@P#T!?Ax~ zGi~D7^D@h5UX9SK%$chy{X+^AOA{1{%TvM7I;-SV%iMG94>B`%(Up1L_2hCg{W5Xv zSEWhH9|c^N(UiBspVF9#qMiAM6>**_T~FtVQO>JA$|`g}jrN;oX0Vib)=IYkJNe%X zJ+@l8saVR3qw@bS{SQdsgZ{s8{S&5`NSKJLg<Io>pjCh0)Ss-|1@ylI zfFgqa@8JK@{NQ2!Z_WP@2LbRO{2%zg7v%qZdVTf!?fj2_`+M`>f8hUW{zo_j{&#_b z=~8*BvUjP!{X|>X`&h~}oNe?#`I{oe?K|L+7}jodHRa{tY-HC-*fmnl!s7&T{RWx&jdSrCAA{!jR?DywV$ z2T{xaHOu#6Edq7?Owf#tdrj-~ z`i_li+%>xd#|Lpu6R15$B00000 N;1^PLS Date: Fri, 14 Dec 2018 21:12:38 +0300 Subject: [PATCH 03/17] Create pycalc.py --- final_task/pycalc/pycalc.py | 218 ++++++++++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 final_task/pycalc/pycalc.py diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py new file mode 100644 index 0000000..da8cfcb --- /dev/null +++ b/final_task/pycalc/pycalc.py @@ -0,0 +1,218 @@ +import argparse +import math +import operator +import importlib.util +from sys import path + + +class Calc: + """ + Pretty class for cmd evaluating + Attributes: + const -> dict - contains math constant values + func -> dict - contains tuples from math funcs and priority 4 + bin_op -> dict - contains tuples from binary funcs and their priority + cmp -> dict - contains compare operations + + eval_list -> list - contains reverse polish entry + op_st -> list - operations stack + cmp_op -> list - contains compare operations from Expression to evaluate + + implicit_mul -> bool - flag of implicit multiplication + neg -> bool - variable negation flag + + lim -> int - length limit of operation names + Methods: + my_eval(self, st) - evaluate Expression and return value or bool + make_note(self, st, impl=False) - make reverse polish entry and write it to eval_list + eval_not(self) - evaluate RPE using eval_list as source + """ + const = {'pi': math.pi, 'e': math.e, 'q': math.pi * math.e} + func = {'round': (round, 4), 'abs': (abs, 4)} + + users_mod = {} + + bin_op = {'+': (operator.add, 1), '-': (operator.neg, 1), '*': (operator.mul, 2), + '/': (operator.truediv, 2), '%': (operator.mod, 2), '$': (operator.floordiv, 2), '^': (operator.pow, 3)} + + cmp = {'<': operator.lt, '>': operator.gt, '==': operator.eq, '<=': operator.le, '>=': operator.ge} + + eval_list = [] # список ОПЗ + op_st = [] # стек с операциями + + # для унарных операций. суть такова: все вычитания заменяем на сложение + # при этом вычитатель - это просто слагаемое с минусом. На случай нескольких минусов предусмотрена сложная логика + neg = False + implicit_mul = False # флаг на случай неявного умножения "(2+3)4" + + # лимит на длину буквенного выражения. Если его превысить -> raise ValueError + lim = max([len(i) for i in dir(math) if '_' not in i]) + + cmp_op = [] + + def __init__(self, users): + for u in [u + '.py' for u in users if '.py' not in u]: + for p in path: + try: + spec = importlib.util.spec_from_file_location('', location=p + r'\\' + u) + foo = importlib.util.module_from_spec(spec) + spec.loader.exec_module(foo) + except: + continue + else: + self.users_mod[foo] = dir(foo) + + def my_eval(self, st): + for k in self.cmp.keys(): + if k in st: + self.cmp_op.append(self.cmp[k]) + st = st.replace(k, ' ') + if not self.cmp_op: + self.make_note(st) # сборка ОПЗ + return self.eval_note() # вычисление ОПЗ + else: + st = st.split(' ') + res = [] + for string in st: + self.make_note(string) # сборка ОПЗ + res.insert(0, self.eval_note()) # вычисление ОПЗ + self.eval_list = [] + for op in self.cmp_op: + if not op(res[0], res[1]): + return False + del res[0] + return True + + def make_note(self, st, impl=False): + egg = '' # содержит операцию + temp_num = '' # содержит операнд + for c in st: + if c.isdigit(): # формируем строку с числом если текущий элемент - цифра + if self.implicit_mul is True: # между цифрой и предыдущем элементом есть неявное умножение + self.make_note('*', True) + temp_num += c + else: # текущий элемент не цифра + if temp_num: # заносим сформированное число в выходной лист ОПЗ + if self.neg: # перед этим операндом был минус + self.eval_list.append(-int(temp_num)) + self.neg = False + else: + self.eval_list.append(int(temp_num)) + temp_num = '' + if c not in self.bin_op.keys() and c != ')': # после числа неявное умножение + self.make_note('*', True) + + if c == ',': # функция round через ',' может получить второй параметр + continue + + egg += c # формируем строку с буквами - sin/pi/epi и тд. Из этого потом сформируем функции или const + # попытка вытащить текущий egg из пользовательского модуля + for u, d in self.users_mod.items(): + if egg in d: # получилось вытащить + temp = getattr(u, egg) + if callable(temp): # функция + self.op_st.insert(0, (temp, 4)) + else: # константа + self.check_neg(temp) + egg = '' + break + else: + if egg == '(': + if self.implicit_mul is True: # перед скобкой вставляем неявное умножение + self.make_note('*', True) + self.op_st.insert(0, (egg, 0)) + + elif egg == ')': + for o in self.op_st: # выгружаем все операции до открывающей скобки + if o[0] == '(': + self.op_st = self.op_st[self.op_st.index(o) + 1:] + break + self.eval_list.append(o[0]) + self.implicit_mul = True # после закрывающей скобки может быть неявное умножение + + # константа + elif egg in self.const: + self.check_neg(self.const[egg]) + + # выбор из math + elif egg in dir(math): + self.op_st.insert(0, (getattr(math, egg), 4)) + + elif egg in self.bin_op: + if egg == '-': # сложная логика для унарных отрицаний. + self.neg = False if self.neg else True + egg = '+' + for o in self.op_st: # выталкиваем приоритетные, префиксные операции + if o[1] >= self.bin_op[egg][1]: + self.eval_list.append(self.op_st.pop(0)[0]) + else: + break + self.op_st.insert(0, self.bin_op[egg]) + self.implicit_mul = False # наличие бинарной операции исключает неявное умножение + + else: + if self.lim <= len(egg): + raise ValueError('unknown function or constant!') + continue + egg = '' + if egg: + raise ValueError('unknown function or constant!') + if impl is True: # обработка неявного умножения. Чисел нет, лист операций трогать нельзя - выход из функции + self.implicit_mul = False + return + if temp_num: # осталось еще число + if self.neg: # перед этим операндом был минус + self.eval_list.append(-int(temp_num)) + self.neg = False + else: + self.eval_list.append(int(temp_num)) + for o in self.op_st: # выгрузить все оставшиеся операции + self.eval_list.append(o[0]) + + def check_neg(self, const): # отрицание константы + if self.neg: + self.eval_list.append(-const) + self.neg = False + else: + self.eval_list.append(const) + self.implicit_mul = True # после константы может быть неявное умножение + + def eval_note(self): + num_stack = [] + for i in self.eval_list: + if not callable(i): # данный элемент не функция, т.е., число -> поместить в стек чисел + num_stack.insert(0, i) + else: # операция -> применить к двум выше лежащим элементам в стеке + try: + egg = i(*num_stack[1::-1]) + num_stack[:2] = [egg] + except TypeError as ex: + if '2 given' in str(ex): + num_stack[0] = i(num_stack[0]) + + return num_stack[0] + + +try: + parser = argparse.ArgumentParser(description='Pure-python command-line calculator.') + + parser.add_argument('EXPRESSION', help='Expression string to evaluate') + parser.add_argument('-m', '--use-modules', action='store', nargs='*', + dest='user', help='Using your own module', default=None) + pr = parser.parse_args().__dict__ + s = pr['EXPRESSION'] + user = [] + pr['user'] + s = s.replace(' ', '') + s = s[1:-1] + if s.count('(') != s.count(')'): + raise ValueError('brackets are not balanced!') + if '$' in s or 'q' in s: + raise ValueError('incorrect symbols!') + else: + s = s.replace('//', '$') + s = s.replace('epi', 'q') + s = s.replace('pie', 'q') + cd = Calc(user) + print(cd.my_eval(s)) +except Exception as e: + print('Error:', e, sep='\n') From 5b9787c6520e10418547cc596f51557a0a070de5 Mon Sep 17 00:00:00 2001 From: "Kaleko Nikita, 650501" <36746354+Shalynishka@users.noreply.github.com> Date: Fri, 14 Dec 2018 21:13:09 +0300 Subject: [PATCH 04/17] Create __init__.py --- final_task/pycalc/__init__.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 final_task/pycalc/__init__.py diff --git a/final_task/pycalc/__init__.py b/final_task/pycalc/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/final_task/pycalc/__init__.py @@ -0,0 +1 @@ + From f199540022181a6c8372ae4bd31fd5655e5d3ecc Mon Sep 17 00:00:00 2001 From: "Kaleko Nikita, 650501" <36746354+Shalynishka@users.noreply.github.com> Date: Fri, 14 Dec 2018 22:07:26 +0300 Subject: [PATCH 05/17] Update setup.py --- final_task/setup.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/final_task/setup.py b/final_task/setup.py index e69de29..bced441 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -0,0 +1,11 @@ +from setuptools import setup, find_packages + +setup(name='pycalc', + version='0.1', + description='Pure-python command-line calculator.', + long_description='Really, my python calculator.', + packages=find_packages(), + entry_points={ + 'console_scripts': ['pycalc = pycalc:calculate'] + } + ) From fae9a0fdfb2f3c8ff76de9ce5cdda2e60e03ccab Mon Sep 17 00:00:00 2001 From: "Kaleko Nikita, 650501" <36746354+Shalynishka@users.noreply.github.com> Date: Fri, 14 Dec 2018 22:09:12 +0300 Subject: [PATCH 06/17] Update pycalc.py --- final_task/pycalc/pycalc.py | 48 +++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index da8cfcb..7486981 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -192,27 +192,29 @@ def eval_note(self): return num_stack[0] + -try: - parser = argparse.ArgumentParser(description='Pure-python command-line calculator.') - - parser.add_argument('EXPRESSION', help='Expression string to evaluate') - parser.add_argument('-m', '--use-modules', action='store', nargs='*', - dest='user', help='Using your own module', default=None) - pr = parser.parse_args().__dict__ - s = pr['EXPRESSION'] - user = [] + pr['user'] - s = s.replace(' ', '') - s = s[1:-1] - if s.count('(') != s.count(')'): - raise ValueError('brackets are not balanced!') - if '$' in s or 'q' in s: - raise ValueError('incorrect symbols!') - else: - s = s.replace('//', '$') - s = s.replace('epi', 'q') - s = s.replace('pie', 'q') - cd = Calc(user) - print(cd.my_eval(s)) -except Exception as e: - print('Error:', e, sep='\n') +def calculate(): + try: + parser = argparse.ArgumentParser(description='Pure-python command-line calculator.') + + parser.add_argument('EXPRESSION', help='Expression string to evaluate') + parser.add_argument('-m', '--use-modules', action='store', nargs='*', + dest='user', help='Using your own module', default=None) + pr = parser.parse_args().__dict__ + s = pr['EXPRESSION'] + user = [] + pr['user'] + s = s.replace(' ', '') + s = s[1:-1] + if s.count('(') != s.count(')'): + raise ValueError('brackets are not balanced!') + if '$' in s or 'q' in s: + raise ValueError('incorrect symbols!') + else: + s = s.replace('//', '$') + s = s.replace('epi', 'q') + s = s.replace('pie', 'q') + cd = Calc(user) + print(cd.my_eval(s)) + except Exception as e: + print('PROBLEM', e, sep='\n') From b752c81761983faee3dc658468e0a72af8eff774 Mon Sep 17 00:00:00 2001 From: "Kaleko Nikita, 650501" <36746354+Shalynishka@users.noreply.github.com> Date: Fri, 14 Dec 2018 22:12:58 +0300 Subject: [PATCH 07/17] Delete pycalc.py --- work/pycalc.py | 218 ------------------------------------------------- 1 file changed, 218 deletions(-) delete mode 100644 work/pycalc.py diff --git a/work/pycalc.py b/work/pycalc.py deleted file mode 100644 index 8f03ce6..0000000 --- a/work/pycalc.py +++ /dev/null @@ -1,218 +0,0 @@ -import argparse -import math -import operator -import importlib.util -from sys import path - - -class Calc: - """ - Pretty class for cmd evaluating - Attributes: - const -> dict - contains math constant values - func -> dict - contains tuples from math funcs and priority 4 - bin_op -> dict - contains tuples from binary funcs and their priority - cmp -> dict - contains compare operations - - eval_list -> list - contains reverse polish entry - op_st -> list - operations stack - cmp_op -> list - contains compare operations from Expression to evaluate - - implicit_mul -> bool - flag of implicit multiplication - neg -> bool - variable negation flag - - lim -> int - length limit of operation names - Methods: - my_eval(self, st) - evaluate Expression and return value or bool - make_note(self, st, impl=False) - make reverse polish entry and write it to eval_list - eval_not(self) - evaluate RPE using eval_list as source - """ - const = {'pi': math.pi, 'e': math.e, 'q': math.pi * math.e} - func = {'round': (round, 4), 'abs': (abs, 4)} - - users_mod = {} - - bin_op = {'+': (operator.add, 1), '-': (operator.neg, 1), '*': (operator.mul, 2), - '/': (operator.truediv, 2), '%': (operator.mod, 2), '$': (operator.floordiv, 2), '^': (operator.pow, 3)} - - cmp = {'<': operator.lt, '>': operator.gt, '==': operator.eq, '<=': operator.le, '>=': operator.ge} - - eval_list = [] # список ОПЗ - op_st = [] # стек с операциями - - # для унарных операций. суть такова: все вычитания заменяем на сложение - # при этом вычитатель - это просто слагаемое с минусом. На случай нескольких минусов предусмотрена сложная логика - neg = False - implicit_mul = False # флаг на случай неявного умножения "(2+3)4" - - # лимит на длину буквенного выражения. Если его превысить -> raise ValueError - lim = max([len(i) for i in dir(math) if '_' not in i]) - - cmp_op = [] - - def __init__(self, users): - for u in [u + '.py' for u in users if '.py' not in u]: - for p in path: - try: - spec = importlib.util.spec_from_file_location('', location=p + r'\\' + u) - foo = importlib.util.module_from_spec(spec) - spec.loader.exec_module(foo) - except: - continue - else: - self.users_mod[foo] = dir(foo) - - def my_eval(self, st): - for k in self.cmp.keys(): - if k in st: - self.cmp_op.append(self.cmp[k]) - st = st.replace(k, ' ') - if not self.cmp_op: - self.make_note(st) # сборка ОПЗ - return self.eval_note() # вычисление ОПЗ - else: - st = st.split(' ') - res = [] - for string in st: - self.make_note(string) # сборка ОПЗ - res.insert(0, self.eval_note()) # вычисление ОПЗ - self.eval_list = [] - for op in self.cmp_op: - if not op(res[0], res[1]): - return False - del res[0] - return True - - def make_note(self, st, impl=False): - egg = '' # содержит операцию - temp_num = '' # содержит операнд - for c in st: - if c.isdigit(): # формируем строку с числом если текущий элемент - цифра - if self.implicit_mul is True: # между цифрой и предыдущем элементом есть неявное умножение - self.make_note('*', True) - temp_num += c - else: # текущий элемент не цифра - if temp_num: # заносим сформированное число в выходной лист ОПЗ - if self.neg: # перед этим операндом был минус - self.eval_list.append(-int(temp_num)) - self.neg = False - else: - self.eval_list.append(int(temp_num)) - temp_num = '' - if c not in self.bin_op.keys() and c != ')': # после числа неявное умножение - self.make_note('*', True) - - if c == ',': # функция round через ',' может получить второй параметр - continue - - egg += c # формируем строку с буквами - sin/pi/epi и тд. Из этого потом сформируем функции или const - # попытка вытащить текущий egg из пользовательского модуля - for u, d in self.users_mod.items(): - if egg in d: # получилось вытащить - temp = getattr(u, egg) - if callable(temp): # функция - self.op_st.insert(0, (temp, 4)) - else: # константа - self.check_neg(temp) - egg = '' - break - else: - if egg == '(': - if self.implicit_mul is True: # перед скобкой вставляем неявное умножение - self.make_note('*', True) - self.op_st.insert(0, (egg, 0)) - - elif egg == ')': - for o in self.op_st: # выгружаем все операции до открывающей скобки - if o[0] == '(': - self.op_st = self.op_st[self.op_st.index(o) + 1:] - break - self.eval_list.append(o[0]) - self.implicit_mul = True # после закрывающей скобки может быть неявное умножение - - # константа - elif egg in self.const: - self.check_neg(self.const[egg]) - - # выбор из math - elif egg in dir(math): - self.op_st.insert(0, (getattr(math, egg), 4)) - - elif egg in self.bin_op: - if egg == '-': # сложная логика для унарных отрицаний. - self.neg = False if self.neg else True - egg = '+' - for o in self.op_st: # выталкиваем приоритетные, префиксные операции - if o[1] >= self.bin_op[egg][1]: - self.eval_list.append(self.op_st.pop(0)[0]) - else: - break - self.op_st.insert(0, self.bin_op[egg]) - self.implicit_mul = False # наличие бинарной операции исключает неявное умножение - - else: - if self.lim <= len(egg): - raise ValueError('unknown function or constant!') - continue - egg = '' - if egg: - raise ValueError('unknown function or constant!') - if impl is True: # обработка неявного умножения. Чисел нет, лист операций трогать нельзя - выход из функции - self.implicit_mul = False - return - if temp_num: # осталось еще число - if self.neg: # перед этим операндом был минус - self.eval_list.append(-int(temp_num)) - self.neg = False - else: - self.eval_list.append(int(temp_num)) - for o in self.op_st: # выгрузить все оставшиеся операции - self.eval_list.append(o[0]) - - def check_neg(self, const): # отрицание константы - if self.neg: - self.eval_list.append(-const) - self.neg = False - else: - self.eval_list.append(const) - self.implicit_mul = True # после константы может быть неявное умножение - - def eval_note(self): - num_stack = [] - for i in self.eval_list: - if not callable(i): # данный элемент не функция, т.е., число -> поместить в стек чисел - num_stack.insert(0, i) - else: # операция -> применить к двум выше лежащим элементам в стеке - try: - egg = i(*num_stack[1::-1]) - num_stack[:2] = [egg] - except TypeError as ex: - if '2 given' in str(ex): - num_stack[0] = i(num_stack[0]) - - return num_stack[0] - - -try: - parser = argparse.ArgumentParser(description='Pure-python command-line calculator.') - - parser.add_argument('EXPRESSION', help='Expression string to evaluate') - parser.add_argument('-m', '--use-modules', action='store', nargs='*', - dest='user', help='Using your own module', default=None) - pr = parser.parse_args().__dict__ - s = pr['EXPRESSION'] - user = [] + pr['user'] - s = s.replace(' ', '') - s = s[1:-1] - if s.count('(') != s.count(')'): - raise ValueError('brackets are not balanced!') - if '$' in s or 'q' in s: - raise ValueError('incorrect symbols!') - else: - s = s.replace('//', '$') - s = s.replace('epi', 'q') - s = s.replace('pie', 'q') - cd = Calc(user) - print(cd.my_eval(s)) -except Exception as e: - print('PROBLEM', e, sep='\n') From 1b6c83ce1463cf601ae68a2c447132f6bfcfddd7 Mon Sep 17 00:00:00 2001 From: "Kaleko Nikita, 650501" <36746354+Shalynishka@users.noreply.github.com> Date: Sat, 15 Dec 2018 02:18:23 +0300 Subject: [PATCH 08/17] Update pycalc.py --- final_task/pycalc/pycalc.py | 117 +++++++++++++++++++++++------------- 1 file changed, 76 insertions(+), 41 deletions(-) diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index 7486981..54b12c8 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -13,13 +13,13 @@ class Calc: func -> dict - contains tuples from math funcs and priority 4 bin_op -> dict - contains tuples from binary funcs and their priority cmp -> dict - contains compare operations + users_mod -> dict - users modules eval_list -> list - contains reverse polish entry op_st -> list - operations stack cmp_op -> list - contains compare operations from Expression to evaluate implicit_mul -> bool - flag of implicit multiplication - neg -> bool - variable negation flag lim -> int - length limit of operation names Methods: @@ -31,18 +31,16 @@ class Calc: func = {'round': (round, 4), 'abs': (abs, 4)} users_mod = {} - - bin_op = {'+': (operator.add, 1), '-': (operator.neg, 1), '*': (operator.mul, 2), + bin_op = {'+': (operator.add, 1), '-': (operator.sub, 1), '*': (operator.mul, 2), '/': (operator.truediv, 2), '%': (operator.mod, 2), '$': (operator.floordiv, 2), '^': (operator.pow, 3)} - cmp = {'<': operator.lt, '>': operator.gt, '==': operator.eq, '<=': operator.le, '>=': operator.ge} + cmp = {'==': operator.eq, '<=': operator.le, '>=': operator.ge, + '<': operator.lt, '>': operator.gt, '!=': operator.ne} eval_list = [] # список ОПЗ op_st = [] # стек с операциями - # для унарных операций. суть такова: все вычитания заменяем на сложение - # при этом вычитатель - это просто слагаемое с минусом. На случай нескольких минусов предусмотрена сложная логика - neg = False + unary = True implicit_mul = False # флаг на случай неявного умножения "(2+3)4" # лимит на длину буквенного выражения. Если его превысить -> raise ValueError @@ -78,34 +76,45 @@ def my_eval(self, st): res.insert(0, self.eval_note()) # вычисление ОПЗ self.eval_list = [] for op in self.cmp_op: - if not op(res[0], res[1]): + if not op(res[1], res[0]): return False del res[0] return True def make_note(self, st, impl=False): + self.unary = True + self.implicit_mul = False egg = '' # содержит операцию temp_num = '' # содержит операнд for c in st: - if c.isdigit(): # формируем строку с числом если текущий элемент - цифра + if c.isdigit() or c == '.': # формируем строку с числом если текущий элемент - цифра if self.implicit_mul is True: # между цифрой и предыдущем элементом есть неявное умножение self.make_note('*', True) temp_num += c else: # текущий элемент не цифра if temp_num: # заносим сформированное число в выходной лист ОПЗ - if self.neg: # перед этим операндом был минус - self.eval_list.append(-int(temp_num)) - self.neg = False - else: - self.eval_list.append(int(temp_num)) + self.unary = False + self.check_num(temp_num) temp_num = '' - if c not in self.bin_op.keys() and c != ')': # после числа неявное умножение + # после числа неявное умножение + if c not in self.bin_op.keys() and c != ')' and c != ',' and c != '.': self.make_note('*', True) + else: + if self.unary: + if c == '-': + self.op_st.insert(0, (operator.neg, 4)) + egg = '' + continue + elif c == '+': + self.op_st.insert(0, (operator.pos, 4)) + egg = '' + continue if c == ',': # функция round через ',' может получить второй параметр continue egg += c # формируем строку с буквами - sin/pi/epi и тд. Из этого потом сформируем функции или const + # попытка вытащить текущий egg из пользовательского модуля for u, d in self.users_mod.items(): if egg in d: # получилось вытащить @@ -113,16 +122,19 @@ def make_note(self, st, impl=False): if callable(temp): # функция self.op_st.insert(0, (temp, 4)) else: # константа - self.check_neg(temp) + self.unary = False + self.eval_list.append(temp) egg = '' break else: if egg == '(': + self.unary = True if self.implicit_mul is True: # перед скобкой вставляем неявное умножение self.make_note('*', True) self.op_st.insert(0, (egg, 0)) elif egg == ')': + self.unary = False for o in self.op_st: # выгружаем все операции до открывающей скобки if o[0] == '(': self.op_st = self.op_st[self.op_st.index(o) + 1:] @@ -132,21 +144,33 @@ def make_note(self, st, impl=False): # константа elif egg in self.const: - self.check_neg(self.const[egg]) + self.unary = False + self.eval_list.append(self.const[egg]) + # self.check_neg() + self.implicit_mul = True # выбор из math elif egg in dir(math): + if self.implicit_mul: + self.make_note('*', True) self.op_st.insert(0, (getattr(math, egg), 4)) + elif egg in self.func: + self.op_st.insert(0, self.func[egg]) + elif egg in self.bin_op: - if egg == '-': # сложная логика для унарных отрицаний. - self.neg = False if self.neg else True - egg = '+' - for o in self.op_st: # выталкиваем приоритетные, префиксные операции - if o[1] >= self.bin_op[egg][1]: - self.eval_list.append(self.op_st.pop(0)[0]) - else: - break + if egg == '^' or (egg == '-' and self.unary is False): + self.unary = True + i = 0 + if self.op_st and not self.op_st[0][0] == self.bin_op[egg][0] == operator.pow: + for o in self.op_st: # выталкиваем приоритетные, префиксные операции + if o[1] >= self.bin_op[egg][1]: + self.eval_list.append(o[0]) + del self.op_st[0] + else: + break + i += 1 + self.op_st[:i] = [] self.op_st.insert(0, self.bin_op[egg]) self.implicit_mul = False # наличие бинарной операции исключает неявное умножение @@ -161,21 +185,18 @@ def make_note(self, st, impl=False): self.implicit_mul = False return if temp_num: # осталось еще число - if self.neg: # перед этим операндом был минус - self.eval_list.append(-int(temp_num)) - self.neg = False - else: - self.eval_list.append(int(temp_num)) + self.check_num(temp_num) for o in self.op_st: # выгрузить все оставшиеся операции self.eval_list.append(o[0]) + self.op_st = [] - def check_neg(self, const): # отрицание константы - if self.neg: - self.eval_list.append(-const) - self.neg = False + def check_num(self, temp_num): # определение типа числа + if '.' in temp_num: + if len(temp_num) == 1: + raise ValueError('incorrect using dots!') + self.eval_list.append(float(temp_num)) else: - self.eval_list.append(const) - self.implicit_mul = True # после константы может быть неявное умножение + self.eval_list.append(int(temp_num)) def eval_note(self): num_stack = [] @@ -187,14 +208,17 @@ def eval_note(self): egg = i(*num_stack[1::-1]) num_stack[:2] = [egg] except TypeError as ex: + # print(ex) if '2 given' in str(ex): num_stack[0] = i(num_stack[0]) + if 'got 1' in str(ex): + raise Exception('incorrect expression!') return num_stack[0] - def calculate(): + re = {' + ': '+', ' * ': '*', ', ': ',', ' - ': '-', '\'': '', '"': ''} try: parser = argparse.ArgumentParser(description='Pure-python command-line calculator.') @@ -203,9 +227,16 @@ def calculate(): dest='user', help='Using your own module', default=None) pr = parser.parse_args().__dict__ s = pr['EXPRESSION'] - user = [] + pr['user'] - s = s.replace(' ', '') - s = s[1:-1] + + user = [] + if pr['user']: + user += pr['user'] + for i, r in re.items(): + s = s.replace(i, r) + if ' ' in s: + raise ValueError('spaces in expression!') + if not s: + raise ValueError('empty expression!') if s.count('(') != s.count(')'): raise ValueError('brackets are not balanced!') if '$' in s or 'q' in s: @@ -217,4 +248,8 @@ def calculate(): cd = Calc(user) print(cd.my_eval(s)) except Exception as e: - print('PROBLEM', e, sep='\n') + print('ERROR: ', e, sep='\n', end='') + + +if __name__ == '__main__': + calculate() From f6fb8a0cf8c81e57454bd9cc64f9c46a7c9770ad Mon Sep 17 00:00:00 2001 From: "Kaleko Nikita, 650501" <36746354+Shalynishka@users.noreply.github.com> Date: Sat, 15 Dec 2018 15:49:51 +0300 Subject: [PATCH 09/17] Update pycalc.py adding description of some methods --- final_task/pycalc/pycalc.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index 54b12c8..fd52410 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -23,9 +23,11 @@ class Calc: lim -> int - length limit of operation names Methods: - my_eval(self, st) - evaluate Expression and return value or bool - make_note(self, st, impl=False) - make reverse polish entry and write it to eval_list - eval_not(self) - evaluate RPE using eval_list as source + __init__(self, users) - collects member names of user modules + my_eval(self, st) - evaluates Expression and return value or bool + make_note(self, st, impl=False) - makes reverse polish entry and write it to eval_list + eval_not(self) - evaluates RPE using eval_list as source + check_num(self, temp_num) - converts str to int or float and store num in eval_list """ const = {'pi': math.pi, 'e': math.e, 'q': math.pi * math.e} func = {'round': (round, 4), 'abs': (abs, 4)} @@ -48,7 +50,7 @@ class Calc: cmp_op = [] - def __init__(self, users): + def __init__(self, users: list): for u in [u + '.py' for u in users if '.py' not in u]: for p in path: try: @@ -60,7 +62,7 @@ def __init__(self, users): else: self.users_mod[foo] = dir(foo) - def my_eval(self, st): + def my_eval(self, st: str): for k in self.cmp.keys(): if k in st: self.cmp_op.append(self.cmp[k]) @@ -81,7 +83,7 @@ def my_eval(self, st): del res[0] return True - def make_note(self, st, impl=False): + def make_note(self, st: str, impl=False): self.unary = True self.implicit_mul = False egg = '' # содержит операцию @@ -190,7 +192,7 @@ def make_note(self, st, impl=False): self.eval_list.append(o[0]) self.op_st = [] - def check_num(self, temp_num): # определение типа числа + def check_num(self, temp_num: str): # определение типа числа if '.' in temp_num: if len(temp_num) == 1: raise ValueError('incorrect using dots!') From aa85e0e79f22ce2573f5f5094cff2ee4b5d83930 Mon Sep 17 00:00:00 2001 From: "Kaleko Nikita, 650501" <36746354+Shalynishka@users.noreply.github.com> Date: Sat, 15 Dec 2018 18:15:35 +0300 Subject: [PATCH 10/17] Update setup.py --- 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 bced441..8ecc55a 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -6,6 +6,6 @@ long_description='Really, my python calculator.', packages=find_packages(), entry_points={ - 'console_scripts': ['pycalc = pycalc:calculate'] + 'console_scripts': ['pycalc = pycalc.pycalc:calculate'] } ) From a7d674936e6d269a0946414e56d9e89cbe464996 Mon Sep 17 00:00:00 2001 From: "Kaleko Nikita, 650501" <36746354+Shalynishka@users.noreply.github.com> Date: Sun, 16 Dec 2018 22:38:59 +0300 Subject: [PATCH 11/17] Update setup.py --- 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 8ecc55a..aa59c2a 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -6,6 +6,6 @@ long_description='Really, my python calculator.', packages=find_packages(), entry_points={ - 'console_scripts': ['pycalc = pycalc.pycalc:calculate'] + 'console_scripts': ['pycalc = pycalc.pycalc:parse_cmd_args'] } ) From 4ed2fc7d695f21bbc540dab81091b78a65df25d8 Mon Sep 17 00:00:00 2001 From: "Kaleko Nikita, 650501" <36746354+Shalynishka@users.noreply.github.com> Date: Sun, 16 Dec 2018 22:39:49 +0300 Subject: [PATCH 12/17] Update pycalc.py Style code updated. --- final_task/pycalc/pycalc.py | 221 ++++++++++++++++++++---------------- 1 file changed, 121 insertions(+), 100 deletions(-) diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index fd52410..41dce07 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -7,32 +7,31 @@ class Calc: """ - Pretty class for cmd evaluating + Class for cmd evaluating. Contains methods and Attributes for converting a mathematical expression to Reverse + Polish Entry and its calculation. Attributes: const -> dict - contains math constant values func -> dict - contains tuples from math funcs and priority 4 bin_op -> dict - contains tuples from binary funcs and their priority cmp -> dict - contains compare operations - users_mod -> dict - users modules + users_mod -> dict - user modules eval_list -> list - contains reverse polish entry op_st -> list - operations stack cmp_op -> list - contains compare operations from Expression to evaluate - + implicit_mul -> bool - flag of implicit multiplication lim -> int - length limit of operation names Methods: - __init__(self, users) - collects member names of user modules - my_eval(self, st) - evaluates Expression and return value or bool - make_note(self, st, impl=False) - makes reverse polish entry and write it to eval_list - eval_not(self) - evaluates RPE using eval_list as source - check_num(self, temp_num) - converts str to int or float and store num in eval_list + evaluate_expression(self, st) - evaluate Expression and return value or bool + make_note(self, st, impl=False) - make reverse polish entry and write it to eval_list + eval_not(self) - evaluate RPE using eval_list as source """ - const = {'pi': math.pi, 'e': math.e, 'q': math.pi * math.e} + const = {attr: getattr(math, attr) for attr in dir(math) if '_' not in attr and not callable(getattr(math, attr))} func = {'round': (round, 4), 'abs': (abs, 4)} - users_mod = {} + user_modules = {} bin_op = {'+': (operator.add, 1), '-': (operator.sub, 1), '*': (operator.mul, 2), '/': (operator.truediv, 2), '%': (operator.mod, 2), '$': (operator.floordiv, 2), '^': (operator.pow, 3)} @@ -48,94 +47,111 @@ class Calc: # лимит на длину буквенного выражения. Если его превысить -> raise ValueError lim = max([len(i) for i in dir(math) if '_' not in i]) - cmp_op = [] + # список операторов сравнения, если такие будут в выражении + cmp_in_expr = [] - def __init__(self, users: list): - for u in [u + '.py' for u in users if '.py' not in u]: - for p in path: - try: - spec = importlib.util.spec_from_file_location('', location=p + r'\\' + u) - foo = importlib.util.module_from_spec(spec) - spec.loader.exec_module(foo) - except: - continue - else: - self.users_mod[foo] = dir(foo) - - def my_eval(self, st: str): - for k in self.cmp.keys(): - if k in st: - self.cmp_op.append(self.cmp[k]) - st = st.replace(k, ' ') - if not self.cmp_op: - self.make_note(st) # сборка ОПЗ - return self.eval_note() # вычисление ОПЗ + def __init__(self, modules): + if modules: + for mod in [mod + '.py' for mod in modules if '.py' not in mod]: + for p in path: + try: + spec = importlib.util.spec_from_file_location('', location=p + r'\\' + mod) + foo = importlib.util.module_from_spec(spec) + spec.loader.exec_module(foo) + except: + raise Exception('module {} not found or cannot be loaded!'.format(mod)) + else: + self.user_modules[foo] = dir(foo) + + def evaluate_expression(self, expr): + """ + Gets string with math expression and returns result of the expression. + The definition of comparison occurs in this function. In this case, expression will be divided into parts. + Result of evaluating of this parts will be compared. + :param expr: str + :return: int, float or bool value + """ + for key in self.cmp.keys(): + if key in expr: + self.cmp_in_expr.append(self.cmp[key]) + expr = expr.replace(key, ' ') + if not self.cmp_in_expr: + self.make_note(expr) # сборка ОПЗ + return self.eval_note() # вычисление ОПЗ else: - st = st.split(' ') + expr_list = expr.split(' ') # разбиение строки выражения на подстроки res = [] - for string in st: + for string in expr_list: self.make_note(string) # сборка ОПЗ - res.insert(0, self.eval_note()) # вычисление ОПЗ + res.insert(0, self.eval_note()) # вычисление ОПЗ self.eval_list = [] - for op in self.cmp_op: - if not op(res[1], res[0]): + for cmp_operator in self.cmp_in_expr: + if not cmp_operator(res[1], res[0]): return False del res[0] return True - def make_note(self, st: str, impl=False): + def make_note(self, expr, impl=False): + """ + Gets string with math expression(without compare) and stores Reverse Polish Entry in self.eval_list. + Optional parameter 'impl' used to handle implicit multiplications using recursion. + :param expr: str + :param impl: bool + :return None + """ self.unary = True self.implicit_mul = False - egg = '' # содержит операцию - temp_num = '' # содержит операнд - for c in st: - if c.isdigit() or c == '.': # формируем строку с числом если текущий элемент - цифра + temp_operation = '' # содержит не числовое выражение + temp_num = '' # содержит числовое выражение + for char in expr: + if char.isdigit() or char == '.': # формируем строку с числом если текущий элемент - цифра if self.implicit_mul is True: # между цифрой и предыдущем элементом есть неявное умножение self.make_note('*', True) - temp_num += c + temp_num += char else: # текущий элемент не цифра if temp_num: # заносим сформированное число в выходной лист ОПЗ self.unary = False self.check_num(temp_num) temp_num = '' # после числа неявное умножение - if c not in self.bin_op.keys() and c != ')' and c != ',' and c != '.': + if char not in self.bin_op.keys() and char != ')' and char != ',' and char != '.': self.make_note('*', True) else: if self.unary: - if c == '-': + if char == '-': self.op_st.insert(0, (operator.neg, 4)) - egg = '' + temp_operation = '' continue - elif c == '+': + elif char == '+': self.op_st.insert(0, (operator.pos, 4)) - egg = '' + temp_operation = '' continue - if c == ',': # функция round через ',' может получить второй параметр + if char == ',': # функция round через ',' может получить второй параметр continue - - egg += c # формируем строку с буквами - sin/pi/epi и тд. Из этого потом сформируем функции или const + + # формируем строку с буквами - sin/pi/epi и тд. Из этого потом сформируем функции или const + temp_operation += char # попытка вытащить текущий egg из пользовательского модуля - for u, d in self.users_mod.items(): - if egg in d: # получилось вытащить - temp = getattr(u, egg) - if callable(temp): # функция + for module_name, module_attributes in self.user_modules.items(): + if temp_operation in module_attributes: # получилось вытащить + temp = getattr(module_name, temp_operation) + if callable(temp): # функция self.op_st.insert(0, (temp, 4)) - else: # константа + else: # константа self.unary = False self.eval_list.append(temp) - egg = '' + temp_operation = '' break else: - if egg == '(': + if temp_operation == '(': self.unary = True if self.implicit_mul is True: # перед скобкой вставляем неявное умножение self.make_note('*', True) - self.op_st.insert(0, (egg, 0)) + self.op_st.insert(0, (temp_operation, 0)) - elif egg == ')': + elif temp_operation == ')': self.unary = False for o in self.op_st: # выгружаем все операции до открывающей скобки if o[0] == '(': @@ -145,43 +161,45 @@ def make_note(self, st: str, impl=False): self.implicit_mul = True # после закрывающей скобки может быть неявное умножение # константа - elif egg in self.const: + elif temp_operation in self.const: self.unary = False - self.eval_list.append(self.const[egg]) + if self.implicit_mul is True: # перед скобкой вставляем неявное умножение + self.make_note('*', True) + self.eval_list.append(self.const[temp_operation]) # self.check_neg() self.implicit_mul = True # выбор из math - elif egg in dir(math): + elif temp_operation in dir(math): if self.implicit_mul: self.make_note('*', True) - self.op_st.insert(0, (getattr(math, egg), 4)) + self.op_st.insert(0, (getattr(math, temp_operation), 4)) - elif egg in self.func: - self.op_st.insert(0, self.func[egg]) + elif temp_operation in self.func: + self.op_st.insert(0, self.func[temp_operation]) - elif egg in self.bin_op: - if egg == '^' or (egg == '-' and self.unary is False): + elif temp_operation in self.bin_op: + if temp_operation == '^' or (temp_operation == '-' and self.unary is False): self.unary = True i = 0 - if self.op_st and not self.op_st[0][0] == self.bin_op[egg][0] == operator.pow: + if self.op_st and not self.op_st[0][0] == self.bin_op[temp_operation][0] == operator.pow: for o in self.op_st: # выталкиваем приоритетные, префиксные операции - if o[1] >= self.bin_op[egg][1]: + if o[1] >= self.bin_op[temp_operation][1]: self.eval_list.append(o[0]) del self.op_st[0] else: break i += 1 self.op_st[:i] = [] - self.op_st.insert(0, self.bin_op[egg]) + self.op_st.insert(0, self.bin_op[temp_operation]) self.implicit_mul = False # наличие бинарной операции исключает неявное умножение else: - if self.lim <= len(egg): + if self.lim <= len(temp_operation): raise ValueError('unknown function or constant!') continue - egg = '' - if egg: + temp_operation = '' + if temp_operation: raise ValueError('unknown function or constant!') if impl is True: # обработка неявного умножения. Чисел нет, лист операций трогать нельзя - выход из функции self.implicit_mul = False @@ -192,7 +210,7 @@ def make_note(self, st: str, impl=False): self.eval_list.append(o[0]) self.op_st = [] - def check_num(self, temp_num: str): # определение типа числа + def check_num(self, temp_num): # определение типа числа if '.' in temp_num: if len(temp_num) == 1: raise ValueError('incorrect using dots!') @@ -201,6 +219,10 @@ def check_num(self, temp_num: str): # определение типа self.eval_list.append(int(temp_num)) def eval_note(self): + """ + Evaluate Reverse Polish Entry from self.evaluate_list + :return: int or float + """ num_stack = [] for i in self.eval_list: if not callable(i): # данный элемент не функция, т.е., число -> поместить в стек чисел @@ -219,39 +241,38 @@ def eval_note(self): return num_stack[0] -def calculate(): - re = {' + ': '+', ' * ': '*', ', ': ',', ' - ': '-', '\'': '', '"': ''} +def calculate(expr, modules): + combinations_for_replace = {' + ': '+', ' * ': '*', ', ': ',', ' - ': '-', '\'': '', '"': ''} + + for i, r in combinations_for_replace.items(): + expr = expr.replace(i, r) + if ' ' in expr: + raise ValueError('spaces in expression!') + if not expr: + raise ValueError('empty expression!') + if expr.count('(') != expr.count(')'): + raise ValueError('brackets are not balanced!') + if '$' in expr or 'q' in expr: + raise ValueError('incorrect symbols!') + else: + expr = expr.replace('//', '$') + cd = Calc(modules) + print(cd.evaluate_expression(expr)) + + +def parse_cmd_args(): try: parser = argparse.ArgumentParser(description='Pure-python command-line calculator.') parser.add_argument('EXPRESSION', help='Expression string to evaluate') parser.add_argument('-m', '--use-modules', action='store', nargs='*', - dest='user', help='Using your own module', default=None) - pr = parser.parse_args().__dict__ - s = pr['EXPRESSION'] - - user = [] - if pr['user']: - user += pr['user'] - for i, r in re.items(): - s = s.replace(i, r) - if ' ' in s: - raise ValueError('spaces in expression!') - if not s: - raise ValueError('empty expression!') - if s.count('(') != s.count(')'): - raise ValueError('brackets are not balanced!') - if '$' in s or 'q' in s: - raise ValueError('incorrect symbols!') - else: - s = s.replace('//', '$') - s = s.replace('epi', 'q') - s = s.replace('pie', 'q') - cd = Calc(user) - print(cd.my_eval(s)) + dest='modules', help='Using your own module', default=None) + pr = parser.parse_args() + calculate(pr.EXPRESSION, pr.modules) + except Exception as e: print('ERROR: ', e, sep='\n', end='') if __name__ == '__main__': - calculate() + parse_cmd_args() From 596dd4ad7b5e0bb42b0bf14d0be94eac08bafd10 Mon Sep 17 00:00:00 2001 From: "Kaleko Nikita, 650501" <36746354+Shalynishka@users.noreply.github.com> Date: Mon, 17 Dec 2018 01:27:43 +0300 Subject: [PATCH 13/17] Update pycalc.py Algorithm has been updated. --- final_task/pycalc/pycalc.py | 84 +++++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 37 deletions(-) diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index 41dce07..4aa3f65 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -101,9 +101,10 @@ def make_note(self, expr, impl=False): """ self.unary = True self.implicit_mul = False - temp_operation = '' # содержит не числовое выражение + temp_func = '' # содержит не числовое выражение temp_num = '' # содержит числовое выражение for char in expr: + # print('char', char, self.op_st) if char.isdigit() or char == '.': # формируем строку с числом если текущий элемент - цифра if self.implicit_mul is True: # между цифрой и предыдущем элементом есть неявное умножение self.make_note('*', True) @@ -120,96 +121,103 @@ def make_note(self, expr, impl=False): if self.unary: if char == '-': self.op_st.insert(0, (operator.neg, 4)) - temp_operation = '' + temp_func = '' continue elif char == '+': self.op_st.insert(0, (operator.pos, 4)) - temp_operation = '' + temp_func = '' continue if char == ',': # функция round через ',' может получить второй параметр + self.unload_stack(True) continue # формируем строку с буквами - sin/pi/epi и тд. Из этого потом сформируем функции или const - temp_operation += char + temp_func += char # попытка вытащить текущий egg из пользовательского модуля for module_name, module_attributes in self.user_modules.items(): - if temp_operation in module_attributes: # получилось вытащить - temp = getattr(module_name, temp_operation) + if temp_func in module_attributes: # получилось вытащить + temp = getattr(module_name, temp_func) if callable(temp): # функция self.op_st.insert(0, (temp, 4)) else: # константа self.unary = False self.eval_list.append(temp) - temp_operation = '' + temp_func = '' break else: - if temp_operation == '(': + if temp_func == '(': self.unary = True if self.implicit_mul is True: # перед скобкой вставляем неявное умножение self.make_note('*', True) - self.op_st.insert(0, (temp_operation, 0)) + self.op_st.insert(0, (temp_func, 0)) + # print('okay', self.eval_list, self.op_st) - elif temp_operation == ')': - self.unary = False - for o in self.op_st: # выгружаем все операции до открывающей скобки - if o[0] == '(': - self.op_st = self.op_st[self.op_st.index(o) + 1:] - break - self.eval_list.append(o[0]) + elif temp_func == ')': + self.unload_stack() self.implicit_mul = True # после закрывающей скобки может быть неявное умножение # константа - elif temp_operation in self.const: + elif temp_func in self.const: self.unary = False if self.implicit_mul is True: # перед скобкой вставляем неявное умножение self.make_note('*', True) - self.eval_list.append(self.const[temp_operation]) + self.eval_list.append(self.const[temp_func]) # self.check_neg() self.implicit_mul = True # выбор из math - elif temp_operation in dir(math): + elif temp_func in dir(math): if self.implicit_mul: self.make_note('*', True) - self.op_st.insert(0, (getattr(math, temp_operation), 4)) + self.op_st.insert(0, (getattr(math, temp_func), 4)) - elif temp_operation in self.func: - self.op_st.insert(0, self.func[temp_operation]) + elif temp_func in self.func: + self.op_st.insert(0, self.func[temp_func]) - elif temp_operation in self.bin_op: - if temp_operation == '^' or (temp_operation == '-' and self.unary is False): + elif temp_func in self.bin_op: + if temp_func == '^' or (temp_func == '-' and self.unary is False) or temp_func == '+': self.unary = True i = 0 - if self.op_st and not self.op_st[0][0] == self.bin_op[temp_operation][0] == operator.pow: - for o in self.op_st: # выталкиваем приоритетные, префиксные операции - if o[1] >= self.bin_op[temp_operation][1]: - self.eval_list.append(o[0]) - del self.op_st[0] + if self.op_st and not self.op_st[0][0] == self.bin_op[temp_func][0] == operator.pow: + for operation in self.op_st: # выталкиваем приоритетные, префиксные операции + if operation[1] >= self.bin_op[temp_func][1]: + self.eval_list.append(operation[0]) + # del self.op_st[0] else: break i += 1 self.op_st[:i] = [] - self.op_st.insert(0, self.bin_op[temp_operation]) + self.op_st.insert(0, self.bin_op[temp_func]) self.implicit_mul = False # наличие бинарной операции исключает неявное умножение else: - if self.lim <= len(temp_operation): + if self.lim <= len(temp_func): raise ValueError('unknown function or constant!') continue - temp_operation = '' - if temp_operation: + temp_func = '' + if temp_func: raise ValueError('unknown function or constant!') if impl is True: # обработка неявного умножения. Чисел нет, лист операций трогать нельзя - выход из функции self.implicit_mul = False return if temp_num: # осталось еще число self.check_num(temp_num) - for o in self.op_st: # выгрузить все оставшиеся операции - self.eval_list.append(o[0]) + for operation in self.op_st: # выгрузить все оставшиеся операции + self.eval_list.append(operation[0]) self.op_st = [] + def unload_stack(self, is_dot=False): + self.unary = False + for op in self.op_st: # выгружаем все операции до открывающей скобки + if op[0] == '(': + self.op_st = self.op_st[self.op_st.index(op) + 1:] + break + self.eval_list.append(op[0]) + if is_dot: + self.op_st.insert(0, ('(', 0)) + def check_num(self, temp_num): # определение типа числа if '.' in temp_num: if len(temp_num) == 1: @@ -223,6 +231,7 @@ def eval_note(self): Evaluate Reverse Polish Entry from self.evaluate_list :return: int or float """ + # print(self.eval_list) num_stack = [] for i in self.eval_list: if not callable(i): # данный элемент не функция, т.е., число -> поместить в стек чисел @@ -232,7 +241,6 @@ def eval_note(self): egg = i(*num_stack[1::-1]) num_stack[:2] = [egg] except TypeError as ex: - # print(ex) if '2 given' in str(ex): num_stack[0] = i(num_stack[0]) if 'got 1' in str(ex): @@ -242,10 +250,11 @@ def eval_note(self): def calculate(expr, modules): - combinations_for_replace = {' + ': '+', ' * ': '*', ', ': ',', ' - ': '-', '\'': '', '"': ''} + combinations_for_replace = {' + ': '+', ' * ': '*', ', ': ',', ' - ': '-', ' -': '-', '\'': '', '"': ''} for i, r in combinations_for_replace.items(): expr = expr.replace(i, r) + print(expr) if ' ' in expr: raise ValueError('spaces in expression!') if not expr: @@ -257,6 +266,7 @@ def calculate(expr, modules): else: expr = expr.replace('//', '$') cd = Calc(modules) + # print(expr) print(cd.evaluate_expression(expr)) From 7b0a5ff959ab266a9d8b204181d3a95c319cf731 Mon Sep 17 00:00:00 2001 From: "Kaleko Nikita, 650501" <36746354+Shalynishka@users.noreply.github.com> Date: Mon, 17 Dec 2018 01:29:22 +0300 Subject: [PATCH 14/17] Delete pycalc.tar.gz --- work/pycalc.tar.gz | Bin 627 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 work/pycalc.tar.gz diff --git a/work/pycalc.tar.gz b/work/pycalc.tar.gz deleted file mode 100644 index 024ad5b25f0f4d7e3d8b306f9c5fe517d9092bb1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 627 zcmV-(0*w71iwFp_@Dp4D|72-%bX;(GV_|G#Eif)IE_7jX0PR>^Z<{a_pSYsm8yb1L{IkrA@YkYT7+7mL2)d9*v(;T6rd}RCM{t>86{b!f& z-H(%Z)05OcilUwM4??n4e;-H0LKy0Qa(rf*CR9`RhUG;n#Rv^N?AQ~Uu@P#T!?Ax~ zGi~D7^D@h5UX9SK%$chy{X+^AOA{1{%TvM7I;-SV%iMG94>B`%(Up1L_2hCg{W5Xv zSEWhH9|c^N(UiBspVF9#qMiAM6>**_T~FtVQO>JA$|`g}jrN;oX0Vib)=IYkJNe%X zJ+@l8saVR3qw@bS{SQdsgZ{s8{S&5`NSKJLg<Io>pjCh0)Ss-|1@ylI zfFgqa@8JK@{NQ2!Z_WP@2LbRO{2%zg7v%qZdVTf!?fj2_`+M`>f8hUW{zo_j{&#_b z=~8*BvUjP!{X|>X`&h~}oNe?#`I{oe?K|L+7}jodHRa{tY-HC-*fmnl!s7&T{RWx&jdSrCAA{!jR?DywV$ z2T{xaHOu#6Edq7?Owf#tdrj-~ z`i_li+%>xd#|Lpu6R15$B00000 N;1^PLS Date: Mon, 17 Dec 2018 01:30:04 +0300 Subject: [PATCH 15/17] Update pycalc.py --- final_task/pycalc/pycalc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index 4aa3f65..8b88221 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -254,7 +254,7 @@ def calculate(expr, modules): for i, r in combinations_for_replace.items(): expr = expr.replace(i, r) - print(expr) +# print(expr) if ' ' in expr: raise ValueError('spaces in expression!') if not expr: From 739a6d72726ad62e49b1d610e0723ebedd8c6a93 Mon Sep 17 00:00:00 2001 From: "Kaleko Nikita, 650501" <36746354+Shalynishka@users.noreply.github.com> Date: Wed, 19 Dec 2018 00:48:38 +0300 Subject: [PATCH 16/17] Update pycalc.py Defenition of similar function(log vs log10) --- final_task/pycalc/pycalc.py | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index 8b88221..d24605e 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -101,11 +101,13 @@ def make_note(self, expr, impl=False): """ self.unary = True self.implicit_mul = False - temp_func = '' # содержит не числовое выражение - temp_num = '' # содержит числовое выражение + temp_func = '' # содержит не числовое выражение + temp_num = '' # содержит числовое выражение + index = -1 # индекс текущего символа в выражении. Необходим для определения схожих функций(log vs log10) for char in expr: - # print('char', char, self.op_st) - if char.isdigit() or char == '.': # формируем строку с числом если текущий элемент - цифра + index += 1 + # формируем строку с числом если текущий элемент - цифра + if not temp_func and char.isdigit() or char == '.': if self.implicit_mul is True: # между цифрой и предыдущем элементом есть неявное умножение self.make_note('*', True) temp_num += char @@ -137,7 +139,7 @@ def make_note(self, expr, impl=False): # попытка вытащить текущий egg из пользовательского модуля for module_name, module_attributes in self.user_modules.items(): - if temp_func in module_attributes: # получилось вытащить + if temp_func in module_attributes: # получилось вытащить temp = getattr(module_name, temp_func) if callable(temp): # функция self.op_st.insert(0, (temp, 4)) @@ -152,11 +154,11 @@ def make_note(self, expr, impl=False): if self.implicit_mul is True: # перед скобкой вставляем неявное умножение self.make_note('*', True) self.op_st.insert(0, (temp_func, 0)) - # print('okay', self.eval_list, self.op_st) elif temp_func == ')': self.unload_stack() self.implicit_mul = True # после закрывающей скобки может быть неявное умножение + self.unary = False # константа elif temp_func in self.const: @@ -164,14 +166,16 @@ def make_note(self, expr, impl=False): if self.implicit_mul is True: # перед скобкой вставляем неявное умножение self.make_note('*', True) self.eval_list.append(self.const[temp_func]) - # self.check_neg() self.implicit_mul = True # выбор из math elif temp_func in dir(math): if self.implicit_mul: self.make_note('*', True) - self.op_st.insert(0, (getattr(math, temp_func), 4)) + if self.find_real_func(expr[index + 1:]): + self.op_st.insert(0, (getattr(math, temp_func), 4)) + else: + continue elif temp_func in self.func: self.op_st.insert(0, self.func[temp_func]) @@ -184,7 +188,6 @@ def make_note(self, expr, impl=False): for operation in self.op_st: # выталкиваем приоритетные, префиксные операции if operation[1] >= self.bin_op[temp_func][1]: self.eval_list.append(operation[0]) - # del self.op_st[0] else: break i += 1 @@ -226,12 +229,20 @@ def check_num(self, temp_num): # определение типа чи else: self.eval_list.append(int(temp_num)) + # проверка на неполноту функции(log vs log10) + @staticmethod + def find_real_func(remainder): + index = remainder.index('(') + if not index: # функция уже полная, за ней идет скобка + return True + else: + return False + def eval_note(self): """ Evaluate Reverse Polish Entry from self.evaluate_list :return: int or float """ - # print(self.eval_list) num_stack = [] for i in self.eval_list: if not callable(i): # данный элемент не функция, т.е., число -> поместить в стек чисел @@ -254,7 +265,6 @@ def calculate(expr, modules): for i, r in combinations_for_replace.items(): expr = expr.replace(i, r) -# print(expr) if ' ' in expr: raise ValueError('spaces in expression!') if not expr: @@ -266,7 +276,6 @@ def calculate(expr, modules): else: expr = expr.replace('//', '$') cd = Calc(modules) - # print(expr) print(cd.evaluate_expression(expr)) From d30054bdd7487f595dd8b4ed188441125c1a3987 Mon Sep 17 00:00:00 2001 From: "Kaleko Nikita, 650501" <36746354+Shalynishka@users.noreply.github.com> Date: Wed, 19 Dec 2018 13:54:01 +0300 Subject: [PATCH 17/17] Update pycalc.py --- final_task/pycalc/pycalc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index d24605e..d9782fe 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -132,12 +132,13 @@ def make_note(self, expr, impl=False): if char == ',': # функция round через ',' может получить второй параметр self.unload_stack(True) + self.unary = True continue # формируем строку с буквами - sin/pi/epi и тд. Из этого потом сформируем функции или const temp_func += char - # попытка вытащить текущий egg из пользовательского модуля + # попытка вытащить текущий temp_func из пользовательского модуля for module_name, module_attributes in self.user_modules.items(): if temp_func in module_attributes: # получилось вытащить temp = getattr(module_name, temp_func) @@ -158,7 +159,6 @@ def make_note(self, expr, impl=False): elif temp_func == ')': self.unload_stack() self.implicit_mul = True # после закрывающей скобки может быть неявное умножение - self.unary = False # константа elif temp_func in self.const: