Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions __init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
name = "pycalc"
3 changes: 3 additions & 0 deletions __main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import pycalc

pycalc.main()
126 changes: 126 additions & 0 deletions calc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import re
import math
from inspect import signature
from inspect import getargs
from operators import *
from split import *


def tran_in_pol_not(inp):
"""Перевод введённого математического выражения в обратную польскую нотацию."""
out_stack = []
slave_stack = []
# парсинг выражения
split_list = split_string(inp, list_of_op)
for i, char in enumerate(split_list):
if char is '(':
slave_stack.append(char)
elif char is ')':
if '(' not in slave_stack:
print("ERROR: brackets are not balanced")
raise Exception
while slave_stack:
elem = slave_stack.pop()
if elem is not '(':
out_stack.append(elem)
else:
break
elif is_num(char):
out_stack.append(char)
elif char in list_of_op:
if len(split_list) == 1:
print("ERROR: it is essential to have at list one operand")
raise ArithmeticError
if char in unary_op:
inf_op = list(split_by_prefix(char, ['+', '-']))[0]
if i + 1 != len(split_list) and (i == 0 or is_num(split_list[i + 1])):
out_stack.extend([split_list[i + 1], 1])
split_list[i + 1] = inf_op
else:
if i + 1 == len(split_list):
out_stack.extend([1, inf_op])
for index in range(i + 1, len(split_list)):
if is_num(split_list[index]):
if index + 1 == len(split_list):
split_list.extend([inf_op, 1])
break
split_list.insert(index, 1)
split_list.insert(index + 1, inf_op)
break
elif operators[char].type == 'inf':
'''if len(slave_stack) == 0:
slave_stack.append(char)
else:'''
if char == '-':
if i == 0:
out_stack.append(0)
elif i + 1 == len(split_list):
print("ERROR: Invalid expression: there is no 2nd operand after -.")
raise ArithmeticError
elif is_num(split_list[i + 1]):
if split_list[i - 1] == '(' and split_list[i + 2] != ')':
out_stack.append(0)

while slave_stack:
item = slave_stack.pop()
if item in parentheses or (item in list_of_op and
operators[char].priority < operators[item].priority):
slave_stack.append(item)
break
else:
out_stack.append(item)
slave_stack.append(char)
else:
slave_stack.append(char)

while len(slave_stack) > 0:
if '(' in slave_stack:
print("ERROR: brackets are not balanced")
raise Exception
out_stack.append(slave_stack.pop())
return out_stack


def pols_not(exp1):
"""Вычисление результата выражения по введённой польской нотации."""
stack = []
index = 0
value = 0
end = False
while not end:
item = exp1[index]
# добавить в стек прочитанное число,
# или результат операции
if is_num(item):
value = float(item)
stack.append(value)
else:
if item in operators:
foo = operators[item].func
elif item in math_func:
foo = math_func[item]
else:
try:
foo = getattr(math, item)
except Exception as inst:
print("Unexpected instance:", inst)
raise
# Evaluate a function according to the type an args or just set a value
if callable(foo):
try:
if item in list_of_op and operators[item].type == 'inf':
op1, op2 = stack.pop(), stack.pop()
value = foo(op2, op1)
else:
value = foo(stack.pop())
# выполнить операцию по ключу С
except ArithmeticError as err:
print("Handling run-time error with evaluating a function or taking args: ", err)
raise
else:
value = foo
stack.append(value)
index += 1
if index >= len(exp1):
end = True
return value
1 change: 1 addition & 0 deletions final_task/pycalc/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
name = "pycalc"
3 changes: 3 additions & 0 deletions final_task/pycalc/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import pycalc

pycalc.main()
126 changes: 126 additions & 0 deletions final_task/pycalc/calc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import re
import math
from inspect import signature
from inspect import getargs
from operators import *
from split import *


def tran_in_pol_not(inp):
"""Перевод введённого математического выражения в обратную польскую нотацию."""
out_stack = []
slave_stack = []
# парсинг выражения
split_list = split_string(inp, list_of_op)
for i, char in enumerate(split_list):
if char is '(':
slave_stack.append(char)
elif char is ')':
if '(' not in slave_stack:
print("ERROR: brackets are not balanced")
raise Exception
while slave_stack:
elem = slave_stack.pop()
if elem is not '(':
out_stack.append(elem)
else:
break
elif is_num(char):
out_stack.append(char)
elif char in list_of_op:
if len(split_list) == 1:
print("ERROR: it is essential to have at list one operand")
raise ArithmeticError
if char in unary_op:
inf_op = list(split_by_prefix(char, ['+', '-']))[0]
if i + 1 != len(split_list) and (i == 0 or is_num(split_list[i + 1])):
out_stack.extend([split_list[i + 1], 1])
split_list[i + 1] = inf_op
else:
if i + 1 == len(split_list):
out_stack.extend([1, inf_op])
for index in range(i + 1, len(split_list)):
if is_num(split_list[index]):
if index + 1 == len(split_list):
split_list.extend([inf_op, 1])
break
split_list.insert(index, 1)
split_list.insert(index + 1, inf_op)
break
elif operators[char].type == 'inf':
'''if len(slave_stack) == 0:
slave_stack.append(char)
else:'''
if char == '-':
if i == 0:
out_stack.append(0)
elif i + 1 == len(split_list):
print("ERROR: Invalid expression: there is no 2nd operand after -.")
raise ArithmeticError
elif is_num(split_list[i + 1]):
if split_list[i - 1] == '(' and split_list[i + 2] != ')':
out_stack.append(0)

