From b3a1b5564e09d5e9986b6b9a2b453e45a8925bd0 Mon Sep 17 00:00:00 2001 From: Vladislav Pirtan Date: Wed, 7 Nov 2018 19:23:28 +0300 Subject: [PATCH 01/27] Add my files --- final_task/pycalc/__init__.py | 2 + final_task/pycalc/__main__.py | 19 +++ final_task/pycalc/pycalc.py | 236 ++++++++++++++++++++++++++++++++++ final_task/setup.py | 10 ++ 4 files changed, 267 insertions(+) create mode 100644 final_task/pycalc/__init__.py create mode 100644 final_task/pycalc/__main__.py create mode 100644 final_task/pycalc/pycalc.py diff --git a/final_task/pycalc/__init__.py b/final_task/pycalc/__init__.py new file mode 100644 index 0000000..139597f --- /dev/null +++ b/final_task/pycalc/__init__.py @@ -0,0 +1,2 @@ + + diff --git a/final_task/pycalc/__main__.py b/final_task/pycalc/__main__.py new file mode 100644 index 0000000..b2650be --- /dev/null +++ b/final_task/pycalc/__main__.py @@ -0,0 +1,19 @@ +import sys +from .pycalc import pol, shunting_yard + +HELP = 'usage: pycalc [-h] EXPRESSION\nPure-python command-line calculator.\npositional arguments:' \ + '\n EXPRESSION expression string to evaluate' + + +def main(): + try: + if sys.argv[1] == '--help': + print(HELP) + else: + print(pol(shunting_yard(sys.argv[1]))) + except Exception as err: + print('ERROR:', err) + + +if __name__ == '__main__': + main() diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py new file mode 100644 index 0000000..b3b6b7f --- /dev/null +++ b/final_task/pycalc/pycalc.py @@ -0,0 +1,236 @@ +import math +import sys + +STR_DIGITS = '0123456789' +STR_OPERATOR = '^*/%+-<=>!' +MATH_CONST = ['pi', 'e', 'tau', 'inf', 'nan'] +ACCURACY = 1e-100 +PREFIX_CONST = '#C' +PREFIX_FUNC = '#F' + +PRIORITY_DICT = { + '^': 5, + '+-': 5, + '*': 4, + '/': 4, + '//': 4, + '%': 4, + '+': 3, + '-': 3, + '<=': 2, + '>=': 2, + '<': 1, + '>': 1, + '==': 1, + '!=': 1} +BOOL_DICT = {'True': True, 'False': False} +FUNCTION_DICT = {'abs': abs, 'pow': pow, 'round': round} + + +def stack_push(stack, token): + buf_str = '' + if token == '^' or token == '+-': + for i in range(len(stack)): + if stack[-1] == '(': + break + elif PRIORITY_DICT[token] < PRIORITY_DICT[stack[-1]]: + buf_str += stack.pop() + ' ' + else: + break + stack.append(token) + + else: + for i in range(len(stack)): + if stack[-1] == '(': + break + elif PRIORITY_DICT[token] <= PRIORITY_DICT[stack[-1]]: + buf_str += stack.pop() + ' ' + else: + break + stack.append(token) + return buf_str + + +def shunting_yard(input_str): + if len(input_str) == 0: + raise Exception('empty input') + stack = [] + output_str = '' + func_buf = '' + last_token = '' + count_args = 1 + i = 0 + while i < len(input_str): + token = input_str[i] + if token == ' ': + i += 1 + continue + + if ((token >= 'A') & (token <= 'Z')) \ + or ((token >= 'a') & (token <= 'z')): + func_buf += token + last_token = token + i += 1 + continue + elif func_buf: + if token in STR_OPERATOR + ')': + output_str += PREFIX_CONST + func_buf + func_buf = '' + elif token == '(': + stack.append(PREFIX_FUNC + func_buf) + func_buf = '' + stack.append('(') + last_token = token + i += 1 + continue + else: + func_buf += token + last_token = token + i += 1 + continue + + if token in STR_DIGITS: + output_str += token + + elif token == '.': + output_str += '.' + + elif token == ',': + count_args += 1 + output_str += ' ' + token = stack[-1] + while token != '(': + output_str += ' ' + stack.pop() + ' ' + token = stack[-1] + + elif token == '(': + stack.append('(') + + elif token == ')': + token = stack[-1] + while token != '(': + output_str += ' ' + stack.pop() + ' ' + if len(stack) != 0: + token = stack[-1] + else: + raise Exception('unpaired brackets') + else: + stack.pop() + if len(stack) != 0: + if PREFIX_FUNC in stack[-1]: + output_str += ' ' + str(count_args) + stack.pop() + count_args = 1 + token = ')' + + elif token in STR_OPERATOR: + next_token = input_str[i + 1] + output_str += ' ' + + if (token == '-') & (last_token in STR_OPERATOR + '('): + output_str += stack_push(stack, '+-') + elif (token == '+') & (last_token in STR_OPERATOR): + pass + elif (token in ['<', '>', '!', '=']) & (next_token == '='): + token += next_token + output_str += stack_push(stack, token) + i += 1 + elif token == '=': + raise Exception('unknown operator: "="') + elif (token == '/') & (next_token == '/'): + token += next_token + output_str += stack_push(stack, token) + i += 1 + else: + output_str += stack_push(stack, token) + + else: + raise Exception('unknown operator: "' + token + '"') + + last_token = token + i += 1 + + if func_buf: + output_str += PREFIX_CONST + func_buf + + while stack: + token = stack.pop() + if token == '(': + raise Exception('unpaired brackets') + output_str += ' ' + token + + return output_str + + +def pol(input_str): + stack = [] + input_list = input_str.split(' ') + for token in input_list: + if token == '': + continue + elif token in PRIORITY_DICT: + if token == '^': + val2 = stack.pop() + val1 = stack.pop() + result = val1 ** val2 + if type(result) is complex: + return 'ERROR: negative number cannot be raised to a fractional power' + stack.append(result) + if token == '+-': + stack[-1] = - stack[-1] + if token == '*': + val2 = stack.pop() + stack.append(stack.pop() * val2) + if token == '/': + val2 = stack.pop() + stack.append(stack.pop() / val2) + if token == '//': + val2 = stack.pop() + stack.append(stack.pop() // val2) + if token == '%': + val2 = stack.pop() + stack.append(stack.pop() % val2) + if token == '+': + val2 = stack.pop() + stack.append(stack.pop() + val2) + if token == '-': + val2 = stack.pop() + val1 = stack.pop() + stack.append(val1 - val2) + if token == '<=': + val2 = stack.pop() + stack.append(stack.pop() <= val2) + if token == '>=': + val2 = stack.pop() + stack.append(stack.pop() >= val2) + if token == '<': + val2 = stack.pop() + stack.append(stack.pop() < val2) + if token == '>': + val2 = stack.pop() + stack.append(stack.pop() > val2) + if token == '==': + val2 = stack.pop() + stack.append(stack.pop() == val2) + if token == '!=': + val2 = stack.pop() + stack.append(stack.pop() != val2) + elif token[2:] in BOOL_DICT: + stack.append(BOOL_DICT[token[2:]]) + elif PREFIX_CONST in token: + stack.append(getattr(math, token[2:])) + elif PREFIX_FUNC in token: + args_list = [] + for i in range(int(token[0])): + args_list.append(stack.pop()) + if token[3:] in FUNCTION_DICT: + x = FUNCTION_DICT[token[3:]](*args_list[::-1]) + else: + x = getattr(math, token[3:])(*args_list[::-1]) + stack.append(x) + else: + stack.append(float(token)) + return stack.pop() + + +if __name__ == '__main__': + print(pol(shunting_yard(sys.argv[1]))) diff --git a/final_task/setup.py b/final_task/setup.py index e69de29..5e2ea06 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -0,0 +1,10 @@ +from setuptools import setup, find_packages + +setup( + name='pycalc', + version='0.1', + author='Vladislav Pirtan', + author_email='Vpirtan@yandex.ru', + packages=find_packages(), + entry_points={'console_scripts': ['pycalc=pycalc.__main__:main']}, +) From 1d9da5ede2f1247ce6e5e7a2954bb495a9afb70d Mon Sep 17 00:00:00 2001 From: Vladislav Pirtan Date: Wed, 7 Nov 2018 19:50:42 +0300 Subject: [PATCH 02/27] input test --- final_task/pycalc/__main__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/final_task/pycalc/__main__.py b/final_task/pycalc/__main__.py index b2650be..0241d13 100644 --- a/final_task/pycalc/__main__.py +++ b/final_task/pycalc/__main__.py @@ -6,6 +6,7 @@ def main(): + print(sys.argv) try: if sys.argv[1] == '--help': print(HELP) From bf86476fcf74ccb704967dae4a58ec00ad2b347d Mon Sep 17 00:00:00 2001 From: Vladislav Pirtan Date: Wed, 7 Nov 2018 19:54:48 +0300 Subject: [PATCH 03/27] bug fix --- final_task/pycalc/__main__.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/final_task/pycalc/__main__.py b/final_task/pycalc/__main__.py index 0241d13..f5e57ca 100644 --- a/final_task/pycalc/__main__.py +++ b/final_task/pycalc/__main__.py @@ -6,12 +6,11 @@ def main(): - print(sys.argv) try: - if sys.argv[1] == '--help': + if sys.argv[2] == '--help': print(HELP) else: - print(pol(shunting_yard(sys.argv[1]))) + print(pol(shunting_yard(sys.argv[2]))) except Exception as err: print('ERROR:', err) From f2e4d6d6e5c48ddf665113cb3be6d9e4d274906a Mon Sep 17 00:00:00 2001 From: Vladislav Pirtan Date: Wed, 7 Nov 2018 20:04:09 +0300 Subject: [PATCH 04/27] fix "1 2" --- final_task/pycalc/pycalc.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index b3b6b7f..6addafc 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -229,6 +229,8 @@ def pol(input_str): stack.append(x) else: stack.append(float(token)) + if len(stack) > 1: + raise Exception('invalid input') return stack.pop() From 02445da843c32421dfa69e610a48d5dbe8dde9d8 Mon Sep 17 00:00:00 2001 From: Vladislav Pirtan Date: Wed, 7 Nov 2018 20:21:09 +0300 Subject: [PATCH 05/27] fix "1 2" N2 --- final_task/pycalc/pycalc.py | 1 + 1 file changed, 1 insertion(+) diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index 6addafc..4e0ca64 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -63,6 +63,7 @@ def shunting_yard(input_str): while i < len(input_str): token = input_str[i] if token == ' ': + output_str += ' ' i += 1 continue From 8a58e1fc786606dce40e8ab87310b77165686023 Mon Sep 17 00:00:00 2001 From: Vladislav Pirtan Date: Wed, 7 Nov 2018 20:30:40 +0300 Subject: [PATCH 06/27] __init__.py fix --- final_task/pycalc/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/final_task/pycalc/__init__.py b/final_task/pycalc/__init__.py index 139597f..e69de29 100644 --- a/final_task/pycalc/__init__.py +++ b/final_task/pycalc/__init__.py @@ -1,2 +0,0 @@ - - From a73f68cb530939ac8d828ed040cbbf1e678a6c2d Mon Sep 17 00:00:00 2001 From: Vladislav Pirtan Date: Wed, 7 Nov 2018 20:36:28 +0300 Subject: [PATCH 07/27] setup.py fix --- final_task/setup.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/final_task/setup.py b/final_task/setup.py index 5e2ea06..193b109 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -1,10 +1,10 @@ -from setuptools import setup, find_packages +from setuptools import setup, find_packages setup( - name='pycalc', - version='0.1', - author='Vladislav Pirtan', - author_email='Vpirtan@yandex.ru', - packages=find_packages(), - entry_points={'console_scripts': ['pycalc=pycalc.__main__:main']}, + name='pycalc', + version='0.1', + author='Vladislav Pirtan', + author_email='Vpirtan@yandex.ru', + packages=find_packages(), + entry_points={'console_scripts': ['pycalc=pycalc.__main__:main']}, ) From f2a5076127b13171509f84dda0901ad53bf2b6f5 Mon Sep 17 00:00:00 2001 From: Vladislav Pirtan Date: Sat, 10 Nov 2018 00:25:38 +0300 Subject: [PATCH 08/27] improved alg --- final_task/pycalc/__main__.py | 4 +- final_task/pycalc/pycalc.py | 97 ++++++++++++++++------------------- 2 files changed, 45 insertions(+), 56 deletions(-) diff --git a/final_task/pycalc/__main__.py b/final_task/pycalc/__main__.py index f5e57ca..7960c53 100644 --- a/final_task/pycalc/__main__.py +++ b/final_task/pycalc/__main__.py @@ -1,5 +1,5 @@ import sys -from .pycalc import pol, shunting_yard +from .pycalc import postfix_eval_alg, shunting_yard_alg HELP = 'usage: pycalc [-h] EXPRESSION\nPure-python command-line calculator.\npositional arguments:' \ '\n EXPRESSION expression string to evaluate' @@ -10,7 +10,7 @@ def main(): if sys.argv[2] == '--help': print(HELP) else: - print(pol(shunting_yard(sys.argv[2]))) + print(postfix_eval_alg(shunting_yard_alg(sys.argv[2]))) except Exception as err: print('ERROR:', err) diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index 4e0ca64..714c971 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -1,13 +1,10 @@ import math import sys +from string import ascii_letters, digits STR_DIGITS = '0123456789' -STR_OPERATOR = '^*/%+-<=>!' -MATH_CONST = ['pi', 'e', 'tau', 'inf', 'nan'] -ACCURACY = 1e-100 PREFIX_CONST = '#C' PREFIX_FUNC = '#F' - PRIORITY_DICT = { '^': 5, '+-': 5, @@ -51,7 +48,7 @@ def stack_push(stack, token): return buf_str -def shunting_yard(input_str): +def shunting_yard_alg(input_str): if len(input_str) == 0: raise Exception('empty input') stack = [] @@ -62,36 +59,20 @@ def shunting_yard(input_str): i = 0 while i < len(input_str): token = input_str[i] + if token == ' ': output_str += ' ' - i += 1 - continue + token = last_token - if ((token >= 'A') & (token <= 'Z')) \ - or ((token >= 'a') & (token <= 'z')): + elif token in ascii_letters \ + or token == '_': func_buf += token - last_token = token - i += 1 - continue - elif func_buf: - if token in STR_OPERATOR + ')': - output_str += PREFIX_CONST + func_buf - func_buf = '' - elif token == '(': - stack.append(PREFIX_FUNC + func_buf) - func_buf = '' - stack.append('(') - last_token = token - i += 1 - continue - else: - func_buf += token - last_token = token - i += 1 - continue - if token in STR_DIGITS: - output_str += token + elif token in digits: + if func_buf: + func_buf += token + else: + output_str += token elif token == '.': output_str += '.' @@ -99,37 +80,44 @@ def shunting_yard(input_str): elif token == ',': count_args += 1 output_str += ' ' - token = stack[-1] - while token != '(': + opr = stack[-1] + while opr != '(': output_str += ' ' + stack.pop() + ' ' - token = stack[-1] + opr = stack[-1] elif token == '(': + if func_buf: + stack.append(PREFIX_FUNC + func_buf) + func_buf = '' stack.append('(') elif token == ')': - token = stack[-1] - while token != '(': + if func_buf: + output_str += PREFIX_CONST + func_buf + func_buf = '' + opr = stack[-1] + while opr != '(': output_str += ' ' + stack.pop() + ' ' if len(stack) != 0: - token = stack[-1] + opr = stack[-1] else: raise Exception('unpaired brackets') - else: - stack.pop() - if len(stack) != 0: - if PREFIX_FUNC in stack[-1]: - output_str += ' ' + str(count_args) + stack.pop() - count_args = 1 - token = ')' + stack.pop() + if len(stack) != 0: + if PREFIX_FUNC in stack[-1]: + output_str += ' ' + str(count_args) + stack.pop() + count_args = 1 + + elif token in ascii_letters: + if func_buf: + output_str += PREFIX_CONST + func_buf + func_buf = '' - elif token in STR_OPERATOR: next_token = input_str[i + 1] output_str += ' ' - - if (token == '-') & (last_token in STR_OPERATOR + '('): + if (token == '-') & (last_token in ascii_letters + '('): output_str += stack_push(stack, '+-') - elif (token == '+') & (last_token in STR_OPERATOR): + elif (token == '+') & (last_token in ascii_letters): pass elif (token in ['<', '>', '!', '=']) & (next_token == '='): token += next_token @@ -162,7 +150,7 @@ def shunting_yard(input_str): return output_str -def pol(input_str): +def postfix_eval_alg(input_str): stack = [] input_list = input_str.split(' ') for token in input_list: @@ -220,14 +208,15 @@ def pol(input_str): elif PREFIX_CONST in token: stack.append(getattr(math, token[2:])) elif PREFIX_FUNC in token: + pos = token.find(PREFIX_FUNC) args_list = [] - for i in range(int(token[0])): + for i in range(int(token[:pos])): args_list.append(stack.pop()) - if token[3:] in FUNCTION_DICT: - x = FUNCTION_DICT[token[3:]](*args_list[::-1]) + if token[pos + len(PREFIX_FUNC):] in FUNCTION_DICT: + res = FUNCTION_DICT[token[3:]](*args_list[::-1]) else: - x = getattr(math, token[3:])(*args_list[::-1]) - stack.append(x) + res = getattr(math, token[3:])(*args_list[::-1]) + stack.append(res) else: stack.append(float(token)) if len(stack) > 1: @@ -236,4 +225,4 @@ def pol(input_str): if __name__ == '__main__': - print(pol(shunting_yard(sys.argv[1]))) + print(postfix_eval_alg(shunting_yard_alg(sys.argv[1]))) From 2cf393d8686ea700917bd53002efbc300f87dfde Mon Sep 17 00:00:00 2001 From: Vladislav Pirtan Date: Sat, 10 Nov 2018 00:30:13 +0300 Subject: [PATCH 09/27] bug fix --- final_task/pycalc/pycalc.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index 714c971..acbbe25 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -2,7 +2,7 @@ import sys from string import ascii_letters, digits -STR_DIGITS = '0123456789' +STR_OPERATOR = '+-*^/%<>=!' PREFIX_CONST = '#C' PREFIX_FUNC = '#F' PRIORITY_DICT = { @@ -108,16 +108,16 @@ def shunting_yard_alg(input_str): output_str += ' ' + str(count_args) + stack.pop() count_args = 1 - elif token in ascii_letters: + elif token in STR_OPERATOR: if func_buf: output_str += PREFIX_CONST + func_buf func_buf = '' next_token = input_str[i + 1] output_str += ' ' - if (token == '-') & (last_token in ascii_letters + '('): + if (token == '-') & (last_token in STR_OPERATOR + '('): output_str += stack_push(stack, '+-') - elif (token == '+') & (last_token in ascii_letters): + elif (token == '+') & (last_token in STR_OPERATOR): pass elif (token in ['<', '>', '!', '=']) & (next_token == '='): token += next_token From 0d54a92f4df582b3ea36b07153b1199f41cd177a Mon Sep 17 00:00:00 2001 From: Vladislav Pirtan Date: Mon, 12 Nov 2018 18:12:05 +0300 Subject: [PATCH 10/27] add doc --- final_task/pycalc/__main__.py | 2 +- final_task/pycalc/pycalc.py | 40 +++++++++++++++++++++-------------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/final_task/pycalc/__main__.py b/final_task/pycalc/__main__.py index 7960c53..a3151ea 100644 --- a/final_task/pycalc/__main__.py +++ b/final_task/pycalc/__main__.py @@ -10,7 +10,7 @@ def main(): if sys.argv[2] == '--help': print(HELP) else: - print(postfix_eval_alg(shunting_yard_alg(sys.argv[2]))) + print(postfix_eval(shunting_yard_alg(sys.argv[2]))) except Exception as err: print('ERROR:', err) diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index acbbe25..819b0c1 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -1,7 +1,7 @@ import math -import sys from string import ascii_letters, digits + STR_OPERATOR = '+-*^/%<>=!' PREFIX_CONST = '#C' PREFIX_FUNC = '#F' @@ -24,31 +24,39 @@ FUNCTION_DICT = {'abs': abs, 'pow': pow, 'round': round} -def stack_push(stack, token): +def stack_push(stack, opr): + """stack_push(stack, opr) + +This function is part of the shunting yard algorithm""" + buf_str = '' - if token == '^' or token == '+-': - for i in range(len(stack)): - if stack[-1] == '(': + if opr == '^' or opr == '+-': + for opr2 in stack[::-1]: + if opr2 == '(': break - elif PRIORITY_DICT[token] < PRIORITY_DICT[stack[-1]]: + elif PRIORITY_DICT[opr] < PRIORITY_DICT[opr2]: buf_str += stack.pop() + ' ' else: break - stack.append(token) + stack.append(opr) else: - for i in range(len(stack)): - if stack[-1] == '(': + for opr2 in stack[::-1]: + if opr2 == '(': break - elif PRIORITY_DICT[token] <= PRIORITY_DICT[stack[-1]]: + elif PRIORITY_DICT[opr] <= PRIORITY_DICT[opr2]: buf_str += stack.pop() + ' ' else: break - stack.append(token) + stack.append(opr) return buf_str def shunting_yard_alg(input_str): + """shunting_yard_alg(input_str) + +The function converts a mathematical expression written in infix notation into postfix notation.""" + if len(input_str) == 0: raise Exception('empty input') stack = [] @@ -150,7 +158,11 @@ def shunting_yard_alg(input_str): return output_str -def postfix_eval_alg(input_str): +def postfix_eval(input_str): + """postfix_eval(input_str) + +The function calculates the mathematical expression written in postfix notation. + """ stack = [] input_list = input_str.split(' ') for token in input_list: @@ -222,7 +234,3 @@ def postfix_eval_alg(input_str): if len(stack) > 1: raise Exception('invalid input') return stack.pop() - - -if __name__ == '__main__': - print(postfix_eval_alg(shunting_yard_alg(sys.argv[1]))) From 9328063b87a218198eb36cab67d0ed0ea0bcfeba Mon Sep 17 00:00:00 2001 From: Vladislav Pirtan Date: Mon, 12 Nov 2018 18:15:10 +0300 Subject: [PATCH 11/27] bug fix --- final_task/pycalc/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/final_task/pycalc/__main__.py b/final_task/pycalc/__main__.py index a3151ea..2839718 100644 --- a/final_task/pycalc/__main__.py +++ b/final_task/pycalc/__main__.py @@ -1,5 +1,5 @@ import sys -from .pycalc import postfix_eval_alg, shunting_yard_alg +from .pycalc import postfix_eval, shunting_yard_alg HELP = 'usage: pycalc [-h] EXPRESSION\nPure-python command-line calculator.\npositional arguments:' \ '\n EXPRESSION expression string to evaluate' From 464621cfcf247782e74cbf8a6a94594ed42b3dd4 Mon Sep 17 00:00:00 2001 From: Vladislav Pirtan Date: Mon, 12 Nov 2018 19:05:48 +0300 Subject: [PATCH 12/27] add argparse --- final_task/pycalc/__main__.py | 19 ------------------- final_task/pycalc/pycalc.py | 12 ++++++++++++ final_task/setup.py | 4 ++-- 3 files changed, 14 insertions(+), 21 deletions(-) delete mode 100644 final_task/pycalc/__main__.py diff --git a/final_task/pycalc/__main__.py b/final_task/pycalc/__main__.py deleted file mode 100644 index 2839718..0000000 --- a/final_task/pycalc/__main__.py +++ /dev/null @@ -1,19 +0,0 @@ -import sys -from .pycalc import postfix_eval, shunting_yard_alg - -HELP = 'usage: pycalc [-h] EXPRESSION\nPure-python command-line calculator.\npositional arguments:' \ - '\n EXPRESSION expression string to evaluate' - - -def main(): - try: - if sys.argv[2] == '--help': - print(HELP) - else: - print(postfix_eval(shunting_yard_alg(sys.argv[2]))) - except Exception as err: - print('ERROR:', err) - - -if __name__ == '__main__': - main() diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index 819b0c1..e97a581 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -1,4 +1,5 @@ import math +import argparse from string import ascii_letters, digits @@ -234,3 +235,14 @@ def postfix_eval(input_str): if len(stack) > 1: raise Exception('invalid input') return stack.pop() + +def main(): + """Entry point""" + + parser = argparse.ArgumentParser(add_help=True, description="Pure-python command-line calculator.") + parser.add_argument("EXPRESSION", type=str, help="expression string to evaluate") + args = parser.parse_args() + try: + print(postfix_eval(shunting_yard_alg(args.EXPRESSION))) + except Exception as err: + print('ERROR:', err) \ No newline at end of file diff --git a/final_task/setup.py b/final_task/setup.py index 193b109..9741c1b 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -2,9 +2,9 @@ setup( name='pycalc', - version='0.1', + version='0.5', author='Vladislav Pirtan', author_email='Vpirtan@yandex.ru', packages=find_packages(), - entry_points={'console_scripts': ['pycalc=pycalc.__main__:main']}, + entry_points={'console_scripts': ['pycalc=pycalc.pycalc:main']}, ) From 391b5750af221cbcc49deee8bbe4f12443e3f850 Mon Sep 17 00:00:00 2001 From: Vladislav Pirtan Date: Mon, 12 Nov 2018 19:11:23 +0300 Subject: [PATCH 13/27] bug fix --- 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 e97a581..d7d93ec 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -2,7 +2,6 @@ import argparse from string import ascii_letters, digits - STR_OPERATOR = '+-*^/%<>=!' PREFIX_CONST = '#C' PREFIX_FUNC = '#F' @@ -236,6 +235,7 @@ def postfix_eval(input_str): raise Exception('invalid input') return stack.pop() + def main(): """Entry point""" @@ -245,4 +245,4 @@ def main(): try: print(postfix_eval(shunting_yard_alg(args.EXPRESSION))) except Exception as err: - print('ERROR:', err) \ No newline at end of file + print('ERROR:', err) From 954ce00ef280fd430a64e12f835ec7d5cde1a704 Mon Sep 17 00:00:00 2001 From: Vladislav Pirtan Date: Mon, 19 Nov 2018 22:01:02 +0300 Subject: [PATCH 14/27] 1 --- final_task/pycalc/pycalc.py | 228 ------------------------------------ 1 file changed, 228 deletions(-) delete mode 100644 final_task/pycalc/pycalc.py diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py deleted file mode 100644 index acbbe25..0000000 --- a/final_task/pycalc/pycalc.py +++ /dev/null @@ -1,228 +0,0 @@ -import math -import sys -from string import ascii_letters, digits - -STR_OPERATOR = '+-*^/%<>=!' -PREFIX_CONST = '#C' -PREFIX_FUNC = '#F' -PRIORITY_DICT = { - '^': 5, - '+-': 5, - '*': 4, - '/': 4, - '//': 4, - '%': 4, - '+': 3, - '-': 3, - '<=': 2, - '>=': 2, - '<': 1, - '>': 1, - '==': 1, - '!=': 1} -BOOL_DICT = {'True': True, 'False': False} -FUNCTION_DICT = {'abs': abs, 'pow': pow, 'round': round} - - -def stack_push(stack, token): - buf_str = '' - if token == '^' or token == '+-': - for i in range(len(stack)): - if stack[-1] == '(': - break - elif PRIORITY_DICT[token] < PRIORITY_DICT[stack[-1]]: - buf_str += stack.pop() + ' ' - else: - break - stack.append(token) - - else: - for i in range(len(stack)): - if stack[-1] == '(': - break - elif PRIORITY_DICT[token] <= PRIORITY_DICT[stack[-1]]: - buf_str += stack.pop() + ' ' - else: - break - stack.append(token) - return buf_str - - -def shunting_yard_alg(input_str): - if len(input_str) == 0: - raise Exception('empty input') - stack = [] - output_str = '' - func_buf = '' - last_token = '' - count_args = 1 - i = 0 - while i < len(input_str): - token = input_str[i] - - if token == ' ': - output_str += ' ' - token = last_token - - elif token in ascii_letters \ - or token == '_': - func_buf += token - - elif token in digits: - if func_buf: - func_buf += token - else: - output_str += token - - elif token == '.': - output_str += '.' - - elif token == ',': - count_args += 1 - output_str += ' ' - opr = stack[-1] - while opr != '(': - output_str += ' ' + stack.pop() + ' ' - opr = stack[-1] - - elif token == '(': - if func_buf: - stack.append(PREFIX_FUNC + func_buf) - func_buf = '' - stack.append('(') - - elif token == ')': - if func_buf: - output_str += PREFIX_CONST + func_buf - func_buf = '' - opr = stack[-1] - while opr != '(': - output_str += ' ' + stack.pop() + ' ' - if len(stack) != 0: - opr = stack[-1] - else: - raise Exception('unpaired brackets') - stack.pop() - if len(stack) != 0: - if PREFIX_FUNC in stack[-1]: - output_str += ' ' + str(count_args) + stack.pop() - count_args = 1 - - elif token in STR_OPERATOR: - if func_buf: - output_str += PREFIX_CONST + func_buf - func_buf = '' - - next_token = input_str[i + 1] - output_str += ' ' - if (token == '-') & (last_token in STR_OPERATOR + '('): - output_str += stack_push(stack, '+-') - elif (token == '+') & (last_token in STR_OPERATOR): - pass - elif (token in ['<', '>', '!', '=']) & (next_token == '='): - token += next_token - output_str += stack_push(stack, token) - i += 1 - elif token == '=': - raise Exception('unknown operator: "="') - elif (token == '/') & (next_token == '/'): - token += next_token - output_str += stack_push(stack, token) - i += 1 - else: - output_str += stack_push(stack, token) - - else: - raise Exception('unknown operator: "' + token + '"') - - last_token = token - i += 1 - - if func_buf: - output_str += PREFIX_CONST + func_buf - - while stack: - token = stack.pop() - if token == '(': - raise Exception('unpaired brackets') - output_str += ' ' + token - - return output_str - - -def postfix_eval_alg(input_str): - stack = [] - input_list = input_str.split(' ') - for token in input_list: - if token == '': - continue - elif token in PRIORITY_DICT: - if token == '^': - val2 = stack.pop() - val1 = stack.pop() - result = val1 ** val2 - if type(result) is complex: - return 'ERROR: negative number cannot be raised to a fractional power' - stack.append(result) - if token == '+-': - stack[-1] = - stack[-1] - if token == '*': - val2 = stack.pop() - stack.append(stack.pop() * val2) - if token == '/': - val2 = stack.pop() - stack.append(stack.pop() / val2) - if token == '//': - val2 = stack.pop() - stack.append(stack.pop() // val2) - if token == '%': - val2 = stack.pop() - stack.append(stack.pop() % val2) - if token == '+': - val2 = stack.pop() - stack.append(stack.pop() + val2) - if token == '-': - val2 = stack.pop() - val1 = stack.pop() - stack.append(val1 - val2) - if token == '<=': - val2 = stack.pop() - stack.append(stack.pop() <= val2) - if token == '>=': - val2 = stack.pop() - stack.append(stack.pop() >= val2) - if token == '<': - val2 = stack.pop() - stack.append(stack.pop() < val2) - if token == '>': - val2 = stack.pop() - stack.append(stack.pop() > val2) - if token == '==': - val2 = stack.pop() - stack.append(stack.pop() == val2) - if token == '!=': - val2 = stack.pop() - stack.append(stack.pop() != val2) - elif token[2:] in BOOL_DICT: - stack.append(BOOL_DICT[token[2:]]) - elif PREFIX_CONST in token: - stack.append(getattr(math, token[2:])) - elif PREFIX_FUNC in token: - pos = token.find(PREFIX_FUNC) - args_list = [] - for i in range(int(token[:pos])): - args_list.append(stack.pop()) - if token[pos + len(PREFIX_FUNC):] in FUNCTION_DICT: - res = FUNCTION_DICT[token[3:]](*args_list[::-1]) - else: - res = getattr(math, token[3:])(*args_list[::-1]) - stack.append(res) - else: - stack.append(float(token)) - if len(stack) > 1: - raise Exception('invalid input') - return stack.pop() - - -if __name__ == '__main__': - print(postfix_eval_alg(shunting_yard_alg(sys.argv[1]))) From 072efadb0fc25e56e6d609a759e93b429b54b0e2 Mon Sep 17 00:00:00 2001 From: Vladislav Pirtan Date: Tue, 20 Nov 2018 00:39:00 +0300 Subject: [PATCH 15/27] bug fix --- 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 b011c31..7de0749 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -166,7 +166,7 @@ def shunting_yard_alg(input_str): if token == '(': raise Exception('unpaired brackets') output_str += ' ' + token - print(output_str) + return output_str From 7bb5bb8da7bee143356d527a6c158dc263150b74 Mon Sep 17 00:00:00 2001 From: Vladislav Pirtan Date: Tue, 20 Nov 2018 00:44:56 +0300 Subject: [PATCH 16/27] pycodestyle fix --- 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 7de0749..ab76224 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -166,7 +166,7 @@ def shunting_yard_alg(input_str): if token == '(': raise Exception('unpaired brackets') output_str += ' ' + token - + return output_str From ff6464713b5adbc5649ea305c3b689222fca6671 Mon Sep 17 00:00:00 2001 From: Vladislav Pirtan Date: Wed, 21 Nov 2018 22:21:36 +0300 Subject: [PATCH 17/27] 'add_implict_multiplication' --- 2 | 2 + 2' | 1 + final_task/pycalc/pycalc.py | 121 +++++++++++++++++++----------------- 3 files changed, 66 insertions(+), 58 deletions(-) create mode 100644 2 create mode 100644 2' diff --git a/2 b/2 new file mode 100644 index 0000000..03ad58c --- /dev/null +++ b/2 @@ -0,0 +1,2 @@ +1 True == +True diff --git a/2' b/2' new file mode 100644 index 0000000..8d2f5b3 --- /dev/null +++ b/2' @@ -0,0 +1 @@ +ERROR: unknown operator: "'" diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index ab76224..92f29a3 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -5,7 +5,6 @@ LIST_OPERATOR = ['+', '-', '*', '^', '/', '%', '<', '>', '=', '!'] LIST_DIGITS = list(digits) LIST_LETTERS = list(ascii_letters) -PREFIX_CONST = '#C' PREFIX_FUNC = '#F' PRIORITY_DICT = { '^': 5, @@ -26,7 +25,7 @@ FUNCTION_DICT = {'abs': abs, 'pow': pow, 'round': round} -def stack_push(stack, opr): +def _stack_push(stack, opr): """stack_push(stack, opr) This function is part of the shunting yard algorithm""" @@ -54,30 +53,58 @@ def stack_push(stack, opr): return buf_str -def shunting_yard_alg(input_str): +def _const_separator(const, stack, module_func_dict): + if const in BOOL_DICT: + return const + for module in module_func_dict: + if const in module_func_dict[module]: + return str(getattr(module, const)) + else: + i = 0 + l_const = '' + while i < len(const): + l_const += const[i] + r_const = const[i+1:] + for module in module_func_dict: + if l_const in module_func_dict[module]: + return str(getattr(module, l_const)) + ' ' + _stack_push(stack, '*') \ + + _const_separator(r_const, stack, module_func_dict) + i += 1 + else: + raise Exception('unknown constant {}'.format(const)) + + +def shunting_yard_alg(input_str, modules=list()): """shunting_yard_alg(input_str) -The function converts a mathematical expression written in infix notation into postfix notation.""" +The function converts a mathematical expression written in infix notation into postfix notation. +""" + module_func_dict = {module: dir(module) for module in modules} if len(input_str) == 0: raise Exception('empty input') stack = [] output_str = '' func_buf = '' last_token = '' - count_args = 1 - i = 0 - while i < len(input_str): - token = input_str[i] - if token == ' ': + count_args = list() + for token in input_str: + if token == '=' and last_token in ['<', '>', '!', '=']: + output_str += _stack_push(stack, last_token+token) + elif last_token == '/': + if token == '/': + output_str += _stack_push(stack, '//') + else: + output_str += _stack_push(stack, '/') + elif token == ' ': output_str += ' ' token = last_token elif token in LIST_LETTERS \ or token == '_': - if last_token in LIST_DIGITS or last_token == ')': + if last_token == ')': output_str += ' ' - output_str += stack_push(stack, '*') + output_str += _stack_push(stack, '*') func_buf += token elif token in LIST_DIGITS: @@ -90,7 +117,7 @@ def shunting_yard_alg(input_str): output_str += '.' elif token == ',': - count_args += 1 + count_args[-1] += 1 output_str += ' ' opr = stack[-1] while opr != '(': @@ -99,67 +126,52 @@ def shunting_yard_alg(input_str): elif token == '(': if func_buf: - stack.append(PREFIX_FUNC + func_buf) + stack.append(func_buf) + count_args.append(1) func_buf = '' elif last_token == ')' or last_token in LIST_DIGITS: output_str += ' ' - output_str += stack_push(stack, '*') + output_str += _stack_push(stack, '*') stack.append('(') elif token == ')': if last_token == '(': - count_args = 0 + count_args[-1] = 0 if func_buf: - output_str += ' ' + PREFIX_CONST + func_buf + output_str += _const_separator(func_buf, stack, module_func_dict) func_buf = '' - if stack: - opr = stack[-1] - while opr != '(': + for opr in stack[::-1]: + if opr == '(': + stack.pop() + break + else: output_str += ' ' + stack.pop() + ' ' - if len(stack) != 0: - opr = stack[-1] - else: - raise Exception('unpaired brackets') - stack.pop() else: raise Exception('unpaired brackets') if stack: - if PREFIX_FUNC in stack[-1]: - output_str += ' ' + str(count_args) + stack.pop() - count_args = 1 + if stack[-1] not in LIST_OPERATOR and stack[-1] != '(': + output_str += ' ' + str(count_args.pop()) + PREFIX_FUNC + stack.pop() elif token in LIST_OPERATOR: if func_buf: - output_str += PREFIX_CONST + func_buf + output_str += _const_separator(func_buf, stack, module_func_dict) func_buf = '' - - next_token = input_str[i + 1] output_str += ' ' if (token == '-') & (last_token in LIST_OPERATOR or last_token in ['', '(']): - output_str += stack_push(stack, '+-') + output_str += _stack_push(stack, '+-') elif (token == '+') & (last_token in LIST_OPERATOR): pass - elif (token in ['<', '>', '!', '=']) & (next_token == '='): - token += next_token - output_str += stack_push(stack, token) - i += 1 - elif token == '=': - raise Exception('unknown operator: "="') - elif (token == '/') & (next_token == '/'): - token += next_token - output_str += stack_push(stack, token) - i += 1 + elif token in ['<', '>', '!', '=', '/']: + pass else: - output_str += stack_push(stack, token) + output_str += _stack_push(stack, token) else: raise Exception('unknown operator: "{}"'.format(token)) - last_token = token - i += 1 if func_buf: - output_str += PREFIX_CONST + func_buf + output_str += _const_separator(func_buf, stack, module_func_dict) while stack: token = stack.pop() @@ -174,8 +186,10 @@ def postfix_eval(input_str, modules=tuple()): """postfix_eval(input_str) The function calculates the mathematical expression written in postfix notation. + """ + print(input_str) module_func_dict = {module: dir(module) for module in modules} stack = [] input_list = input_str.split(' ') @@ -229,17 +243,8 @@ def postfix_eval(input_str, modules=tuple()): if token == '!=': val2 = stack.pop() stack.append(stack.pop() != val2) - elif token[2:] in BOOL_DICT: - stack.append(BOOL_DICT[token[2:]]) - elif PREFIX_CONST in token: - pos = token.find(PREFIX_CONST) - const_name = token[pos + len(PREFIX_CONST):] - for module in module_func_dict: - if const_name in module_func_dict[module]: - stack.append(getattr(module, const_name)) - break - else: - raise Exception('unknown constant {}'.format(const_name)) + elif token in BOOL_DICT: + stack.append(BOOL_DICT[token]) elif PREFIX_FUNC in token: pos = token.find(PREFIX_FUNC) args_list = [] @@ -248,7 +253,7 @@ def postfix_eval(input_str, modules=tuple()): func_name = token[pos + len(PREFIX_FUNC):] for module in module_func_dict: if func_name in module_func_dict[module]: - res = getattr(module, token[3:])(*args_list[::-1]) + res = getattr(module, func_name)(*args_list[::-1]) break else: if func_name in FUNCTION_DICT: @@ -277,7 +282,7 @@ def main(): args.MODULE = ['math'] modules = [importlib.import_module(module) for module in args.MODULE] try: - print(postfix_eval(shunting_yard_alg(args.EXPRESSION), modules)) + print(postfix_eval(shunting_yard_alg(args.EXPRESSION, modules), modules)) except Exception as err: print('ERROR:', err) From 8e8581fdd73c055cd78e6d9046d5d3bdba83bab3 Mon Sep 17 00:00:00 2001 From: Vladislav Pirtan Date: Wed, 21 Nov 2018 22:25:35 +0300 Subject: [PATCH 18/27] 'bug_fix' --- final_task/pycalc/pycalc.py | 1 - 1 file changed, 1 deletion(-) diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index 92f29a3..1d6448f 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -189,7 +189,6 @@ def postfix_eval(input_str, modules=tuple()): """ - print(input_str) module_func_dict = {module: dir(module) for module in modules} stack = [] input_list = input_str.split(' ') From 585c86d08e72db7518448c80fcd7d46755721bf0 Mon Sep 17 00:00:00 2001 From: Vladislav Pirtan Date: Wed, 21 Nov 2018 23:08:14 +0300 Subject: [PATCH 19/27] fix division --- 3 | 2 ++ 3' | 1 + final_task/pycalc/pycalc.py | 24 ++++++++++++++---------- 3 files changed, 17 insertions(+), 10 deletions(-) create mode 100644 3 create mode 100644 3' diff --git a/3 b/3 new file mode 100644 index 0000000..b69e991 --- /dev/null +++ b/3 @@ -0,0 +1,2 @@ +5 +5.0 diff --git a/3' b/3' new file mode 100644 index 0000000..8d2f5b3 --- /dev/null +++ b/3' @@ -0,0 +1 @@ +ERROR: unknown operator: "'" diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index 1d6448f..85802cc 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -89,16 +89,12 @@ def shunting_yard_alg(input_str, modules=list()): last_token = '' count_args = list() for token in input_str: - if token == '=' and last_token in ['<', '>', '!', '=']: - output_str += _stack_push(stack, last_token+token) - elif last_token == '/': - if token == '/': - output_str += _stack_push(stack, '//') - else: - output_str += _stack_push(stack, '/') - elif token == ' ': + if token != '=' and last_token in ['<', '>', '!', '=']\ + or token != '/' and last_token == '/': + output_str += _stack_push(stack, last_token) + + if token == ' ': output_str += ' ' - token = last_token elif token in LIST_LETTERS \ or token == '_': @@ -161,8 +157,16 @@ def shunting_yard_alg(input_str, modules=list()): output_str += _stack_push(stack, '+-') elif (token == '+') & (last_token in LIST_OPERATOR): pass - elif token in ['<', '>', '!', '=', '/']: + elif token in ['<', '>', '!']: pass + elif token == '=': + if last_token in ['<', '>', '!', '=']: + token = last_token+token + output_str += _stack_push(stack, token) + elif token == '/': + if last_token == '/': + token = '//' + output_str += _stack_push(stack, token) else: output_str += _stack_push(stack, token) From 0ef084b8f9ab03f272d9a14a081c88091a245de7 Mon Sep 17 00:00:00 2001 From: Vladislav Pirtan Date: Thu, 22 Nov 2018 00:48:02 +0300 Subject: [PATCH 20/27] bug fix2 --- final_task/pycalc/pycalc.py | 39 ++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index 85802cc..a363fe8 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -58,7 +58,7 @@ def _const_separator(const, stack, module_func_dict): return const for module in module_func_dict: if const in module_func_dict[module]: - return str(getattr(module, const)) + return ' ' + str(getattr(module, const)) else: i = 0 l_const = '' @@ -67,7 +67,7 @@ def _const_separator(const, stack, module_func_dict): r_const = const[i+1:] for module in module_func_dict: if l_const in module_func_dict[module]: - return str(getattr(module, l_const)) + ' ' + _stack_push(stack, '*') \ + return ' ' + str(getattr(module, l_const)) + ' ' + _stack_push(stack, '*') \ + _const_separator(r_const, stack, module_func_dict) i += 1 else: @@ -87,20 +87,36 @@ def shunting_yard_alg(input_str, modules=list()): output_str = '' func_buf = '' last_token = '' + buf_opr = '' count_args = list() for token in input_str: - if token != '=' and last_token in ['<', '>', '!', '=']\ - or token != '/' and last_token == '/': - output_str += _stack_push(stack, last_token) + if buf_opr in ['=', '<', '>', '!']: + if token == '=': + output_str += _stack_push(stack, buf_opr+token) + buf_opr = '' + continue + else: + raise Exception('unknown operator: "="') + elif buf_opr == '/': + if token == '/': + output_str += _stack_push(stack, '//') + buf_opr = '' + continue + else: + output_str += _stack_push(stack, '/') + buf_opr = '' if token == ' ': output_str += ' ' + token = last_token elif token in LIST_LETTERS \ or token == '_': if last_token == ')': output_str += ' ' output_str += _stack_push(stack, '*') + if last_token in LIST_DIGITS: + output_str += _stack_push(stack, '*') func_buf += token elif token in LIST_DIGITS: @@ -157,16 +173,8 @@ def shunting_yard_alg(input_str, modules=list()): output_str += _stack_push(stack, '+-') elif (token == '+') & (last_token in LIST_OPERATOR): pass - elif token in ['<', '>', '!']: - pass - elif token == '=': - if last_token in ['<', '>', '!', '=']: - token = last_token+token - output_str += _stack_push(stack, token) - elif token == '/': - if last_token == '/': - token = '//' - output_str += _stack_push(stack, token) + elif token in ['=', '<', '>', '!', '/']: + buf_opr = token else: output_str += _stack_push(stack, token) @@ -192,7 +200,6 @@ def postfix_eval(input_str, modules=tuple()): The function calculates the mathematical expression written in postfix notation. """ - module_func_dict = {module: dir(module) for module in modules} stack = [] input_list = input_str.split(' ') From 7850c01fbfea612d7094f6eb4afcb967853c427b Mon Sep 17 00:00:00 2001 From: Vladislav Pirtan Date: Mon, 17 Dec 2018 19:17:34 +0300 Subject: [PATCH 21/27] refactoring --- 2 | 2 - 2' | 1 - 3 | 2 - 3' | 1 - final_task/pycalc/pycalc.py | 195 +++++++++++++++++++++--------------- 5 files changed, 114 insertions(+), 87 deletions(-) delete mode 100644 2 delete mode 100644 2' delete mode 100644 3 delete mode 100644 3' diff --git a/2 b/2 deleted file mode 100644 index 03ad58c..0000000 --- a/2 +++ /dev/null @@ -1,2 +0,0 @@ -1 True == -True diff --git a/2' b/2' deleted file mode 100644 index 8d2f5b3..0000000 --- a/2' +++ /dev/null @@ -1 +0,0 @@ -ERROR: unknown operator: "'" diff --git a/3 b/3 deleted file mode 100644 index b69e991..0000000 --- a/3 +++ /dev/null @@ -1,2 +0,0 @@ -5 -5.0 diff --git a/3' b/3' deleted file mode 100644 index 8d2f5b3..0000000 --- a/3' +++ /dev/null @@ -1 +0,0 @@ -ERROR: unknown operator: "'" diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index a363fe8..4ff1a0f 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -2,6 +2,7 @@ import importlib from string import ascii_letters, digits +# const variable LIST_OPERATOR = ['+', '-', '*', '^', '/', '%', '<', '>', '=', '!'] LIST_DIGITS = list(digits) LIST_LETTERS = list(ascii_letters) @@ -23,184 +24,216 @@ '!=': 1} BOOL_DICT = {'True': True, 'False': False} FUNCTION_DICT = {'abs': abs, 'pow': pow, 'round': round} +# global variable dict, key: str module name, value: list containing fuction names from [key] +module_func_dict = {} +# +stack_opr = [] -def _stack_push(stack, opr): - """stack_push(stack, opr) +def set_user_mod(modules=tuple()): + """set_user_mod(modules) -This function is part of the shunting yard algorithm""" +Initialize global variable module_func_dict - buf_str = '' + """ + + modules_list = [importlib.import_module(module) for module in modules] + global module_func_dict + module_func_dict = {module: dir(module) for module in modules_list} + + +def _stack_push(opr): + """stack_push(opr) + +This function is part of the shunting yard algorithm. Takes an operator and push it on the stack. +The operator pushes a stack of other operators. + +""" + + global stack_opr + buf = list() if opr == '^' or opr == '+-': - for opr2 in stack[::-1]: + for opr2 in stack_opr[::-1]: if opr2 == '(': break elif PRIORITY_DICT[opr] < PRIORITY_DICT[opr2]: - buf_str += stack.pop() + ' ' + buf.extend([' ', stack_opr.pop()]) else: break - stack.append(opr) + stack_opr.append(opr) else: - for opr2 in stack[::-1]: + for opr2 in stack_opr[::-1]: if opr2 == '(': break elif PRIORITY_DICT[opr] <= PRIORITY_DICT[opr2]: - buf_str += stack.pop() + ' ' + buf.extend([' ', stack_opr.pop()]) else: break - stack.append(opr) - return buf_str + stack_opr.append(opr) + + buf.append(' ') + return buf + +def _const_separator(const): + """_const_separator(const) -def _const_separator(const, stack, module_func_dict): +Splits constants which are implicit multiplication. Example "pie" equal "pi*e". Constants are searched in the modules +defined in the variable module_func_dict. + +""" + global module_func_dict if const in BOOL_DICT: return const for module in module_func_dict: if const in module_func_dict[module]: - return ' ' + str(getattr(module, const)) + return [' ', str(getattr(module, const))] else: + # Recursion i = 0 l_const = '' while i < len(const): l_const += const[i] - r_const = const[i+1:] + r_const = const[i + 1:] for module in module_func_dict: if l_const in module_func_dict[module]: - return ' ' + str(getattr(module, l_const)) + ' ' + _stack_push(stack, '*') \ - + _const_separator(r_const, stack, module_func_dict) + return [' ', str(getattr(module, l_const)), *_stack_push('*'), *_const_separator(r_const)] i += 1 else: raise Exception('unknown constant {}'.format(const)) -def shunting_yard_alg(input_str, modules=list()): - """shunting_yard_alg(input_str) +def postfix_translat(input_str): + """postfix_translat(input_str) The function converts a mathematical expression written in infix notation into postfix notation. +Implements an Shunting-yard algorithm """ - module_func_dict = {module: dir(module) for module in modules} - if len(input_str) == 0: + global module_func_dict + global stack_opr + + if not input_str: raise Exception('empty input') - stack = [] - output_str = '' + + output_list = list() func_buf = '' last_token = '' - buf_opr = '' count_args = list() - for token in input_str: - if buf_opr in ['=', '<', '>', '!']: - if token == '=': - output_str += _stack_push(stack, buf_opr+token) - buf_opr = '' - continue - else: - raise Exception('unknown operator: "="') - elif buf_opr == '/': - if token == '/': - output_str += _stack_push(stack, '//') - buf_opr = '' - continue - else: - output_str += _stack_push(stack, '/') - buf_opr = '' - + for i, token in enumerate(input_str): if token == ' ': - output_str += ' ' - token = last_token + output_list.append(' ') + continue elif token in LIST_LETTERS \ or token == '_': if last_token == ')': - output_str += ' ' - output_str += _stack_push(stack, '*') + output_list.extend(_stack_push('*')) if last_token in LIST_DIGITS: - output_str += _stack_push(stack, '*') + output_list.extend(_stack_push('*')) func_buf += token elif token in LIST_DIGITS: if func_buf: func_buf += token else: - output_str += token + if last_token in PRIORITY_DICT: + output_list.append(' ') + output_list.append(token) elif token == '.': - output_str += '.' + output_list.append('.') elif token == ',': - count_args[-1] += 1 - output_str += ' ' - opr = stack[-1] - while opr != '(': - output_str += ' ' + stack.pop() + ' ' - opr = stack[-1] + if count_args: + count_args[-1] += 1 + output_list.append(' ') + opr = stack_opr[-1] + while opr != '(': + output_list.extend([' ', stack_opr.pop()]) + opr = stack_opr[-1] + else: + raise Exception('Using the symbol "," outside the function') elif token == '(': if func_buf: - stack.append(func_buf) + stack_opr.append(func_buf) count_args.append(1) func_buf = '' elif last_token == ')' or last_token in LIST_DIGITS: - output_str += ' ' - output_str += _stack_push(stack, '*') - stack.append('(') + output_list.extend(_stack_push('*')) + stack_opr.append('(') elif token == ')': if last_token == '(': count_args[-1] = 0 if func_buf: - output_str += _const_separator(func_buf, stack, module_func_dict) + output_list.extend(_const_separator(func_buf)) func_buf = '' - for opr in stack[::-1]: + for opr in stack_opr[::-1]: if opr == '(': - stack.pop() + stack_opr.pop() break else: - output_str += ' ' + stack.pop() + ' ' + output_list.extend([' ', stack_opr.pop()]) else: raise Exception('unpaired brackets') - if stack: - if stack[-1] not in LIST_OPERATOR and stack[-1] != '(': - output_str += ' ' + str(count_args.pop()) + PREFIX_FUNC + stack.pop() + if stack_opr: + if stack_opr[-1] not in LIST_OPERATOR and stack_opr[-1] != '(': + output_list.extend([' ', str(count_args.pop()), PREFIX_FUNC, stack_opr.pop()]) elif token in LIST_OPERATOR: if func_buf: - output_str += _const_separator(func_buf, stack, module_func_dict) + output_list.extend(_const_separator(func_buf)) func_buf = '' - output_str += ' ' if (token == '-') & (last_token in LIST_OPERATOR or last_token in ['', '(']): - output_str += _stack_push(stack, '+-') + output_list.extend(_stack_push('+-')) elif (token == '+') & (last_token in LIST_OPERATOR): pass - elif token in ['=', '<', '>', '!', '/']: - buf_opr = token + elif token in ['=', '<', '>', '!']: + if last_token in ['!=', '==', '<=', '>=', '!=']: + continue + next_token = input_str[i + 1] + if next_token == '=': + token = token + next_token + output_list.extend(_stack_push(token)) + elif token == '=': + raise Exception('unknown operator: "="') + else: + output_list.extend(_stack_push(token)) + elif token == '/': + next_token = input_str[i + 1] + if next_token == '/': + output_list.extend(_stack_push(token + next_token)) + else: + output_list.extend(_stack_push(token)) else: - output_str += _stack_push(stack, token) + output_list.extend(_stack_push(token)) else: raise Exception('unknown operator: "{}"'.format(token)) last_token = token if func_buf: - output_str += _const_separator(func_buf, stack, module_func_dict) + output_list.extend(_const_separator(func_buf)) - while stack: - token = stack.pop() + while stack_opr: + token = stack_opr.pop() if token == '(': raise Exception('unpaired brackets') - output_str += ' ' + token - - return output_str + output_list.extend([' ', token]) + return ''.join(output_list) -def postfix_eval(input_str, modules=tuple()): +def postfix_eval(input_str): """postfix_eval(input_str) The function calculates the mathematical expression written in postfix notation. """ - module_func_dict = {module: dir(module) for module in modules} + + global module_func_dict stack = [] input_list = input_str.split(' ') for token in input_list: @@ -282,7 +315,7 @@ def main(): """Entry point""" parser = argparse.ArgumentParser(add_help=True, description="Pure-python command-line calculator.") - parser.add_argument("EXPRESSION", type=str, help="expression string to evaluate") + parser.add_argument("EXPRESSION", type=str, help="expression string to evaluate") parser.add_argument('-m', '--use-modules', dest='MODULE', type=str, nargs='+', action='store', help="additional modules to use") args = parser.parse_args() @@ -290,9 +323,9 @@ def main(): args.MODULE.append('math') else: args.MODULE = ['math'] - modules = [importlib.import_module(module) for module in args.MODULE] try: - print(postfix_eval(shunting_yard_alg(args.EXPRESSION, modules), modules)) + set_user_mod(tuple(args.MODULE)) + print(postfix_eval(postfix_translat(args.EXPRESSION))) except Exception as err: print('ERROR:', err) From 4bc4a30572ae5b2af2cf06f79921a6b046d57b19 Mon Sep 17 00:00:00 2001 From: Vladislav Pirtan Date: Mon, 17 Dec 2018 19:24:17 +0300 Subject: [PATCH 22/27] hot bug fix --- final_task/pycalc/pycalc.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index 4ff1a0f..1617fcf 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -153,6 +153,7 @@ def postfix_translat(input_str): while opr != '(': output_list.extend([' ', stack_opr.pop()]) opr = stack_opr[-1] + output_list.append(' ') else: raise Exception('Using the symbol "," outside the function') @@ -223,6 +224,8 @@ def postfix_translat(input_str): if token == '(': raise Exception('unpaired brackets') output_list.extend([' ', token]) + print(output_list) + print(''.join(output_list)) return ''.join(output_list) From e0cff263a323a14304f02af27d194b3622cefce1 Mon Sep 17 00:00:00 2001 From: Vladislav Pirtan Date: Mon, 17 Dec 2018 19:27:13 +0300 Subject: [PATCH 23/27] hot fix hot bug fix --- final_task/pycalc/pycalc.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index 1617fcf..92342fc 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -224,8 +224,6 @@ def postfix_translat(input_str): if token == '(': raise Exception('unpaired brackets') output_list.extend([' ', token]) - print(output_list) - print(''.join(output_list)) return ''.join(output_list) From 266b886faeba838b1326881f78d36f408d7dc659 Mon Sep 17 00:00:00 2001 From: Vladislav Pirtan Date: Tue, 18 Dec 2018 19:01:54 +0300 Subject: [PATCH 24/27] add unit test --- final_task/pycalc/pycalc.py | 26 ++++++++------- final_task/pycalc/pycalc_test.py | 57 ++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 12 deletions(-) create mode 100644 final_task/pycalc/pycalc_test.py diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index 92342fc..4aad9c7 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -30,13 +30,13 @@ stack_opr = [] -def set_user_mod(modules=tuple()): +def set_user_mod(modules=None): """set_user_mod(modules) Initialize global variable module_func_dict """ - + if not modules: return modules_list = [importlib.import_module(module) for module in modules] global module_func_dict module_func_dict = {module: dir(module) for module in modules_list} @@ -72,7 +72,8 @@ def _stack_push(opr): break stack_opr.append(opr) - buf.append(' ') + if opr != '+-': + buf.append(' ') return buf @@ -88,7 +89,7 @@ def _const_separator(const): return const for module in module_func_dict: if const in module_func_dict[module]: - return [' ', str(getattr(module, const))] + return [str(getattr(module, const))] else: # Recursion i = 0 @@ -98,14 +99,14 @@ def _const_separator(const): r_const = const[i + 1:] for module in module_func_dict: if l_const in module_func_dict[module]: - return [' ', str(getattr(module, l_const)), *_stack_push('*'), *_const_separator(r_const)] + return [str(getattr(module, l_const)), *_stack_push('*'), *_const_separator(r_const)] i += 1 else: raise Exception('unknown constant {}'.format(const)) -def postfix_translat(input_str): - """postfix_translat(input_str) +def postfix_translator(input_str): + """postfix_translator(input_str) The function converts a mathematical expression written in infix notation into postfix notation. Implements an Shunting-yard algorithm @@ -138,8 +139,6 @@ def postfix_translat(input_str): if func_buf: func_buf += token else: - if last_token in PRIORITY_DICT: - output_list.append(' ') output_list.append(token) elif token == '.': @@ -190,7 +189,7 @@ def postfix_translat(input_str): func_buf = '' if (token == '-') & (last_token in LIST_OPERATOR or last_token in ['', '(']): output_list.extend(_stack_push('+-')) - elif (token == '+') & (last_token in LIST_OPERATOR): + elif (token == '+') & (last_token in LIST_OPERATOR or last_token == ''): pass elif token in ['=', '<', '>', '!']: if last_token in ['!=', '==', '<=', '>=', '!=']: @@ -204,8 +203,11 @@ def postfix_translat(input_str): else: output_list.extend(_stack_push(token)) elif token == '/': + if last_token == '//': + continue next_token = input_str[i + 1] if next_token == '/': + token == '//' output_list.extend(_stack_push(token + next_token)) else: output_list.extend(_stack_push(token)) @@ -292,7 +294,7 @@ def postfix_eval(input_str): elif PREFIX_FUNC in token: pos = token.find(PREFIX_FUNC) args_list = [] - for i in range(int(token[:pos])): + for _ in range(int(token[:pos])): args_list.append(stack.pop()) func_name = token[pos + len(PREFIX_FUNC):] for module in module_func_dict: @@ -326,7 +328,7 @@ def main(): args.MODULE = ['math'] try: set_user_mod(tuple(args.MODULE)) - print(postfix_eval(postfix_translat(args.EXPRESSION))) + print(postfix_eval(postfix_translator(args.EXPRESSION))) except Exception as err: print('ERROR:', err) diff --git a/final_task/pycalc/pycalc_test.py b/final_task/pycalc/pycalc_test.py new file mode 100644 index 0000000..b269142 --- /dev/null +++ b/final_task/pycalc/pycalc_test.py @@ -0,0 +1,57 @@ +import unittest +try: + from pycalc import set_user_mod, postfix_translator, postfix_eval, PREFIX_FUNC +except Exception as err: + from .pycalc import set_user_mod, postfix_translator, postfix_eval, PREFIX_FUNC + +from math import pi, e, pow, sin + + +class TestPostfixTranslator(unittest.TestCase): + + def setUp(self): + set_user_mod(['math']) + + def test_common(self): + self.assertEqual(postfix_translator("1+2*(3+4)/5.1"), "1 2 3 4 + * 5.1 / +") + + def test_pow(self): + self.assertEqual(postfix_translator("2^2^2^2"), "2 2 2 2 ^ ^ ^") + + def test_unary_opr(self): + self.assertEqual(postfix_translator("+-1-(-1)*-2"), "1 +- 1 +- 2 +- * -") + + def test_const(self): + self.assertEqual(postfix_translator("pi*e-pi"), "{} {} * {} -".format(pi, e, pi)) + + def test_func(self): + self.assertEqual(postfix_translator("pow(3,2)"), "3 2 2{}pow".format(PREFIX_FUNC)) + + def test_twice_opr(self): + self.assertEqual(postfix_translator("3//2>=2!=3"), "3 2 // 2 >= 3 !=") + + def test_impl_mult(self): + self.assertEqual(postfix_translator("(1+2)(2+3)pie"), "1 2 + 2 3 + * {} * {} *".format(pi, e)) + + +class TestPostfixEval(unittest.TestCase): + + def setUp(self): + set_user_mod(['math']) + + def test_common(self): + ans = 1+2*(3-4)/5.1%2//2.45 + self.assertEqual(postfix_eval("1 2 3 4 - * 5.1 / 2 % 2.45 // +"), ans) + + def test_bool(self): + ans = 3 == True <= 2 > 4 + self.assertEqual(postfix_eval("3 True 2 <= == 4 >"), ans) + + def test_func(self): + ans = pow(4, 2)*sin(3)*abs(-3) + self.assertEqual(postfix_eval("4 2 2#Fpow 3 1#Fsin * 3 +- 1#Fabs *"), ans) + + +if __name__ == '__main__': + unittest.main() + From c8b31045e977014183ab6c4de30b280d4f384da2 Mon Sep 17 00:00:00 2001 From: Vladislav Pirtan Date: Tue, 18 Dec 2018 19:07:58 +0300 Subject: [PATCH 25/27] pycodestyle fix --- final_task/pycalc/pycalc.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index 4aad9c7..fb90c00 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -36,7 +36,9 @@ def set_user_mod(modules=None): Initialize global variable module_func_dict """ - if not modules: return + + if not modules: + return modules_list = [importlib.import_module(module) for module in modules] global module_func_dict module_func_dict = {module: dir(module) for module in modules_list} @@ -226,6 +228,7 @@ def postfix_translator(input_str): if token == '(': raise Exception('unpaired brackets') output_list.extend([' ', token]) + return ''.join(output_list) From 1dbde90c39b01caf8deb83209a5a2b88afc12241 Mon Sep 17 00:00:00 2001 From: Vladislav Pirtan Date: Tue, 18 Dec 2018 21:00:43 +0300 Subject: [PATCH 26/27] bug fix --- final_task/pycalc/pycalc.py | 7 +++---- final_task/pycalc/pycalc_test.py | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index fb90c00..0a94adc 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -198,7 +198,7 @@ def postfix_translator(input_str): continue next_token = input_str[i + 1] if next_token == '=': - token = token + next_token + token += next_token output_list.extend(_stack_push(token)) elif token == '=': raise Exception('unknown operator: "="') @@ -209,8 +209,8 @@ def postfix_translator(input_str): continue next_token = input_str[i + 1] if next_token == '/': - token == '//' - output_list.extend(_stack_push(token + next_token)) + token = '//' + output_list.extend(_stack_push(token)) else: output_list.extend(_stack_push(token)) else: @@ -228,7 +228,6 @@ def postfix_translator(input_str): if token == '(': raise Exception('unpaired brackets') output_list.extend([' ', token]) - return ''.join(output_list) diff --git a/final_task/pycalc/pycalc_test.py b/final_task/pycalc/pycalc_test.py index b269142..f426d94 100644 --- a/final_task/pycalc/pycalc_test.py +++ b/final_task/pycalc/pycalc_test.py @@ -40,12 +40,12 @@ def setUp(self): set_user_mod(['math']) def test_common(self): - ans = 1+2*(3-4)/5.1%2//2.45 + ans = 1+2*(3-4)/5.1 % 2//2.45 self.assertEqual(postfix_eval("1 2 3 4 - * 5.1 / 2 % 2.45 // +"), ans) def test_bool(self): - ans = 3 == True <= 2 > 4 - self.assertEqual(postfix_eval("3 True 2 <= == 4 >"), ans) + ans = 3 == 1 <= 2 > 4 + self.assertEqual(postfix_eval("3 1 2 <= == 4 >"), ans) def test_func(self): ans = pow(4, 2)*sin(3)*abs(-3) @@ -54,4 +54,3 @@ def test_func(self): if __name__ == '__main__': unittest.main() - From 85c697607d0150b5b1bae42a2e92faba15187570 Mon Sep 17 00:00:00 2001 From: Vladislav Pirtan Date: Thu, 20 Dec 2018 23:14:52 +0300 Subject: [PATCH 27/27] final commit --- final_task/pycalc/pycalc.py | 59 ++++++++++++++++++++------------ final_task/pycalc/pycalc_test.py | 14 ++++---- final_task/setup.py | 2 +- 3 files changed, 45 insertions(+), 30 deletions(-) diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/pycalc.py index 0a94adc..e7eed0c 100644 --- a/final_task/pycalc/pycalc.py +++ b/final_task/pycalc/pycalc.py @@ -6,7 +6,6 @@ LIST_OPERATOR = ['+', '-', '*', '^', '/', '%', '<', '>', '=', '!'] LIST_DIGITS = list(digits) LIST_LETTERS = list(ascii_letters) -PREFIX_FUNC = '#F' PRIORITY_DICT = { '^': 5, '+-': 5, @@ -26,7 +25,7 @@ FUNCTION_DICT = {'abs': abs, 'pow': pow, 'round': round} # global variable dict, key: str module name, value: list containing fuction names from [key] module_func_dict = {} -# +# Operator stack stack_opr = [] @@ -54,6 +53,7 @@ def _stack_push(opr): global stack_opr buf = list() + # right-associative operator if opr == '^' or opr == '+-': for opr2 in stack_opr[::-1]: if opr2 == '(': @@ -64,6 +64,7 @@ def _stack_push(opr): break stack_opr.append(opr) + # left-associative operator else: for opr2 in stack_opr[::-1]: if opr2 == '(': @@ -91,7 +92,7 @@ def _const_separator(const): return const for module in module_func_dict: if const in module_func_dict[module]: - return [str(getattr(module, const))] + return [const] else: # Recursion i = 0 @@ -101,7 +102,7 @@ def _const_separator(const): r_const = const[i + 1:] for module in module_func_dict: if l_const in module_func_dict[module]: - return [str(getattr(module, l_const)), *_stack_push('*'), *_const_separator(r_const)] + return [l_const, *_stack_push('*'), *_const_separator(r_const)] i += 1 else: raise Exception('unknown constant {}'.format(const)) @@ -121,25 +122,24 @@ def postfix_translator(input_str): raise Exception('empty input') output_list = list() - func_buf = '' + func_buf = list() # argument counter in function last_token = '' - count_args = list() + count_args = list() # argument counter in function for i, token in enumerate(input_str): if token == ' ': output_list.append(' ') continue - elif token in LIST_LETTERS \ - or token == '_': + elif token in LIST_LETTERS or token == '_': if last_token == ')': output_list.extend(_stack_push('*')) if last_token in LIST_DIGITS: output_list.extend(_stack_push('*')) - func_buf += token + func_buf.append(token) elif token in LIST_DIGITS: if func_buf: - func_buf += token + func_buf.append(token) else: output_list.append(token) @@ -160,19 +160,20 @@ def postfix_translator(input_str): elif token == '(': if func_buf: - stack_opr.append(func_buf) + stack_opr.append(''.join(func_buf)) count_args.append(1) - func_buf = '' + func_buf = [] elif last_token == ')' or last_token in LIST_DIGITS: output_list.extend(_stack_push('*')) stack_opr.append('(') elif token == ')': + # function without argument if last_token == '(': count_args[-1] = 0 if func_buf: - output_list.extend(_const_separator(func_buf)) - func_buf = '' + output_list.extend(_const_separator(''.join(func_buf))) + func_buf = [] for opr in stack_opr[::-1]: if opr == '(': stack_opr.pop() @@ -181,18 +182,21 @@ def postfix_translator(input_str): output_list.extend([' ', stack_opr.pop()]) else: raise Exception('unpaired brackets') + # if function brackets if stack_opr: if stack_opr[-1] not in LIST_OPERATOR and stack_opr[-1] != '(': - output_list.extend([' ', str(count_args.pop()), PREFIX_FUNC, stack_opr.pop()]) + output_list.extend([' ', stack_opr.pop(), '(', str(count_args.pop()), ')']) elif token in LIST_OPERATOR: if func_buf: - output_list.extend(_const_separator(func_buf)) - func_buf = '' + output_list.extend(_const_separator(''.join(func_buf))) + func_buf = [] + # unary operator if (token == '-') & (last_token in LIST_OPERATOR or last_token in ['', '(']): output_list.extend(_stack_push('+-')) elif (token == '+') & (last_token in LIST_OPERATOR or last_token == ''): pass + # twice operator != <= >= == elif token in ['=', '<', '>', '!']: if last_token in ['!=', '==', '<=', '>=', '!=']: continue @@ -204,6 +208,7 @@ def postfix_translator(input_str): raise Exception('unknown operator: "="') else: output_list.extend(_stack_push(token)) + # twice operator // elif token == '/': if last_token == '//': continue @@ -213,6 +218,7 @@ def postfix_translator(input_str): output_list.extend(_stack_push(token)) else: output_list.extend(_stack_push(token)) + # other operator else: output_list.extend(_stack_push(token)) @@ -221,13 +227,14 @@ def postfix_translator(input_str): last_token = token if func_buf: - output_list.extend(_const_separator(func_buf)) + output_list.extend(_const_separator(''.join(func_buf))) while stack_opr: token = stack_opr.pop() if token == '(': raise Exception('unpaired brackets') output_list.extend([' ', token]) + return ''.join(output_list) @@ -293,12 +300,13 @@ def postfix_eval(input_str): stack.append(stack.pop() != val2) elif token in BOOL_DICT: stack.append(BOOL_DICT[token]) - elif PREFIX_FUNC in token: - pos = token.find(PREFIX_FUNC) + # function + elif '(' in token: + pos = token.find('(') args_list = [] - for _ in range(int(token[:pos])): + for _ in range(int(token[pos+1:-1])): args_list.append(stack.pop()) - func_name = token[pos + len(PREFIX_FUNC):] + func_name = token[:pos] for module in module_func_dict: if func_name in module_func_dict[module]: res = getattr(module, func_name)(*args_list[::-1]) @@ -309,6 +317,13 @@ def postfix_eval(input_str): else: raise Exception('unknown function {}'.format(func_name)) stack.append(res) + # const + elif token.isidentifier(): + for module in module_func_dict: + if token in module_func_dict[module]: + stack.append(getattr(module, token)) + break + # number else: stack.append(float(token)) if len(stack) > 1: diff --git a/final_task/pycalc/pycalc_test.py b/final_task/pycalc/pycalc_test.py index f426d94..f915cad 100644 --- a/final_task/pycalc/pycalc_test.py +++ b/final_task/pycalc/pycalc_test.py @@ -1,10 +1,10 @@ import unittest try: - from pycalc import set_user_mod, postfix_translator, postfix_eval, PREFIX_FUNC + from pycalc import set_user_mod, postfix_translator, postfix_eval except Exception as err: - from .pycalc import set_user_mod, postfix_translator, postfix_eval, PREFIX_FUNC + from .pycalc import set_user_mod, postfix_translator, postfix_eval -from math import pi, e, pow, sin +from math import pow, sin class TestPostfixTranslator(unittest.TestCase): @@ -22,16 +22,16 @@ def test_unary_opr(self): self.assertEqual(postfix_translator("+-1-(-1)*-2"), "1 +- 1 +- 2 +- * -") def test_const(self): - self.assertEqual(postfix_translator("pi*e-pi"), "{} {} * {} -".format(pi, e, pi)) + self.assertEqual(postfix_translator("pi*e-pi"), "pi e * pi -") def test_func(self): - self.assertEqual(postfix_translator("pow(3,2)"), "3 2 2{}pow".format(PREFIX_FUNC)) + self.assertEqual(postfix_translator("pow(3,2)"), "3 2 pow(2)") def test_twice_opr(self): self.assertEqual(postfix_translator("3//2>=2!=3"), "3 2 // 2 >= 3 !=") def test_impl_mult(self): - self.assertEqual(postfix_translator("(1+2)(2+3)pie"), "1 2 + 2 3 + * {} * {} *".format(pi, e)) + self.assertEqual(postfix_translator("(1+2)(2+3)pie"), "1 2 + 2 3 + * pi * e *") class TestPostfixEval(unittest.TestCase): @@ -49,7 +49,7 @@ def test_bool(self): def test_func(self): ans = pow(4, 2)*sin(3)*abs(-3) - self.assertEqual(postfix_eval("4 2 2#Fpow 3 1#Fsin * 3 +- 1#Fabs *"), ans) + self.assertEqual(postfix_eval("4 2 pow(2) 3 sin(1) * 3 +- abs(1) *"), ans) if __name__ == '__main__': diff --git a/final_task/setup.py b/final_task/setup.py index 9741c1b..2177ace 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -2,7 +2,7 @@ setup( name='pycalc', - version='0.5', + version='0.6', author='Vladislav Pirtan', author_email='Vpirtan@yandex.ru', packages=find_packages(),