From aef25b8b727086812c8646f56f48270bf582f679 Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 19 Dec 2018 21:05:29 +0300 Subject: [PATCH 1/3] [WIP] Nick --- final_task/__inite__.py | 0 final_task/mylib.py | 3 + final_task/pycalc.py | 336 ++++++++++++++++++++++++++++++++++++++++ final_task/readme.txt | 0 final_task/setup.py | 10 ++ 5 files changed, 349 insertions(+) create mode 100644 final_task/__inite__.py create mode 100644 final_task/mylib.py create mode 100644 final_task/pycalc.py create mode 100644 final_task/readme.txt diff --git a/final_task/__inite__.py b/final_task/__inite__.py new file mode 100644 index 0000000..e69de29 diff --git a/final_task/mylib.py b/final_task/mylib.py new file mode 100644 index 0000000..2521281 --- /dev/null +++ b/final_task/mylib.py @@ -0,0 +1,3 @@ + +def s(n: object) -> object: + return n*n diff --git a/final_task/pycalc.py b/final_task/pycalc.py new file mode 100644 index 0000000..3e7d4d9 --- /dev/null +++ b/final_task/pycalc.py @@ -0,0 +1,336 @@ +import math +import operator +import importlib.util +import argparse +import sys + +OPERATORS = {'+': (2, operator.add), '-': (2, operator.sub), + '*': (3, operator.mul), '/': (3, operator.truediv), + '^': (4, operator.pow), '//': (3, operator.floordiv), + '%': (3, operator.mod), '<': (1, operator.lt), + '<=': (1, operator.le), '>': (1, operator.gt), + '!=': (1, operator.ne), '>=': (1, operator.ge), + '==': (1, operator.eq), '?': (4, operator.neg)} + +STLO = {'True': True, 'False': False} +FUNCTION = {'round': round, 'abs': abs, 'pow': pow} +STNUMBER = '1234567890.' +STBUK = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM_' +STOP = '<+->/!=*^%' + +version = "1.2.1" +global module + + +def createparser(): + parser = argparse.ArgumentParser( + prog='''pycalc''', + description='''A program to calculate complex mathematical expressions + with support for custom libraries.''', + epilog='''(c) Nick Dubovik 2018. The author of + the program, as always,does not take any responsibility for anything.''', + add_help=False) + parent_group = parser.add_argument_group(title='Optional arguments') + parent_group.add_argument('--help', '-h', action='help', help='Help') + parser.add_argument('--mod', '-m', default='math', help='Additional modules to use', + metavar='MODULE') + parser.add_argument('string', type=str, default='', help='Expression string to evaluate', + metavar='EXPRESSION') + parent_group.add_argument('--version', action='version', help='Show the version number', + version='%(prog)s {}'.format(version)) + return parser + + +def check_module(module_name): + # Checks if the module can be imported without actually importing it + module_spec = importlib.util.find_spec(module_name) + if module_spec is None: + # print('Module: {} not found'.format(module_name)) + return None + else: + # print('Module: {} can be imported!'.format(module_name)) + return module_spec + + +def import_module_from_spec(module_spec): + module = importlib.util.module_from_spec(module_spec) + module_spec.loader.exec_module(module) + return module + + +def sum(module, sst): + # Reading and token allocation------------------------------------------------------ + def parse(sst): + if len(sst) == 0: + raise Exception('Empty expression.') + pr = 0 + lev = 0 + last_op = '' + for j, s in enumerate(sst): + if s == ' ': + if sst[j - 1] in '/*%^><=' and sst[j + 1] in '/*%^><=': + raise Exception('space between operators') + if sst[j - 1] in STNUMBER and sst[j + 1] in STNUMBER: + raise Exception('space') + st = sst.replace(" ", "") + if st[0] in '+-': + st = "0" + st + n = len(st) + i = 0 + while i < n: + if st[i] in STNUMBER: + last_op = '' + number = '' + # if st[i] == '.': + # number += '0' + while st[i] in STNUMBER: + number += st[i] + i = i + 1 + if i >= n: + break + if i >= n: + yield (float(number)) + break + elif i < n and (st[i] in STOP or st[i] == ')' or st[i] == ','): + yield (float(number)) + elif i < n and st[i] in STBUK or st[i] == '(': + yield float(number) + yield '*' + + if st[i] in STOP: + if st[0] in '*/%^><=!': + raise Exception('No arguments befor operator') + op = '' + while st[i] in STOP: + op += st[i] + i = i + 1 + if op in '-+^%*': + break + elif i < n and op in '-+^%*/><=' and st[i] in '+-': + break + if i >= n: + break + if last_op != '': + if op == '-': + op = "?" + yield "?" + last_op = op + elif op == '+': + last_op = op + else: + if i >= n: + raise Exception('The operator has not got arguments.') + yield op + last_op = op + + if st[i] in STBUK: + last_op = '' + func = '' + while st[i] in STBUK or st[i] in STNUMBER: + func += st[i] + i = i + 1 + if (i < n and (hasattr(math, func) or hasattr(module, func)) and ( + st[i] == '(' or st[i] in STBUK)) or i >= n or func in STLO: + break + if i >= n: + if hasattr(math, func) or hasattr(module, func): + cc = '$' + func + '@' + yield cc + elif func in STLO: + logic = '' + logic += '$' + func + '#' + yield logic + else: + raise Exception(func + ' is not in the library.') + else: + if st[i] == '(' and (hasattr(math, func) or hasattr(module, func) or func in FUNCTION): + f = '' + # k-number of '()',z-number of ',' + k = 0 + z = 0 + j = i + while k != 0 or j < n: + if st[j] == ')': + k = k - 1 + elif st[j] == '(': + k = k + 1 + elif st[j] == ',' and k == 1: + z = z + 1 + j = j + 1 + if j >= n or k == 0: + break + zz = str(z) + f += '$' + zz + func + '&' # function + yield f + + elif hasattr(math, func) or hasattr(module, func): + const = '' + const += '$' + func + '@' # const + if st[i] in STBUK or st[i] in STNUMBER: + yield const + yield '*' + else: + yield const + + elif func in STLO: + logic = '' + logic += '$' + func + '#' # const + if st[i] in STBUK or st[i] in STNUMBER: + yield logic + yield '*' + else: + yield logic + else: + raise Exception(func + ' is not in the library.') + if i >= n: + break + + if st[i] in '()': + if st[i] == '(': + lev = lev + 1 + last_op = '(' + elif st[i] == ')': + pr = pr + 1 + last_op = '' + i = i + 1 + if i < n and st[i - 1] == ')' and (st[i] in STNUMBER or st[i] in STBUK or st[i] == '('): + yield st[i - 1] + yield '*' + else: + yield st[i - 1] + if i >= n: + break + + if st[i] == ',': + last_op = ',' + i = i + 1 + yield st[i - 1] + if i >= n: + break + if pr != lev: + raise Exception('Unequal number of brackets') + + # conversion to Polish notation------------------------------------------------------------------------------ + + def shunting_yard(parsed_formula): + stack = [] + for token in parsed_formula: + + if type(token) is str and token[0] == '$' and token[-1] == '&': + stack.append(token) # function + + elif type(token) is str and token[0] == '$' and token[-1] == '@': + mcon = token.replace('$', '').replace('@', '') + zz = getattr(math, mcon) + yield float(zz) # const + + elif type(token) is str and token[0] == '$' and token[-1] == '#': + mlo = token.replace('$', '').replace('#', '') + yield STLO[mlo] # logic + + elif token in OPERATORS: + if token == '^' or token == '?': + while stack and stack[-1] != "(" and OPERATORS[token][0] < OPERATORS[stack[-1]][0]: + yield stack.pop() + else: + while stack and stack[-1] != "(" and OPERATORS[token][0] <= OPERATORS[stack[-1]][0]: + yield stack.pop() + stack.append(token) + + elif token == ',': + while stack: + if stack[-1] == '(': + break + yield stack.pop() + + elif token == ")": + while stack: + x = stack.pop() + if x == "(": + break + yield x + if stack: + yy = stack[-1] + if type(yy) is str and yy[0] == '$' and yy[-1] == '&': + yield stack.pop() + + elif token == "(": + stack.append(token) + + elif type(token) is float: + yield token + + while stack: + yield stack.pop() + + # calculating Polish notation------------------------------------------------------------------------------ + + def calculation(polish): + stack = [] + for token in polish: + if token in OPERATORS: + if token == '?': + x = stack.pop() + stack.append(OPERATORS[token][1](x)) + else: + y, x = stack.pop(), stack.pop() + stack.append(OPERATORS[token][1](x, y)) + + elif type(token) is str and token[0] == '$' and token[-1] == '&': + mfun = token.replace('$', '').replace('&', '') + kk = '' + for s in mfun: + if s in STNUMBER: + kk += s + else: + break + k = int(kk) + # k-number of ',' k+1-number of arguments for function + mmfun = mfun.lstrip(kk) + x = [] # list of arguments for function + for l in range(k + 1): + y = stack.pop() + # if type(y) is float: + x.append(y) + if hasattr(module, mmfun): + z = getattr(module, mmfun) + stack.append(z(*x[::-1])) + elif hasattr(math, mmfun): + z = getattr(math, mmfun) + stack.append(z(*x[::-1])) + elif mmfun in FUNCTION: + g = FUNCTION[mmfun](*x[::-1]) + stack.append(g) + else: + raise Exception('The function is not in the library.') + # print(stack) + else: + stack.append(token) + + return stack[0] + + return calculation(shunting_yard(parse(sst))) + + +# main------------------------------------------------------------------------------------ + + +def main(): + parser = createparser() + namespace = parser.parse_args(sys.argv[1:]) + print(namespace) + try: + lib = namespace.mod + # lib is connected library + st = namespace.string + module_spec = check_module(lib) + if module_spec: + module = import_module_from_spec(module_spec) + print(sum(module, st)) + # Output result + except Exception as error: + print('ERROR: ' + repr(error)) + + +#if __name__ == "__main__": + # main() diff --git a/final_task/readme.txt b/final_task/readme.txt new file mode 100644 index 0000000..e69de29 diff --git a/final_task/setup.py b/final_task/setup.py index e69de29..1743927 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="1.2.1", + author="Nick Dubovik", + author_email="kizrumscience@yandex.ru", + description='A program to calculate complex mathematical expressions with support for custom libraries.', + scripts={"pycalc.py"}, + packages=find_packages(), + entry_points={'console_scripts': ['pycalc = pycalc:main']}, ) From 53773c945909221fca86c469ecfad9fe31a95c1f Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 19 Dec 2018 22:29:47 +0300 Subject: [PATCH 2/3] [WIP] Nick Dubovik --- final_task/__inite__.py | 0 final_task/pycalc.py | 22 +++++++--------------- 2 files changed, 7 insertions(+), 15 deletions(-) delete mode 100644 final_task/__inite__.py diff --git a/final_task/__inite__.py b/final_task/__inite__.py deleted file mode 100644 index e69de29..0000000 diff --git a/final_task/pycalc.py b/final_task/pycalc.py index 3e7d4d9..8346d3a 100644 --- a/final_task/pycalc.py +++ b/final_task/pycalc.py @@ -18,8 +18,8 @@ STBUK = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM_' STOP = '<+->/!=*^%' + version = "1.2.1" -global module def createparser(): @@ -44,12 +44,7 @@ def createparser(): def check_module(module_name): # Checks if the module can be imported without actually importing it module_spec = importlib.util.find_spec(module_name) - if module_spec is None: - # print('Module: {} not found'.format(module_name)) - return None - else: - # print('Module: {} can be imported!'.format(module_name)) - return module_spec + return module_spec def import_module_from_spec(module_spec): @@ -58,7 +53,7 @@ def import_module_from_spec(module_spec): return module -def sum(module, sst): +def result(module, sst): # Reading and token allocation------------------------------------------------------ def parse(sst): if len(sst) == 0: @@ -303,7 +298,6 @@ def calculation(polish): stack.append(g) else: raise Exception('The function is not in the library.') - # print(stack) else: stack.append(token) @@ -318,7 +312,7 @@ def calculation(polish): def main(): parser = createparser() namespace = parser.parse_args(sys.argv[1:]) - print(namespace) + # print(namespace) try: lib = namespace.mod # lib is connected library @@ -326,11 +320,9 @@ def main(): module_spec = check_module(lib) if module_spec: module = import_module_from_spec(module_spec) - print(sum(module, st)) - # Output result + print(result(module, st)) + # Output result except Exception as error: print('ERROR: ' + repr(error)) - -#if __name__ == "__main__": - # main() + # final From a5373163367f385c7e1eeb53e29684e4fc8d9377 Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 19 Dec 2018 23:02:10 +0300 Subject: [PATCH 3/3] [WIP] Nick Dubovik --- final_task/pycalc.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/final_task/pycalc.py b/final_task/pycalc.py index 8346d3a..35462cc 100644 --- a/final_task/pycalc.py +++ b/final_task/pycalc.py @@ -25,10 +25,8 @@ def createparser(): parser = argparse.ArgumentParser( prog='''pycalc''', - description='''A program to calculate complex mathematical expressions - with support for custom libraries.''', - epilog='''(c) Nick Dubovik 2018. The author of - the program, as always,does not take any responsibility for anything.''', + description='''A program to calculate complex mathematical expressions''', + epilog='''(c) Nick Dubovik 2018. The author of the program does not take any responsibility for anything.''', add_help=False) parent_group = parser.add_argument_group(title='Optional arguments') parent_group.add_argument('--help', '-h', action='help', help='Help') @@ -54,8 +52,11 @@ def import_module_from_spec(module_spec): def result(module, sst): + # Reading and token allocation------------------------------------------------------ + def parse(sst): + if len(sst) == 0: raise Exception('Empty expression.') pr = 0 @@ -285,7 +286,6 @@ def calculation(polish): x = [] # list of arguments for function for l in range(k + 1): y = stack.pop() - # if type(y) is float: x.append(y) if hasattr(module, mmfun): z = getattr(module, mmfun)