diff --git a/.travis.yml b/.travis.yml index baca1385..44e4ab99 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,7 @@ script: - cd final_task - pip install . - nosetests --cover-branches --with-coverage . - - pycodestyle --max-line-length=120 . + - pycodestyle --ignore=W605 --max-line-length=120 . - python ./../pycalc_checker.py - - cd - \ No newline at end of file + - python ./test_main.py + - cd - diff --git a/final_task/dist/pycalc-0.1.tar.gz b/final_task/dist/pycalc-0.1.tar.gz new file mode 100644 index 00000000..fa062afe Binary files /dev/null and b/final_task/dist/pycalc-0.1.tar.gz differ diff --git a/final_task/pycalc.egg-info/PKG-INFO b/final_task/pycalc.egg-info/PKG-INFO new file mode 100644 index 00000000..f7d7d56b --- /dev/null +++ b/final_task/pycalc.egg-info/PKG-INFO @@ -0,0 +1,10 @@ +Metadata-Version: 1.0 +Name: pycalc +Version: 0.1 +Summary: Alpha version pure-python command-line calculator +Home-page: UNKNOWN +Author: Andrei Trukhan +Author-email: andrey-truhan@tut.by +License: free +Description: UNKNOWN +Platform: UNKNOWN diff --git a/final_task/pycalc.egg-info/SOURCES.txt b/final_task/pycalc.egg-info/SOURCES.txt new file mode 100644 index 00000000..26c30242 --- /dev/null +++ b/final_task/pycalc.egg-info/SOURCES.txt @@ -0,0 +1,10 @@ +setup.py +pycalc/main.py +pycalc/stack.py +pycalc/start.py +pycalc.egg-info/PKG-INFO +pycalc.egg-info/SOURCES.txt +pycalc.egg-info/dependency_links.txt +pycalc.egg-info/entry_points.txt +pycalc.egg-info/not-zip-safe +pycalc.egg-info/top_level.txt \ No newline at end of file diff --git a/final_task/pycalc.egg-info/dependency_links.txt b/final_task/pycalc.egg-info/dependency_links.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/final_task/pycalc.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/final_task/pycalc.egg-info/entry_points.txt b/final_task/pycalc.egg-info/entry_points.txt new file mode 100644 index 00000000..58b68d8b --- /dev/null +++ b/final_task/pycalc.egg-info/entry_points.txt @@ -0,0 +1,3 @@ +[console_scripts] +pycalc = pycalc.start:main + diff --git a/final_task/pycalc.egg-info/not-zip-safe b/final_task/pycalc.egg-info/not-zip-safe new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/final_task/pycalc.egg-info/not-zip-safe @@ -0,0 +1 @@ + diff --git a/final_task/pycalc.egg-info/top_level.txt b/final_task/pycalc.egg-info/top_level.txt new file mode 100644 index 00000000..f6ff781f --- /dev/null +++ b/final_task/pycalc.egg-info/top_level.txt @@ -0,0 +1 @@ +pycalc diff --git a/final_task/pycalc/__pycache__/check_mistakes.cpython-36.pyc b/final_task/pycalc/__pycache__/check_mistakes.cpython-36.pyc new file mode 100644 index 00000000..d9c2dc83 Binary files /dev/null and b/final_task/pycalc/__pycache__/check_mistakes.cpython-36.pyc differ diff --git a/final_task/pycalc/__pycache__/consts.cpython-36.pyc b/final_task/pycalc/__pycache__/consts.cpython-36.pyc new file mode 100644 index 00000000..36e4ff54 Binary files /dev/null and b/final_task/pycalc/__pycache__/consts.cpython-36.pyc differ diff --git a/final_task/pycalc/__pycache__/main.cpython-36.pyc b/final_task/pycalc/__pycache__/main.cpython-36.pyc new file mode 100644 index 00000000..8667c1cc Binary files /dev/null and b/final_task/pycalc/__pycache__/main.cpython-36.pyc differ diff --git a/final_task/pycalc/__pycache__/stack.cpython-36.pyc b/final_task/pycalc/__pycache__/stack.cpython-36.pyc new file mode 100644 index 00000000..44749858 Binary files /dev/null and b/final_task/pycalc/__pycache__/stack.cpython-36.pyc differ diff --git a/final_task/pycalc/__pycache__/stack.cpython-37.pyc b/final_task/pycalc/__pycache__/stack.cpython-37.pyc new file mode 100644 index 00000000..eeb3e006 Binary files /dev/null and b/final_task/pycalc/__pycache__/stack.cpython-37.pyc differ diff --git a/final_task/pycalc/check_mistakes.py b/final_task/pycalc/check_mistakes.py new file mode 100644 index 00000000..96fb5a32 --- /dev/null +++ b/final_task/pycalc/check_mistakes.py @@ -0,0 +1,105 @@ +from pycalc.main import Stack +from pycalc.consts import * + + +def check_mistakes(expression, functions={}, mode=0): + + def is_number(part): + for char in part: + if not char.isnumeric() and char != '.': + return False + return True + + if len(expression) == 0: + if mode == 0: + return "ERROR: empty expression" + elif mode == 1: + return "ERROR: empty brackets" + elif mode == 2: + return '' + + begin = -1 + brackets_stack = Stack.Stack() + + for index, element in enumerate(expression): + if element == '(': + brackets_stack.push('(') + if begin == -1: + begin = index + elif element == ')': + if brackets_stack.is_empty(): + return "ERROR: brackets are not paired" + brackets_stack.pop() + if brackets_stack.is_empty(): + if expression[begin - 1].isalpha() and not expression[begin - 1] in math_consts: + error = check_mistakes(expression[begin + 1: index], functions, 2) + else: + error = check_mistakes(expression[begin + 1: index], functions, 1) + begin = -1 + + if error != '': + return error + + elif element == " ": + expression.remove(element) + if not brackets_stack.is_empty(): + return "ERROR: brackets are not paired" + + if expression[len(expression) - 1] in signs: + return "ERROR: no number after operator" + + main_sign_count = 0 + l_sign_count = 0 + number_count = 0 + logical_sign_pos = 0 + + for index in range(len(expression)): + + if expression[index] in ['+', '-']: + main_sign_count += 1 + + elif expression[index] in ['*', '/', '^', '%', '//']: + if number_count == 0: + return "ERROR: no numbers before sign" + + if index != len(expression) - 1 and expression[index + 1] in ['*', '/', '^', '%', '//']: + return "ERROR: duplicate multiply or div sign" + + elif expression[index] in logical_signs: + l_sign_count += 1 + logical_sign_pos = index + if l_sign_count > 1: + return "ERROR: more than one logical operator" + + elif expression[index] == '=': + return "ERROR: illegal usage '='" + + elif is_number(expression[index]): + number_count += 1 + if index != len(expression) - 1 and is_number(expression[index + 1]): + return "ERROR: no sign between expression" + + elif expression[index].isalpha(): + if expression[index] in math_consts: + number_count += 1 + elif expression[index] in functions: + if index == len(expression) - 1 or expression[index + 1] != '(': + return "ERROR: no brackets after function" + + else: + return "ERROR: function does not exist" + + elif expression[index] == '(': + if expression[index - 1] not in functions and expression[index - 1] not in signs \ + and index != 0: + return "ERROR: no sign or function before brackets" + + if l_sign_count == 1: + if (len(expression[:logical_sign_pos])) == 0 or (len(expression[logical_sign_pos:])) == 0 \ + or logical_sign_pos in [0, len(expression) - 1]: + return "ERROR: one of expressions around logical operator is empty" + + if number_count == 0: + return "ERROR: no numbers in expression" + + return '' diff --git a/final_task/pycalc/consts.py b/final_task/pycalc/consts.py new file mode 100644 index 00000000..c1628179 --- /dev/null +++ b/final_task/pycalc/consts.py @@ -0,0 +1,32 @@ +import operator +import math + +signs = ['+', '-', '*', '/', '^', '%', '>', '<', '=', '//', '!'] + +logical_signs = { + '>': operator.gt, + '>=': operator.ge, + '==': operator.eq, + '!=': operator.ne, + "<=": operator.le, + "<": operator.le +} + + +operators_type1 = { + '*': operator.mul, + '/': operator.truediv, + '//': operator.floordiv, + '%': operator.mod +} + +operators_main = { + '+': operator.add, + '-': operator.sub +} + +math_consts = { + "pi": math.pi, + "e": math.e + +} diff --git a/final_task/pycalc/main.py b/final_task/pycalc/main.py new file mode 100644 index 00000000..30e8b4a1 --- /dev/null +++ b/final_task/pycalc/main.py @@ -0,0 +1,305 @@ +import pycalc.stack as Stack +import importlib +import re +from pycalc.consts import * +from pycalc.check_mistakes import check_mistakes + + +functions = {'round': round, 'abs': abs} + + +def get_all_func(modules): + global functions + if 'math' not in modules: # including module math + modules.append('math') + for module in modules: # adding all functions to dictionary + workspace = importlib.import_module(module) + for name in dir(workspace): + functions[name] = getattr(workspace, name) + + +def get_args(expression): + if len(expression) == 0: + return '' + expression.append(',') + result = [] + prev = -1 + stack = Stack.Stack() + for index, part in enumerate(expression): + if part == ',' and stack.is_empty(): + result.append(float(calc(expression[prev + 1: index]))) + prev = index + elif part == '(': + stack.push(part) + elif part == ')': + stack.pop() + return result + + +def separate(expression): # separates expression to logical parts + + def check_log10(): + nonlocal current + nonlocal expression_list + if current == '10' and expression_list[len(expression_list) - 1] == 'log': + expression_list.pop() + expression_list.append('log10') + current = '' + + def fix_signs(): + nonlocal flag + nonlocal current + if flag == 'sign_main' and len(re.findall('-', current)) + len(re.findall("\+", current)) > 1: + if (-1) ** len(re.findall('-', current)) < 0: + current = '-' + else: + current = '+' + + def is_number(string): + try: + float(string) + return True + except ValueError: + return False + + expression_list = [] + flag = "number" + current = '' + for char in expression: + if char == ' ': + flag = 'space' + elif '0' <= char <= '9' or char == '.': # if part is number + if flag == 'number': # if previously symbols were numbers + current += char + else: # if previously symbols weren't number + if current != '': + fix_signs() + expression_list.append(current) + current = '' + flag = 'number' + current += char + elif 'a' <= char.lower() <= 'z': + if flag == 'function': # if previously symbols were function + current += char + else: # if previously symbols weren't numbers + if current != '': + fix_signs() + expression_list.append(current) + current = '' + flag = 'function' + current += char + + elif char in operators_type1 or char == '^': # if previously symbols were sign + if flag == 'sign_type1': + current += char + else: # if previously symbols weren't numbers + if current != '': + expression_list.append(current) + current = '' + flag = 'sign_type1' + current += char + + elif char in signs: # if previously symbols were sign + if flag == 'sign_main': + current += char + else: # if previously symbols weren't numbers + if current != '': + expression_list.append(current) + current = '' + flag = 'sign_main' + current += char + + elif char in ['(', ')']: + if current != '': + check_log10() + fix_signs() + if current != '': + expression_list.append(current) + current = '' + flag = 'bracket' + expression_list.append(char) + elif char == ',': + if current != '': + expression_list.append(current) + current = '' + flag = 'args' + expression_list.append(char) + if current != '': + expression_list.append(current) + + index = 1 + while index <= len(expression_list) - 1: + if expression_list[index] in operators_main and\ + (expression_list[index - 1] in operators_type1 or expression_list[index - 1] == '^') and\ + (is_number(expression_list[index + 1]) or expression_list[index + 1] in math_consts): + expression_list[index + 1] = expression_list[index] + expression_list[index + 1] + expression_list.pop(index) + else: + index += 1 + return expression_list + + +def calc(expression): + expression.append("/") # creating airbag + expression.append("1") + expression.append("+") + expression.append("0") + + global functions # list of functions + brackets = False # flag, if we are looking for brackets + result = 0 # result variable + main_number = '' # variable for number after + or - + number = '' # variable for other numbers + main_sign = '+' # sign before main number (default +) + func = '' # variable for function + sign = '' # sign before number + previous_sign = '' # additionaly variable for sign + stack = Stack.Stack() # stack for brackets + power_stack = Stack.Stack() # stack for powered numbers + + for index, element in enumerate(expression): + if brackets: # if in we find expression in brackets, we start searching of end bracket with stack + if element == '(': + stack.push('(') + elif element == ')': + stack.pop() + if stack.is_empty(): + end = index + if func != '': # if we find function + temp = get_args(expression[begin + 1:end]) # getting arguments for func + try: + if temp != ['']: + element = functions[func](*temp) # processing function with 1 or more args + else: + element = functions[func]() # processing function with no args + func = '' + except TypeError: + return "ERROR: wrong amount of arguments for " + func + "(..)" + + else: + element = float(calc(expression[begin + 1:end])) # if no function, just a brackets + + if sign == '^': # processing power operator section + power_stack.push(element) + sign = '' + else: + if main_number != '': + if sign != '': + if sign in ['*', '/', '//', '%']: + if number != '': + main_number = operators_type1[previous_sign](float(main_number), float(number)) + number = element + previous_sign = sign + sign = '' + else: + main_number = element + brackets = False + + else: # if no in stack + if element in math_consts: # if element is const + element = str(math_consts[element]) + elif element == '-e': + element = str(-1 * math.e) + elif element == '-pi': + element = str(-1 * math.pi) + elif element == '+e': + element = str(math.e) + elif element == '+pi': + element = str(math.pi) + + if element == '(': # start extracting expression in brackets + brackets = True + begin = index + stack.push('(') + + elif element in functions: # if element is function + func = element + + elif element in signs: # if element is sign + if element == '^': # processing power operator + sign = '^' + else: + if not power_stack.is_empty(): + last = float(power_stack.pop()) + if power_stack.is_empty(): + if number != '': + number = str(float(number) ** last) + else: + main_number = str(float(main_number) ** last) + else: + while not power_stack.is_empty(): + last = float(power_stack.pop()) ** last + main_number = str(float(main_number) ** last) + + if element in ['+', '-']: # processing + or - + if main_number != '': + if number != '': + main_number = operators_type1[previous_sign](float(main_number), float(number)) + number = '' + previous_sign = '' + result = operators_main[main_sign](result, float(main_number)) + main_number = '' + main_sign = element + + elif element in ['*', '/', '//', '%']: # processing other operators + sign = element + else: # element is number + if sign == '^': # if power operator stays before this element + power_stack.push(element) + sign = '' + else: + if main_number != '': # if main_number was found + if sign != '': + if sign in ['*', '/', '//', '%']: + if number != '': + main_number = operators_type1[previous_sign](float(main_number), float(number)) + number = element + previous_sign = sign + sign = '' + else: + main_number = element + + if main_number != '': # adding last number to result + if main_sign == '+': + result += float(main_number) + elif main_sign == '-': + result -= float(main_number) + if result == 0 and main_number == '': + return '' + else: + return str(result) + + +def pycalc(expression, modules=list()): + global functions + get_all_func(modules) + new = '' + + for char in expression: + if char != ' ': + new += char + error = check_mistakes(separate(expression), functions) + if error == "": # handling some mistakes + expression = separate(new) # separating expression (look separate function) + for index, element in enumerate(expression): # looking for logical signs + if element in logical_signs: + left = calc(expression[:index]) + right = calc(expression[index + 1:]) + try: + left = float(left) + except ValueError: + return left + + try: + right = float(right) + except ValueError: + return left + return logical_signs[element](left, right) + result = calc(expression) + try: + result = float(result) # start counting + except ValueError: + pass + return result + else: + return error diff --git a/final_task/pycalc/stack.py b/final_task/pycalc/stack.py new file mode 100644 index 00000000..2ea6abbd --- /dev/null +++ b/final_task/pycalc/stack.py @@ -0,0 +1,15 @@ +class Stack: + def __init__(self): + self.__items = [] + + def is_empty(self): + return self.__items == [] + + def push(self, item): + self.__items.append(item) + + def pop(self): + return self.__items.pop() + + def size(self): + return len(self.__items) diff --git a/final_task/pycalc/start.py b/final_task/pycalc/start.py new file mode 100644 index 00000000..4f34429f --- /dev/null +++ b/final_task/pycalc/start.py @@ -0,0 +1,18 @@ +import argparse +import pycalc.main as Main + + +def main(): + parser = argparse.ArgumentParser(description="Pure-python command-line calculator") + parser.add_argument("EXPRESSION", type=str, help='expression string to evaluate') + parser.add_argument('-m MODULE[MODULE...]', '--use-modules MODULE[MODULE...]', nargs='*', + help='additional modules to use', dest='modules', type=str, default=['math']) + args = parser.parse_args() + answer = Main.pycalc(args.EXPRESSION, args.modules) + if type(answer) == float or type(answer) == int: + if answer % 1 == 0: + print(int(answer)) + else: + print(answer) + else: + print(answer) diff --git a/final_task/pycalc/test_main.py b/final_task/pycalc/test_main.py new file mode 100644 index 00000000..3c056797 --- /dev/null +++ b/final_task/pycalc/test_main.py @@ -0,0 +1,53 @@ +import pycalc.main as mn +import unittest +from pycalc.check_mistakes import check_mistakes +import math as m + + +class TestFunctions(unittest.TestCase): + + def test_check_mistakes(self): + self.assertEqual(check_mistakes([]), "ERROR: empty expression") + self.assertEqual(check_mistakes(['123', '-']), "ERROR: no number after operator") + self.assertEqual(check_mistakes(['123', '-', '(', ')']), "ERROR: empty brackets") + self.assertEqual(check_mistakes(['123', '-', '(', '-', ')']), "ERROR: no number after operator") + self.assertEqual(check_mistakes(['func', '(', ')']), "ERROR: function does not exist") + self.assertEqual(check_mistakes(['123', '+', '23', '*', '(', '12', '-', '23', '+', '12', ')', ')']), + "ERROR: brackets are not paired") + self.assertEqual(check_mistakes(['123', '+', '23', '*', '(', '12', '-', '23', '(', '12', ')', ')']), + "ERROR: no sign or function before brackets") + self.assertEqual(check_mistakes(['123', '+', '23', '/', ' ', '/', '(', '12', '-', '3', ')']), + "ERROR: duplicate multiply or div sign") + + def test_get_args(self): + self.assertEqual(mn.get_args(['12', ',', 'pi', ',', '12', '+', '32', ',', '43', '*', '2']), [12, m.pi, 44, 86]) + + def test_separate(self): + self.assertEqual(mn.separate(''), []) + self.assertEqual(mn.separate("123-5"), ['123', '-', '5']) + self.assertEqual(mn.separate("123-(7)"), ['123', '-', '(', '7', ')']) + self.assertEqual(mn.separate("12-(-)"), ['12', '-', '(', '-', ')']) + self.assertEqual(mn.separate("func()"), ['func', '(', ')']) + self.assertEqual(mn.separate("123+23*(12-23+12)"), + ['123', '+', '23', '*', '(', '12', '-', '23', '+', '12', ')']) + self.assertEqual(mn.separate("123+23*(12-23(12))"), + ['123', '+', '23', '*', '(', '12', '-', '23', '(', '12', ')', ')']) + self.assertEqual(mn.separate("123+23/ /(12^3)"), + ['123', '+', '23', '/', '/', '(', '12', '^', '3', ')']) + self.assertEqual(mn.separate("log10(123) + --- 23"), + ['log10', '(', '123', ')', '+', '---', '23']) + + def test_calc(self): + self.assertEqual(mn.calc(["10"]), '10.0') + self.assertEqual(mn.calc(['123', '-', '10']), '113.0') + self.assertEqual(mn.calc(['123', '-', '(', '10', '+', '7', ')']), '106.0') + self.assertEqual(mn.calc(['2', '^', '(', '5', '-', '7', ')']), '0.25') + self.assertEqual(mn.calc(['123', '/', '23', '*', '(', '12', '-', '23', '^', '2', ')']), + "-2764.8260869565215") + self.assertEqual(mn.calc(['15', '+', '23', '//', '(', '17', '-', '23', ')']), + "11.0") + + def test_pycalc(self): + self.assertAlmostEqual(float(mn.pycalc('15+23*cos(17-23)')), 37.08391659295842) + self.assertEqual(float(mn.pycalc('round(3.6675)')), 4.0) + self.assertEqual(mn.pycalc('2^4^2'), 65536) diff --git a/final_task/setup.py b/final_task/setup.py index e69de29b..19d54634 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -0,0 +1,11 @@ +from setuptools import setup + +setup(name='pycalc', + version='0.1', + author_email='andrey-truhan@tut.by', + description='Alpha version pure-python command-line calculator', + author='Andrei Trukhan', + license='free', + packages=['pycalc'], + zip_safe=False, + entry_points={"console_scripts": ["pycalc=pycalc.start:main"]}) diff --git a/final_task/setup.sh b/final_task/setup.sh new file mode 100644 index 00000000..f29b6ec4 --- /dev/null +++ b/final_task/setup.sh @@ -0,0 +1,4 @@ +#!/bin/bash +python setup.py sdist; +sudo pip install dist/pycalc-0.1.tar.gz; + diff --git a/final_task/test_main.py b/final_task/test_main.py new file mode 100644 index 00000000..3c056797 --- /dev/null +++ b/final_task/test_main.py @@ -0,0 +1,53 @@ +import pycalc.main as mn +import unittest +from pycalc.check_mistakes import check_mistakes +import math as m + + +class TestFunctions(unittest.TestCase): + + def test_check_mistakes(self): + self.assertEqual(check_mistakes([]), "ERROR: empty expression") + self.assertEqual(check_mistakes(['123', '-']), "ERROR: no number after operator") + self.assertEqual(check_mistakes(['123', '-', '(', ')']), "ERROR: empty brackets") + self.assertEqual(check_mistakes(['123', '-', '(', '-', ')']), "ERROR: no number after operator") + self.assertEqual(check_mistakes(['func', '(', ')']), "ERROR: function does not exist") + self.assertEqual(check_mistakes(['123', '+', '23', '*', '(', '12', '-', '23', '+', '12', ')', ')']), + "ERROR: brackets are not paired") + self.assertEqual(check_mistakes(['123', '+', '23', '*', '(', '12', '-', '23', '(', '12', ')', ')']), + "ERROR: no sign or function before brackets") + self.assertEqual(check_mistakes(['123', '+', '23', '/', ' ', '/', '(', '12', '-', '3', ')']), + "ERROR: duplicate multiply or div sign") + + def test_get_args(self): + self.assertEqual(mn.get_args(['12', ',', 'pi', ',', '12', '+', '32', ',', '43', '*', '2']), [12, m.pi, 44, 86]) + + def test_separate(self): + self.assertEqual(mn.separate(''), []) + self.assertEqual(mn.separate("123-5"), ['123', '-', '5']) + self.assertEqual(mn.separate("123-(7)"), ['123', '-', '(', '7', ')']) + self.assertEqual(mn.separate("12-(-)"), ['12', '-', '(', '-', ')']) + self.assertEqual(mn.separate("func()"), ['func', '(', ')']) + self.assertEqual(mn.separate("123+23*(12-23+12)"), + ['123', '+', '23', '*', '(', '12', '-', '23', '+', '12', ')']) + self.assertEqual(mn.separate("123+23*(12-23(12))"), + ['123', '+', '23', '*', '(', '12', '-', '23', '(', '12', ')', ')']) + self.assertEqual(mn.separate("123+23/ /(12^3)"), + ['123', '+', '23', '/', '/', '(', '12', '^', '3', ')']) + self.assertEqual(mn.separate("log10(123) + --- 23"), + ['log10', '(', '123', ')', '+', '---', '23']) + + def test_calc(self): + self.assertEqual(mn.calc(["10"]), '10.0') + self.assertEqual(mn.calc(['123', '-', '10']), '113.0') + self.assertEqual(mn.calc(['123', '-', '(', '10', '+', '7', ')']), '106.0') + self.assertEqual(mn.calc(['2', '^', '(', '5', '-', '7', ')']), '0.25') + self.assertEqual(mn.calc(['123', '/', '23', '*', '(', '12', '-', '23', '^', '2', ')']), + "-2764.8260869565215") + self.assertEqual(mn.calc(['15', '+', '23', '//', '(', '17', '-', '23', ')']), + "11.0") + + def test_pycalc(self): + self.assertAlmostEqual(float(mn.pycalc('15+23*cos(17-23)')), 37.08391659295842) + self.assertEqual(float(mn.pycalc('round(3.6675)')), 4.0) + self.assertEqual(mn.pycalc('2^4^2'), 65536) diff --git a/test_main.py b/test_main.py new file mode 100644 index 00000000..3c056797 --- /dev/null +++ b/test_main.py @@ -0,0 +1,53 @@ +import pycalc.main as mn +import unittest +from pycalc.check_mistakes import check_mistakes +import math as m + + +class TestFunctions(unittest.TestCase): + + def test_check_mistakes(self): + self.assertEqual(check_mistakes([]), "ERROR: empty expression") + self.assertEqual(check_mistakes(['123', '-']), "ERROR: no number after operator") + self.assertEqual(check_mistakes(['123', '-', '(', ')']), "ERROR: empty brackets") + self.assertEqual(check_mistakes(['123', '-', '(', '-', ')']), "ERROR: no number after operator") + self.assertEqual(check_mistakes(['func', '(', ')']), "ERROR: function does not exist") + self.assertEqual(check_mistakes(['123', '+', '23', '*', '(', '12', '-', '23', '+', '12', ')', ')']), + "ERROR: brackets are not paired") + self.assertEqual(check_mistakes(['123', '+', '23', '*', '(', '12', '-', '23', '(', '12', ')', ')']), + "ERROR: no sign or function before brackets") + self.assertEqual(check_mistakes(['123', '+', '23', '/', ' ', '/', '(', '12', '-', '3', ')']), + "ERROR: duplicate multiply or div sign") + + def test_get_args(self): + self.assertEqual(mn.get_args(['12', ',', 'pi', ',', '12', '+', '32', ',', '43', '*', '2']), [12, m.pi, 44, 86]) + + def test_separate(self): + self.assertEqual(mn.separate(''), []) + self.assertEqual(mn.separate("123-5"), ['123', '-', '5']) + self.assertEqual(mn.separate("123-(7)"), ['123', '-', '(', '7', ')']) + self.assertEqual(mn.separate("12-(-)"), ['12', '-', '(', '-', ')']) + self.assertEqual(mn.separate("func()"), ['func', '(', ')']) + self.assertEqual(mn.separate("123+23*(12-23+12)"), + ['123', '+', '23', '*', '(', '12', '-', '23', '+', '12', ')']) + self.assertEqual(mn.separate("123+23*(12-23(12))"), + ['123', '+', '23', '*', '(', '12', '-', '23', '(', '12', ')', ')']) + self.assertEqual(mn.separate("123+23/ /(12^3)"), + ['123', '+', '23', '/', '/', '(', '12', '^', '3', ')']) + self.assertEqual(mn.separate("log10(123) + --- 23"), + ['log10', '(', '123', ')', '+', '---', '23']) + + def test_calc(self): + self.assertEqual(mn.calc(["10"]), '10.0') + self.assertEqual(mn.calc(['123', '-', '10']), '113.0') + self.assertEqual(mn.calc(['123', '-', '(', '10', '+', '7', ')']), '106.0') + self.assertEqual(mn.calc(['2', '^', '(', '5', '-', '7', ')']), '0.25') + self.assertEqual(mn.calc(['123', '/', '23', '*', '(', '12', '-', '23', '^', '2', ')']), + "-2764.8260869565215") + self.assertEqual(mn.calc(['15', '+', '23', '//', '(', '17', '-', '23', ')']), + "11.0") + + def test_pycalc(self): + self.assertAlmostEqual(float(mn.pycalc('15+23*cos(17-23)')), 37.08391659295842) + self.assertEqual(float(mn.pycalc('round(3.6675)')), 4.0) + self.assertEqual(mn.pycalc('2^4^2'), 65536)