while slave_stack:
item = slave_stack.pop()
if item in parentheses or (item in list_of_op and
operators[char].priority < operators[item].priority):
slave_stack.append(item)
break
else:
out_stack.append(item)
slave_stack.append(char)
else:
slave_stack.append(char)

while len(slave_stack) > 0:
if '(' in slave_stack:
print("ERROR: brackets are not balanced")
raise Exception
out_stack.append(slave_stack.pop())
return out_stack


def pols_not(exp1):
"""Вычисление результата выражения по введённой польской нотации."""
stack = []
index = 0
value = 0
end = False
while not end:
item = exp1[index]
# добавить в стек прочитанное число,
# или результат операции
if is_num(item):
value = float(item)
stack.append(value)
else:
if item in operators:
foo = operators[item].func
elif item in math_func:
foo = math_func[item]
else:
try:
foo = getattr(math, item)
except Exception as inst:
print("Unexpected instance:", inst)
raise
# Evaluate a function according to the type an args or just set a value
if callable(foo):
try:
if item in list_of_op and operators[item].type == 'inf':
op1, op2 = stack.pop(), stack.pop()
value = foo(op2, op1)
else:
value = foo(stack.pop())
# выполнить операцию по ключу С
except ArithmeticError as err:
print("Handling run-time error with evaluating a function or taking args: ", err)
raise
else:
value = foo
stack.append(value)
index += 1
if index >= len(exp1):
end = True
return value
28 changes: 28 additions & 0 deletions final_task/pycalc/operators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import math
from collections import namedtuple


Operator = namedtuple('Operator', ("priority", "type", "func"))
operators = {
'+': Operator(3, 'inf', lambda a, b: a + b),
'-': Operator(3, 'inf', lambda a, b: a - b),
'*': Operator(2, 'inf', lambda a, b: a * b),
'/': Operator(2, 'inf', lambda a, b: a / b),
'//': Operator(2, 'inf', lambda a, b: a // b),
'%': Operator(2, 'inf', lambda a, b: getattr(math, 'fmod')(a, b)),
'^': Operator(1, 'inf', lambda a, b: getattr(math, 'pow')(a, b)),
'<': Operator(4, 'inf', lambda a, b: a < b),
'<=': Operator(4, 'inf', lambda a, b: a <= b),
'==': Operator(5, 'inf', lambda a, b: a == b),
'!=': Operator(5, 'inf', lambda a, b: a != b),
'>=': Operator(4, 'inf', lambda a, b: a >= b),
'>': Operator(4, 'inf', lambda a, b: a > b),
'!': Operator(5, 'post', lambda a: getattr(math, 'factorial')(a))
}

math_func = {'abs': abs,
'round': round}
[math_func.update({attr: getattr(math, attr)}) for attr in dir(math) if callable(getattr(math, attr))]
parentheses = {'(': 3, ')': 3}
unary_op = ['++', '--']
list_of_op = unary_op + list(operators.keys()) + list(parentheses.keys())
14 changes: 14 additions & 0 deletions final_task/pycalc/pycalc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import argparse
import calc


def main():
parser = argparse.ArgumentParser(description="Pure-python command-line calculator.")
parser.add_argument("EXPRESSION", help="Please, enter an expression for calculating", type=str)
args = parser.parse_args()
result = calc.pols_not(calc.tran_in_pol_not(args.EXPRESSION))
print(result)


if __name__ == '__main__':
main()
38 changes: 38 additions & 0 deletions final_task/pycalc/split.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import re
import operators
from operators import *


def is_num(char):
try:
float(char)
return True
except ValueError:
return False


def split_string(inp, prefixes=list_of_op):
"""Разбиение строки по операторам и операндам с помощью регулярных выражений"""
"""String splitting by operators and operands using regular expressions"""
str_list = re.findall('(?:\d*\.\d+)|(?:\d+\.?)|[a-zA-Z\d]+|\W+', inp)
new_str = []
for item in str_list:
if is_num(item) or item in list_of_op:
new_str.append(item)
else:
new_str.extend(split_by_prefix(item, prefixes))
return [i.strip(' ') for i in new_str]


def split_by_prefix(string, prefixes):
"""Разбиение строки по префиксам."""
regex = re.compile('|'.join(map(re.escape, prefixes)))
while True:
match = regex.match(string)
if not match:
break
end = match.end()
yield string[:end]
string = string[end:]
if string:
yield string
Loading