-
Notifications
You must be signed in to change notification settings - Fork 27
Hanna Kovalenok #41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Hanna Kovalenok #41
Changes from all commits
455f590
36ce838
5f8cca7
701c01d
dd0d11b
b1e5f2a
4ad48e5
688293d
f8e31da
97b9c24
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import re | ||
import pycalc.operators as operators | ||
import pycalc.difcalc as difcalc | ||
from numbers import Number | ||
import importlib.util | ||
from os import path | ||
|
||
|
||
class CheckAndChange(): | ||
|
||
def do_all_changes(self, expr, module): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Для читаемости можно добавлять аннотации функций/методов |
||
self.add_args(module) | ||
|
||
if not re.search(r'[0-9]+', expr): | ||
const = re.search(r'[A-ZAa-z]+', expr) | ||
if not const: | ||
raise Exception("No Numbers in expression") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Создание кастомных эксепшенов тоже улучшает читаемость кода |
||
else: | ||
if const[0] not in difcalc.ComplexCalc.const and const[0] not in difcalc.ComplexCalc.math_functions: | ||
raise Exception("Check your const or function") | ||
|
||
expr = expr.replace("//", "&") | ||
self.correct_brackets(expr) | ||
self.correct_spaces(expr) | ||
expr = expr.replace(" ", "") | ||
|
||
return expr | ||
|
||
def add_args(self, modul): | ||
if modul: | ||
base = path.basename(modul) | ||
|
||
module_name = path.splitext(base)[0] | ||
spec = importlib.util.spec_from_file_location(module_name, modul) | ||
module = importlib.util.module_from_spec(spec) | ||
spec.loader.exec_module(module) | ||
|
||
new_functions = { | ||
attr: getattr(module, attr) for attr in dir(module) if callable(getattr(module, attr)) | ||
} | ||
difcalc.ComplexCalc.math_functions.update(new_functions) | ||
|
||
new_const = { | ||
attr: getattr(module, attr) for attr in dir(module)if isinstance(getattr(module, attr), Number) | ||
} | ||
difcalc.ComplexCalc.const.update(new_const) | ||
|
||
def correct_spaces(self, expr): | ||
searcher = expr.find(" ") | ||
expression = expr | ||
|
||
while searcher != -1 and expression != "": | ||
if searcher != len(expression) - 1 and searcher != 0: | ||
if expression[searcher - | ||
1].isdigit() and expression[searcher + 1].isdigit(): | ||
raise Exception("must not be 'digit' 'space' 'digit'") | ||
|
||
if expression[searcher - 1] in operators.operators \ | ||
and expression[searcher + 1] in operators.operators: | ||
raise Exception( | ||
"must not be 'operator' 'space' 'operator'") | ||
|
||
if expression[searcher - 1] in difcalc.ComplexCalc.compare \ | ||
and expression[searcher + 1] in difcalc.ComplexCalc.compare: | ||
raise Exception("Check your spaces betwin") | ||
|
||
expression = expression[searcher + 1:] | ||
searcher = expression.find(" ") | ||
else: | ||
if searcher == len(expression) - 1: | ||
break | ||
if searcher == 0: | ||
expression = expression[1:] | ||
|
||
def correct_brackets(self, expr): | ||
counter = 0 | ||
for one in expr: | ||
|
||
if one == "(": | ||
counter += 1 | ||
elif one == ")": | ||
counter -= 1 | ||
if counter < 0: | ||
raise Exception("check brackets! ") | ||
else: | ||
if counter != 0: | ||
raise Exception("check brackets! ") |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
import re | ||
import pycalc.easyCalculation as easyCalculation | ||
import math | ||
from numbers import Number | ||
|
||
|
||
class ComplexCalc(easyCalculation.Calculator): | ||
"""more functional calculator than "Calculator" """ | ||
|
||
def __init__(self): | ||
"""initializing data from "math" """ | ||
ComplexCalc.const = { | ||
**{attr: getattr(math, attr) for attr in dir(math) if isinstance(getattr(math, attr), Number)}, | ||
**{"True": 1, "False": 0} | ||
} | ||
|
||
ComplexCalc.math_functions = { | ||
**{attr: getattr(math, attr) for attr in dir(math) if callable(getattr(math, attr))}, | ||
**{"abs": lambda a: abs(a), | ||
"round": lambda a: round(a), | ||
"pow": lambda a, b: pow(a, b)} | ||
} | ||
|
||
def expression_search(self, expr): | ||
"""search function or constant and calc power if function negative""" | ||
|
||
while True: | ||
|
||
func = re.search(r'[A-ZAa-z]+1?0?', expr) | ||
|
||
if func is None: | ||
return self.search_braсkets(expr) | ||
|
||
afterExpr = func.end() | ||
place = func.start() | ||
if func[0] in self.const: | ||
|
||
rezult = self.const[func[0]] | ||
expr = expr[:place] + str(rezult) + expr[afterExpr:] | ||
continue | ||
|
||
searcher = 0 | ||
count = 1 | ||
for one in expr[afterExpr + 1:]: | ||
|
||
searcher += 1 | ||
if one == ")": | ||
count -= 1 | ||
if one == "(": | ||
count += 1 | ||
if count == 0: | ||
break | ||
end = searcher + afterExpr | ||
# выкинуть если конец строки | ||
if expr[afterExpr] != '(': | ||
|
||
raise Exception( | ||
"the expression must be written in the following way 'function(expression)'") | ||
|
||
else: | ||
|
||
rezult = self._find_replacement( | ||
func[0], expr[afterExpr + 1:end]) | ||
expr = expr[:place] + rezult + expr[end + 1:] | ||
if float(rezult) < 0: | ||
end = place + len(rezult) - 1 | ||
expr = self._calc_if_power(expr, place, end) | ||
|
||
def _find_replacement(self, func, expr): | ||
"""count all arguments from function""" | ||
if func in ComplexCalc.math_functions: | ||
allargs = self._commasplit(expr) | ||
|
||
float_args = [] | ||
for each in allargs: | ||
float_args.append(float(self.expression_search(each))) | ||
|
||
rezult = '{:.15f}'.format( | ||
ComplexCalc.math_functions[func]( | ||
*float_args)) | ||
|
||
else: | ||
|
||
raise Exception("Indefined function") | ||
return str(rezult) | ||
|
||
def _commasplit(self, expr: str): | ||
"""search arguments for function""" | ||
breketscounter = 0 | ||
preve = 0 | ||
count = 1 | ||
split = [] | ||
for each in expr: | ||
if breketscounter == 0 and each == ",": | ||
split.append(expr[preve:count - 1]) | ||
preve = count | ||
|
||
elif each == "(": | ||
breketscounter += 1 | ||
elif each == ")": | ||
breketscounter -= 1 | ||
count += 1 | ||
|
||
split.append(expr[preve:count]) | ||
|
||
return split | ||
compare = { | ||
|
||
">": 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 | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Зачем такое количество пустых строк? |
||
|
||
} | ||
|
||
def calculate(self, expr): | ||
"""begin of calculatoion, search compare""" | ||
place = re.search(r'(>=)|(>)|(<=)|(<)|(!=)|(==)', expr) | ||
|
||
while place: | ||
after = re.search(r'(>=)|(>)|(<=)|(<)|(!=)|(==)', | ||
expr[place.end():]) | ||
number_one = self.expression_search(expr[:place.start()]) | ||
|
||
if not after: | ||
number_two = self.expression_search(expr[place.end():]) | ||
else: | ||
number_two = self.expression_search( | ||
expr[place.end():after.start() + place.end()]) | ||
|
||
if number_one is not None and number_two is not None: | ||
rezult = ComplexCalc.compare[place[0]](number_one, number_two) | ||
end = "" | ||
|
||
if after: | ||
if after.start() == 0: | ||
raise Exception("no symbols between compare") | ||
end = expr[after.end() + place.end():] | ||
expr = str(rezult) + after[0] + end | ||
else: | ||
return rezult | ||
|
||
else: | ||
raise Exception( | ||
"uncorrect expression must be 'expr' operator 'expr'") | ||
place = re.search(r'(>=)|(>)|(<=)|(<)|(!=)|(==)', expr) | ||
|
||
return self.expression_search(expr) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
import re | ||
import pycalc.operators as operators | ||
|
||
|
||
class Calculator(): | ||
"""calculator for counting simple expression""" | ||
|
||
def _calculation(self, expr): | ||
"""find all binary operation, "^" work in a different way """ | ||
|
||
place = expr.rfind("^") | ||
|
||
while place != -1: | ||
findBefore = self.search_simple_number(expr[:place]) | ||
begin = place - findBefore.end() | ||
findAfter = self.search_number_from_begin(expr[place:]) | ||
end = place + findAfter.end() | ||
expr = self.__binary_operation(expr, begin, place, end) | ||
|
||
place = expr.rfind("^") | ||
|
||
place = re.search(r'/|\*|%|&', expr) | ||
|
||
while place: | ||
point = place.start() | ||
findBefore = self.search_number_from_end(expr[:point]) | ||
begin = point - findBefore.end() + 1 | ||
findAfter = self.search_number_from_begin(expr[point:]) | ||
end = point + findAfter.end() | ||
expr = self.__binary_operation(expr, begin, point, end) | ||
place = re.search(r'/|\*|%|&', expr) | ||
|
||
return self.sum(expr) | ||
|
||
def sum(self, expr): | ||
"""sum all elements as elements with binary minus""" | ||
|
||
if expr[-1] == "+" or expr[-1] == "-": | ||
raise Exception( | ||
"'+' or '-' and bla-bla-blah mustn' be the last even in brackets") | ||
|
||
summing = 0 | ||
number = 0 | ||
while expr != "": | ||
find = re.search(r'([+-]+)?([0-9]+([.][0-9]*)?|[.][0-9]+)', expr) | ||
if find.start() != 0: | ||
raise Exception("Undefine operator") | ||
number = find[0] | ||
|
||
expr = expr[find.end():] | ||
a = self.unary_rezult(number) | ||
summing += a | ||
return summing | ||
|
||
def unary_rezult(self, number): | ||
"""just coubt all minuses and define symbol of number""" | ||
minus = number.count("-") | ||
plus = number.count("+") | ||
real_number = number[plus + minus:] | ||
if minus % 2 == 1: | ||
real_number = float("-" + real_number) | ||
else: | ||
real_number = float(real_number) | ||
return real_number | ||
|
||
def search_braсkets(self, expr): | ||
"""search lowest bracket's level""" | ||
expr = expr.replace(" ", "") | ||
|
||
while "(" in expr: | ||
|
||
end = expr.find(")") | ||
if end != len(expr) - 1\ | ||
and expr[end + 1] not in operators.operators: | ||
Exception("no operator after brackets") | ||
|
||
begin = expr[:end].rfind("(") | ||
if begin + 1 == end: | ||
raise Exception("no Number in brakets") | ||
|
||
if begin != 0 and expr[begin - 1] not in operators.operators \ | ||
and expr[begin - 1] != '-' and expr[begin - 1] != '+': | ||
raise Exception("no operators before brackets") | ||
|
||
rezult = self._calculation("+" + expr[begin + 1:end]) | ||
expr = expr[:begin] + str(rezult) + expr[end + 1:] | ||
|
||
if rezult < 0: | ||
end = begin + len(str(rezult)) - 1 | ||
expr = self._calc_if_power(expr, begin, end) | ||
|
||
rezult = self._calculation("+" + expr) | ||
|
||
return rezult | ||
|
||
def _calc_if_power(self, expr, begin, braket): | ||
"""power doesn't know about unary minus, therefore it's calculate separately""" | ||
place = braket + 1 | ||
if braket is not len(expr) - 1 and expr[place] is "^": | ||
findAfter = self.search_number_from_begin(expr[place:]) | ||
end = place + findAfter.end() | ||
expr = self.__binary_operation(expr, begin, place, end) | ||
return expr | ||
|
||
def __binary_operation(self, expr, begin, place, end): | ||
"""calculate binary operation""" | ||
rezult = '{:.15f}'.format(operators.operators[expr[place]]( | ||
self.unary_rezult(expr[begin:place]), self.unary_rezult(expr[place + 1:end]))) | ||
|
||
before = expr[:begin] | ||
after = expr[end:] | ||
expr = before + rezult + after | ||
return expr | ||
|
||
def search_number_from_begin(self, expr): | ||
"""searching number after operatior""" | ||
number = re.search( | ||
r'([+-]+)?([0-9]+([.][0-9]*)?|[.][0-9]+)', expr) | ||
|
||
if not number or number.start() != 1: | ||
raise Exception( | ||
"the expression should be written in the following form 'number operator number'") | ||
|
||
return number | ||
|
||
def search_number_from_end(self, expr): | ||
"""searching number before operator""" | ||
number = re.search( | ||
r'([0-9]+([.][0-9]*)?|[.][0-9]+)([+-]+)?', expr[::-1]) | ||
|
||
if not number or number.start() != 0: | ||
raise Exception( | ||
"the expression should be written in the following form 'number operator number'") | ||
|
||
return number | ||
|
||
def search_simple_number(self, expr): | ||
"""search number before power""" | ||
number = re.search(r'([0-9]+([.][0-9]*)?|[.][0-9]+)', expr[::-1]) | ||
return number |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import operator | ||
|
||
|
||
operators = { | ||
"^": operator.pow, | ||
"*": operator.mul, | ||
"/": operator.truediv, | ||
"%": operator.mod, | ||
"&": operator.floordiv, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Порядок объявления импортов описан в PEP8