From 387501b0f56fb2cc2b53a3ea483cce3d232d6980 Mon Sep 17 00:00:00 2001 From: lanaumch Date: Tue, 18 Dec 2018 02:14:52 +0000 Subject: [PATCH 1/6] add parser --- final_task/main.py | 55 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 final_task/main.py diff --git a/final_task/main.py b/final_task/main.py new file mode 100644 index 0000000..8f351d5 --- /dev/null +++ b/final_task/main.py @@ -0,0 +1,55 @@ +import re + + +inp = '8*5-4+7*2' + + +regExpression = r'(?:\d+(?:\.\d*)?|\.\d+)|[\+\-\*\/]' +result = re.findall(regExpression, inp) +print(result) + +priority = [ + ['+', '-'], + ['*', '/', '^'], + ['(', ')'] +] + +tokens = [] + + +class Token: + def __init__(self, value, token_priority, index): + self.value = value + self.priority = token_priority + self.index = index + + def __repr__(self): + return "Token: {}, priority: {}".format(self.value, self.priority) + + +class Tree: + def __init__(self, token): + self.rightNode = None + self.leftNode = None + self.token = token + + def insert(self, value): + pass + + def __repr__(self): + return '{} (index = {})'.format(self.token.value, self.token.index) + + +for i, token in enumerate(result): + for tokenPriority, operators in enumerate(priority): + if token in operators: + tokens.append(Token(token, tokenPriority, i)) + + +root = Tree(Token(None, 3, None)) + +for token in tokens: + if root.token.priority > token.priority: + root.token = token + +print(root) \ No newline at end of file From 26adea9408fdfc4bcdc0c0a6f8da988e5803ca09 Mon Sep 17 00:00:00 2001 From: lanaumch Date: Tue, 18 Dec 2018 15:26:30 +0000 Subject: [PATCH 2/6] add expression tree --- final_task/main.py | 53 ++++++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/final_task/main.py b/final_task/main.py index 8f351d5..c90b17e 100644 --- a/final_task/main.py +++ b/final_task/main.py @@ -4,52 +4,63 @@ inp = '8*5-4+7*2' -regExpression = r'(?:\d+(?:\.\d*)?|\.\d+)|[\+\-\*\/]' -result = re.findall(regExpression, inp) -print(result) +regularExpression = r'(?:\d+(?:\.\d*)?|\.\d+)|[\+\-\*\/]' +parsedArray = re.findall(regularExpression, inp) + +print(parsedArray) priority = [ ['+', '-'], ['*', '/', '^'], ['(', ')'] ] - -tokens = [] +maxPriority = len(priority)-1 class Token: - def __init__(self, value, token_priority, index): + def __init__(self, value, token_priority, array_index): self.value = value self.priority = token_priority - self.index = index + self.array_index = array_index def __repr__(self): - return "Token: {}, priority: {}".format(self.value, self.priority) + return "{}".format(self.value) class Tree: - def __init__(self, token): + def __init__(self, value): self.rightNode = None self.leftNode = None - self.token = token - - def insert(self, value): - pass + self.token = value def __repr__(self): - return '{} (index = {})'.format(self.token.value, self.token.index) + return "{}".format(self.token) -for i, token in enumerate(result): +tokensArray = [] +for i, token in enumerate(parsedArray): for tokenPriority, operators in enumerate(priority): if token in operators: - tokens.append(Token(token, tokenPriority, i)) + tokensArray.append(Token(token, tokenPriority, i)) + break + else: + tokensArray.append(Token(token, maxPriority, i)) + + +def build_tree(arr): + root = Tree(Token(None, maxPriority, None)) + for token in arr: + if root.token.priority > token.priority: + root.token = token + if root.token.value is None: + root.token = arr[0] + border = arr.index(root.token) -root = Tree(Token(None, 3, None)) + if root.token != arr[0]: + root.leftNode = build_tree(arr[:border:]) + root.rightNode = build_tree(arr[border + 1::]) + return root -for token in tokens: - if root.token.priority > token.priority: - root.token = token -print(root) \ No newline at end of file +tree = build_tree(tokensArray) From 61c8334c332f374e12a9c976e71c720988732b7d Mon Sep 17 00:00:00 2001 From: lanaumch Date: Tue, 18 Dec 2018 17:39:10 +0000 Subject: [PATCH 3/6] add calculating, fix expression tree --- final_task/main.py | 45 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/final_task/main.py b/final_task/main.py index c90b17e..acdea75 100644 --- a/final_task/main.py +++ b/final_task/main.py @@ -11,9 +11,16 @@ priority = [ ['+', '-'], - ['*', '/', '^'], - ['(', ')'] + ['*', '/'], + ['(', ')', '^'] ] + +operation = {'+': lambda a, b: a+b, + '-': lambda a, b: a-b, + '*': lambda a, b: a*b, + '/': lambda a, b: a/b + } + maxPriority = len(priority)-1 @@ -28,10 +35,11 @@ def __repr__(self): class Tree: - def __init__(self, value): + def __init__(self, value, parent=None): self.rightNode = None self.leftNode = None self.token = value + self.parent = parent def __repr__(self): return "{}".format(self.token) @@ -47,20 +55,41 @@ def __repr__(self): tokensArray.append(Token(token, maxPriority, i)) -def build_tree(arr): - root = Tree(Token(None, maxPriority, None)) +def build_tree(arr, parent=None): + root = Tree(Token(None, maxPriority, None), parent) for token in arr: - if root.token.priority > token.priority: + if root.token.priority >= token.priority: root.token = token + if root.token.value is None: root.token = arr[0] border = arr.index(root.token) if root.token != arr[0]: - root.leftNode = build_tree(arr[:border:]) - root.rightNode = build_tree(arr[border + 1::]) + root.leftNode = build_tree(arr[:border:], root) + root.rightNode = build_tree(arr[border + 1::], root) return root tree = build_tree(tokensArray) + + +result = 0 + +operands_array = [] + + +def postOrder(node): + if not node.leftNode and not node.rightNode: + return node + left = postOrder(node.leftNode) + right = postOrder(node.rightNode) + if left and right: + result = operation[left.parent.token.value](int(left.token.value), int(right.token.value)) + left.parent.token.value = result + return left.parent + +print(postOrder(tree)) + + From 49ad3195e3088a222db77e88c759af44b849dc8f Mon Sep 17 00:00:00 2001 From: lanaumch Date: Wed, 19 Dec 2018 20:24:34 +0000 Subject: [PATCH 4/6] add stack, delete tree, add brackets processing --- final_task/main.py | 155 +++++++++++++++++++++++++++------------------ 1 file changed, 95 insertions(+), 60 deletions(-) diff --git a/final_task/main.py b/final_task/main.py index acdea75..1ab9a07 100644 --- a/final_task/main.py +++ b/final_task/main.py @@ -1,95 +1,130 @@ import re +import math +inp = '(7+4)*(2+3)+8' +inp = '5^7/(5*8)+10' -inp = '8*5-4+7*2' - - -regularExpression = r'(?:\d+(?:\.\d*)?|\.\d+)|[\+\-\*\/]' +regularExpression = r'(?:\d+(?:\.\d*)?|\.\d+)|(?://)|[\+\-\*\/\%\^\(\)]' parsedArray = re.findall(regularExpression, inp) -print(parsedArray) - -priority = [ - ['+', '-'], - ['*', '/'], - ['(', ')', '^'] -] - operation = {'+': lambda a, b: a+b, '-': lambda a, b: a-b, '*': lambda a, b: a*b, - '/': lambda a, b: a/b + '/': lambda a, b: a/b, + '//': lambda a, b: a//b, + '%': lambda a, b: a % b, + '^': lambda a, b: a**b } -maxPriority = len(priority)-1 +priority = [ + ['('], + [')'], + ['<', '<=', '!=', '>=', '>'], + ['+', '-'], + ['/', '*'], + ['^'] +] class Token: - def __init__(self, value, token_priority, array_index): + def __init__(self, value, type=None, token_priority=None, array_index=None): self.value = value self.priority = token_priority self.array_index = array_index + self.type = type def __repr__(self): return "{}".format(self.value) -class Tree: - def __init__(self, value, parent=None): - self.rightNode = None - self.leftNode = None - self.token = value - self.parent = parent - - def __repr__(self): - return "{}".format(self.token) - - tokensArray = [] -for i, token in enumerate(parsedArray): +for token in parsedArray: for tokenPriority, operators in enumerate(priority): if token in operators: - tokensArray.append(Token(token, tokenPriority, i)) + tokensArray.append(Token(token, 'operator', tokenPriority)) break else: - tokensArray.append(Token(token, maxPriority, i)) - - -def build_tree(arr, parent=None): - root = Tree(Token(None, maxPriority, None), parent) - for token in arr: - if root.token.priority >= token.priority: - root.token = token - - if root.token.value is None: - root.token = arr[0] - - border = arr.index(root.token) + if '.' in token: + token = float(token) + else: + token = int(token) + tokensArray.append(Token(token, type(token))) - if root.token != arr[0]: - root.leftNode = build_tree(arr[:border:], root) - root.rightNode = build_tree(arr[border + 1::], root) - return root +output = [] +operators = [] -tree = build_tree(tokensArray) +class Stack: + def __init__(self, arr=[]): + self.items = arr + self.len = 0 + self.last = None -result = 0 + def push(self, token): + self.items.append(token) + self.len += 1 + self.last = token -operands_array = [] + def pop(self, count=1): + for i in range(count): + if self.len > 0: + self.items.pop() + self.len -= 1 + if self.len > 0: + self.last = self.items[-1] + else: + break + def __iter__(self): + return iter(self.items[::-1]) -def postOrder(node): - if not node.leftNode and not node.rightNode: - return node - left = postOrder(node.leftNode) - right = postOrder(node.rightNode) - if left and right: - result = operation[left.parent.token.value](int(left.token.value), int(right.token.value)) - left.parent.token.value = result - return left.parent - -print(postOrder(tree)) - + def __repr__(self): + return repr(self.items) + + +stack = Stack() + +for elem in tokensArray: + if elem.type == 'operator': + if stack.len == 0: + stack.push(elem) + elif elem.priority > stack.last.priority or elem.priority == 0: + stack.push(elem) + + else: + for el in stack: + if elem.priority == 1: + for i in stack: + if i.priority != 0: + output.append(i) + stack.pop() + else: + stack.pop() + break + + elif elem.priority <= el.priority: + stack.pop() + stack.push(elem) + output.append(el) + else: + output.append(elem) +else: + if stack.len > 0: + for i in stack: + output.append(i) + stack.pop() + +print(tokensArray) +print(output) + +for token in output: + if token.type == 'operator': + result = operation[token.value](stack.items[-2].value, stack.items[-1].value) + new_token = Token(result, type(result)) + stack.pop(2) + stack.push(new_token) + else: + stack.push(token) +print(stack) From 47518f702ff98fa3b70aaa890a1ab52bd4b2d9db Mon Sep 17 00:00:00 2001 From: lanaumch Date: Thu, 20 Dec 2018 00:42:00 +0000 Subject: [PATCH 5/6] add lib math, refactor priority list --- final_task/main.py | 97 +++++++++++++++++++++++++++++++--------------- 1 file changed, 65 insertions(+), 32 deletions(-) diff --git a/final_task/main.py b/final_task/main.py index 1ab9a07..45141c5 100644 --- a/final_task/main.py +++ b/final_task/main.py @@ -1,30 +1,51 @@ import re import math -inp = '(7+4)*(2+3)+8' -inp = '5^7/(5*8)+10' +"""import argparse +parser = argparse.ArgumentParser(description='Pure-python command-line calculator.') +parser.add_argument("EXPRESSION", type=str, + help="expression string to evaluate") +args = parser.parse_args() +inp = args.EXPRESSION""" + +inp = '1+cos(0)' + +functions = {name: func for name, func in math.__dict__.items() if not name.startswith('__') and callable(func)} +functions.update({'abs': lambda a: abs(a), 'round': lambda a: round(a)}) + +constants = {name: const for name, const in math.__dict__.items() if not name.startswith('__') and not callable(const)} + + +operations = list(['(', ')', + {'<': lambda a, b: a < b, + '>': lambda a, b: a > b, + '<=': lambda a, b: a <= b, + '>=': lambda a, b: a >= b, + '!=': lambda a, b: a != b, + '==': lambda a, b: a == b}]) +operations.append({'+': lambda a, b: a+b, + '-': lambda a, b: a-b}) + +operations.append({'*': lambda a, b: a*b, + '/': lambda a, b: a/b, + '//': lambda a, b: a//b, + '%': lambda a, b: a % b}) + +operations.append({'^': lambda a, b: a**b}) +operations.append(functions) +func_reg = '' +for func in functions: + func_reg += r'|(?:{})'.format(func) +const_reg = '' +for const in constants: + const_reg += r'|(?:{})'.format(const) + +regularExpression = r'(?:\d+(?:\.\d*)?|\.\d+)|' \ + r'(?://)|(?:<=)|(?:!=)|(?:>=)|(?:==)|'\ + r'[\+\-\*\/\%\^\(\)\<\>]'+func_reg+const_reg -regularExpression = r'(?:\d+(?:\.\d*)?|\.\d+)|(?://)|[\+\-\*\/\%\^\(\)]' parsedArray = re.findall(regularExpression, inp) -operation = {'+': lambda a, b: a+b, - '-': lambda a, b: a-b, - '*': lambda a, b: a*b, - '/': lambda a, b: a/b, - '//': lambda a, b: a//b, - '%': lambda a, b: a % b, - '^': lambda a, b: a**b - } - -priority = [ - ['('], - [')'], - ['<', '<=', '!=', '>=', '>'], - ['+', '-'], - ['/', '*'], - ['^'] -] - class Token: def __init__(self, value, type=None, token_priority=None, array_index=None): @@ -39,13 +60,20 @@ def __repr__(self): tokensArray = [] for token in parsedArray: - for tokenPriority, operators in enumerate(priority): + for tokenPriority, operators in enumerate(operations): if token in operators: - tokensArray.append(Token(token, 'operator', tokenPriority)) + if token in functions: + tokensArray.append(Token(token, 'function', tokenPriority)) + else: + tokensArray.append(Token(token, 'operator', tokenPriority)) break else: - if '.' in token: + if token in constants: + token = constants[token] + tokensArray.append(Token(token, type(token))) + elif '.' in token: token = float(token) + tokensArray.append(Token(token, type(token))) else: token = int(token) tokensArray.append(Token(token, type(token))) @@ -85,11 +113,12 @@ def __repr__(self): stack = Stack() + for elem in tokensArray: - if elem.type == 'operator': + if elem.type == 'operator' or elem.type == 'function': if stack.len == 0: stack.push(elem) - elif elem.priority > stack.last.priority or elem.priority == 0: + elif (elem.priority > stack.last.priority or elem.priority == 0) and elem.priority != 1: stack.push(elem) else: @@ -112,19 +141,23 @@ def __repr__(self): else: if stack.len > 0: for i in stack: - output.append(i) + if i.priority > 1: + output.append(i) stack.pop() - -print(tokensArray) print(output) - for token in output: if token.type == 'operator': - result = operation[token.value](stack.items[-2].value, stack.items[-1].value) + result = operations[token.priority][token.value](stack.items[-2].value, stack.items[-1].value) new_token = Token(result, type(result)) stack.pop(2) stack.push(new_token) + elif token.type == 'function': + result = operations[token.priority][token.value](stack.items[-1].value) + new_token = Token(result, type(result)) + stack.pop() + stack.push(new_token) + else: stack.push(token) -print(stack) +print(stack.last) From 2e5e5dd6842a7cfad5cc6a05cd87760bf8b987a5 Mon Sep 17 00:00:00 2001 From: lanaumch Date: Thu, 20 Dec 2018 16:36:29 +0000 Subject: [PATCH 6/6] add error list, add unary operators --- final_task/error_list.py | 28 ++++++++++++++++++ final_task/main.py | 62 ++++++++++++++++++++++++++++------------ 2 files changed, 72 insertions(+), 18 deletions(-) create mode 100644 final_task/error_list.py diff --git a/final_task/error_list.py b/final_task/error_list.py new file mode 100644 index 0000000..a188d56 --- /dev/null +++ b/final_task/error_list.py @@ -0,0 +1,28 @@ +def is_brackets_balanced(tokens_array): + brackets_balance_count = 0 + for elem in tokens_array: + if elem.priority == 0: + brackets_balance_count += 1 + elif elem.priority == 1: + brackets_balance_count -= 1 + return brackets_balance_count + + +def is_operations_missed(tokens_array): + types = ['operator', 'function'] + for i in range(len(tokens_array)-1): + if tokens_array[i].type not in types and tokens_array[i+1].type not in types: + return True + else: + return False + + +def is_operations_ordered(token_array, operations): + invalid_combinations = {'+', '-', '*', '/', '^', '<', '>', '%'} + for i, checked_char in enumerate(token_array): + for operators in operations: + if str(checked_char.value) in operators and str(token_array[i+1].value) in invalid_combinations: + print(checked_char) + return False + else: + return True diff --git a/final_task/main.py b/final_task/main.py index 45141c5..bd4cf7c 100644 --- a/final_task/main.py +++ b/final_task/main.py @@ -1,5 +1,6 @@ import re import math +import error_list """import argparse parser = argparse.ArgumentParser(description='Pure-python command-line calculator.') @@ -8,11 +9,11 @@ args = parser.parse_args() inp = args.EXPRESSION""" -inp = '1+cos(0)' + +inp = 'sin(-cos(-sin(3.0)-cos(-sin(-3.0*5.0)-sin(cos(log10(43.0))))+cos(sin(sin(34.0-2.0^2.0))))--cos(1.0)--cos(0.0)^3.0)' functions = {name: func for name, func in math.__dict__.items() if not name.startswith('__') and callable(func)} functions.update({'abs': lambda a: abs(a), 'round': lambda a: round(a)}) - constants = {name: const for name, const in math.__dict__.items() if not name.startswith('__') and not callable(const)} @@ -23,9 +24,12 @@ '>=': lambda a, b: a >= b, '!=': lambda a, b: a != b, '==': lambda a, b: a == b}]) + operations.append({'+': lambda a, b: a+b, '-': lambda a, b: a-b}) +operations.append({'$': lambda a: -a}) + operations.append({'*': lambda a, b: a*b, '/': lambda a, b: a/b, '//': lambda a, b: a//b, @@ -33,9 +37,14 @@ operations.append({'^': lambda a, b: a**b}) operations.append(functions) + func_reg = '' +temp_arr = [] for func in functions: + temp_arr.append(func) +for func in reversed(temp_arr): func_reg += r'|(?:{})'.format(func) + const_reg = '' for const in constants: const_reg += r'|(?:{})'.format(const) @@ -59,12 +68,17 @@ def __repr__(self): tokensArray = [] -for token in parsedArray: +for index, token in enumerate(parsedArray): for tokenPriority, operators in enumerate(operations): if token in operators: if token in functions: tokensArray.append(Token(token, 'function', tokenPriority)) else: + if tokenPriority == 3: + if index == 0 or ((tokensArray[index-1].type == 'operator' or tokensArray[index-1].type == 'function') and tokensArray[index-1].priority != 1): + if token == '-': + tokensArray.append((Token('$', 'function', 4))) + break tokensArray.append(Token(token, 'operator', tokenPriority)) break else: @@ -82,6 +96,15 @@ def __repr__(self): output = [] operators = [] +if error_list.is_brackets_balanced(tokensArray) != 0: + raise RuntimeError('ERROR: brackets are not balanced.') + +if error_list.is_operations_missed(tokensArray): + raise RuntimeError('ERROR: missing operations') + +if not error_list.is_operations_ordered(tokensArray, operations): + raise RuntimeError('ERROR: invalid operations order or missing operands') + class Stack: def __init__(self, arr=[]): @@ -101,6 +124,8 @@ def pop(self, count=1): self.len -= 1 if self.len > 0: self.last = self.items[-1] + else: + self.last = None else: break @@ -122,20 +147,22 @@ def __repr__(self): stack.push(elem) else: - for el in stack: - if elem.priority == 1: - for i in stack: - if i.priority != 0: - output.append(i) - stack.pop() - else: - stack.pop() - break - - elif elem.priority <= el.priority: + if elem.priority == 1: + for i in stack: + if i.priority != 0: + output.append(i) + stack.pop() + else: + stack.pop() + break + + else: + while stack.len > 0 and elem.priority <= stack.last.priority: + if len(output) == 0 and elem.value == '$': + break + output.append(stack.last) stack.pop() - stack.push(elem) - output.append(el) + stack.push(elem) else: output.append(elem) else: @@ -144,7 +171,7 @@ def __repr__(self): if i.priority > 1: output.append(i) stack.pop() -print(output) + for token in output: if token.type == 'operator': result = operations[token.priority][token.value](stack.items[-2].value, stack.items[-1].value) @@ -156,7 +183,6 @@ def __repr__(self): new_token = Token(result, type(result)) stack.pop() stack.push(new_token) - else: stack.push(token)