Skip to content
Open
Empty file added final_task/pycalc/__init__.py
Empty file.
18 changes: 18 additions & 0 deletions final_task/pycalc/calculator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from . import corrector
from . import converter
from . import evaluator


class Calculator(object):
def __init__(self, **kwargs):

self.corrector = corrector.Corrector()
self.converter = converter.Converter()
self.evaluator = evaluator.Evaluator()

def calculate(self, iExpr):
"""function for final calculate"""
correctedExpr = self.corrector.correct(iExpr)
convertedExpr = self.converter.convert(correctedExpr)
evaluatedExpr = self.evaluator.evaluate(convertedExpr)
return evaluatedExpr
150 changes: 150 additions & 0 deletions final_task/pycalc/calculator_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import unittest
from . import calculator
import math


class TestCalculate(unittest.TestCase):
def setUp(self):
self.calculator = calculator.Calculator()

def testUnary(self):
iExpr = "-13"
self.assertEqual(self.calculator.calculate(iExpr), eval(iExpr))
iExpr = "6-(-13)"
self.assertEqual(self.calculator.calculate(iExpr), eval(iExpr))
iExpr = "1---1"
self.assertEqual(self.calculator.calculate(iExpr), eval(iExpr))
iExpr = "-+---+-1"
self.assertEqual(self.calculator.calculate(iExpr), eval(iExpr))

def testOppriority(self):
iExpr = "1+2*2"
self.assertEqual(self.calculator.calculate(iExpr), eval(iExpr))
iExpr = "1+(2+3*2)*3"
self.assertEqual(self.calculator.calculate(iExpr), eval(iExpr))
iExpr = "10*(2+1)"
self.assertEqual(self.calculator.calculate(iExpr), eval(iExpr))
iExpr = "10^(2+1)"
self.assertEqual(self.calculator.calculate(iExpr), eval("10**(2+1)"))
iExpr = "100/3^2"
self.assertEqual(self.calculator.calculate(iExpr), eval("100/3**2"))
iExpr = "100/3%2^2"
self.assertEqual(self.calculator.calculate(iExpr), eval("100/3%2**2"))

def testFunc(self):
iExpr = "pi+e"
self.assertEqual(self.calculator.calculate(iExpr), eval("math.pi+math.e"))
iExpr = "log(e)"
self.assertEqual(self.calculator.calculate(iExpr), eval("math.log(math.e)"))
iExpr = "sin(pi/2)"
self.assertEqual(self.calculator.calculate(iExpr), eval("math.sin(math.pi/2)"))
iExpr = "log10(100)"
self.assertEqual(self.calculator.calculate(iExpr), eval("math.log10(100)"))
iExpr = "sin(pi/2)*111*6"
self.assertEqual(
self.calculator.calculate(iExpr), eval("math.sin(math.pi/2)*111*6")
)
iExpr = "2*sin(pi/2)"
self.assertEqual(
self.calculator.calculate(iExpr), eval("2*math.sin(math.pi/2)")
)
iExpr = "2^3^2^2"
self.assertEqual(self.calculator.calculate(iExpr), eval("2**3**2**2"))

def testAssociative(self):
iExpr = r"102%12%7"
self.assertEqual(self.calculator.calculate(iExpr), eval(r"102%12%7"))
iExpr = "100/4/3"
self.assertEqual(self.calculator.calculate(iExpr), eval("100/4/3"))
iExpr = "2^3^4"
self.assertEqual(self.calculator.calculate(iExpr), eval("2**3**4"))

def testComparison(self):
iExpr = r"1+2*3==1+2*3"
self.assertEqual(self.calculator.calculate(iExpr), eval(iExpr))
iExpr = r"e^5>=e^5+1"
self.assertEqual(
self.calculator.calculate(iExpr), eval("math.e**5>=math.e**5+1")
)
iExpr = r"1+2*4/3+1!=1+2*4/3+2"
self.assertEqual(self.calculator.calculate(iExpr), eval(iExpr))

def testCommon(self):
iExpr = r"(100)"
self.assertEqual(self.calculator.calculate(iExpr), eval(iExpr))
iExpr = r"666"
self.assertEqual(self.calculator.calculate(iExpr), eval(iExpr))
iExpr = r"-.1"
self.assertEqual(self.calculator.calculate(iExpr), eval(iExpr))
iExpr = r"1/3"
self.assertEqual(self.calculator.calculate(iExpr), eval(iExpr))
iExpr = r"1.0/3.0"
self.assertEqual(self.calculator.calculate(iExpr), eval(iExpr))
iExpr = r".1 * 2.0^56.0"
self.assertEqual(self.calculator.calculate(iExpr), eval(".1 * 2.0**56.0"))
iExpr = r"e^34"
self.assertEqual(self.calculator.calculate(iExpr), eval("math.e**34"))
iExpr = r"(2.0^(pi/pi+e/e+2.0^0.0))"
self.assertEqual(
self.calculator.calculate(iExpr),
eval("(2.0**(math.pi/math.pi+math.e/math.e+2.0**0.0))"),
)
iExpr = r"(2.0^(pi/pi+e/e+2.0^0.0))^(1.0/3.0)"
self.assertEqual(
self.calculator.calculate(iExpr),
eval("(2.0**(math.pi/math.pi+math.e/math.e+2.0**0.0))**(1.0/3.0)"),
)
iExpr = r"sin(pi/2^1) + log(1*4+2^2+1, 3^2)"
self.assertEqual(
self.calculator.calculate(iExpr),
eval("math.sin(math.pi/2**1) + math.log(1*4+2**2+1, 3**2)"),
)
iExpr = r"10*e^0*log10(.4 -5/ -0.1-10) - -abs(-53/10) + -5"
self.assertEqual(
self.calculator.calculate(iExpr),
eval("10*math.e**0*math.log10(.4 -5/ -0.1-10) - -abs(-53/10) + -5"),
)

iExpr = r"2.0^(2.0^2.0*2.0^2.0)"
self.assertEqual(
self.calculator.calculate(iExpr), eval("2.0**(2.0**2.0*2.0**2.0)")
)

"""def testError(self):
iExpr = r"((1+2)"
with self.assertRaises(converter.ConvertError):
self.calculator.calculate(iExpr)
iExpr = r""
with self.assertRaises(ValueError):
self.calculator.calculate(iExpr)

iExpr = r"((((("
with self.assertRaises(converter.ConvertError):
self.calculator.calculate(iExpr)
iExpr = r"1 + 1 2 3 4 5 6"
with self.assertRaises(ValueError):
self.calculator.calculate(iExpr)
iExpr = r"1 2"
with self.assertRaises(ValueError):
self.calculator.calculate(iExpr)
iExpr = r"5 > = 6"
with self.assertRaises(ValueError):
self.calculator.calculate(iExpr)
iExpr = r"5 / / 6"
with self.assertRaises(ValueError):
self.calculator.calculate(iExpr)
iExpr = r"6 < = 6"
with self.assertRaises(ValueError):
self.calculator.calculate(iExpr)
iExpr = r"6 * * 6"
with self.assertRaises(ValueError):
self.calculator.calculate(iExpr)
iExpr = r"log100(100)"
with self.assertRaises(ValueError):
self.calculator.calculate(iExpr)
"""


if __name__ == "__main__":

unittest.main()
27 changes: 27 additions & 0 deletions final_task/pycalc/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import argparse
from . import calculator


def create_parser():
parser = argparse.ArgumentParser(
description="Pure-python command-line calculator.")
parser.add_argument('EXPRESSION', help='string to evaluate',)
args = parser.parse_args()
iExpr = args.EXPRESSION
return iExpr


def main():
calc = calculator.Calculator()
try:
iExpr = create_parser()
if iExpr:
print(calc.calculate(iExpr))
else:
raise Exception('empty expression')
except Exception as error:
print("ERROR: " + str(error))


if __name__ == '__main__':
main()
175 changes: 175 additions & 0 deletions final_task/pycalc/converter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
from . import definitions
from collections import deque
import re


class ConvertError(Exception):
"""class for error"""

def __str__(self):
return "cannot convert expression to RPN due to mismatched parentheses"


class Converter:
def __init__(self):
self.func = re.compile(
r"("
+ "|".join(
[
re.escape(func)
for func in sorted(
definitions._functions, key=lambda func: len(func), reverse=True
)
]
)
+ ")"
)
self.num = re.compile(definitions._number)
self.op = re.compile(
r"|".join(
[
re.escape(op)
for op in sorted(
definitions._operators, key=lambda func: len(func), reverse=True
)
]
)
)

def convert(self, iExpr):
"""
function for converting parsed expression in RPN
"""
func = self.func
num = self.num
op = self.op
pos = 0
operatorStack = deque()
outputStack = deque()

while pos < len(iExpr):
if num.match(iExpr, pos):
numM = num.match(iExpr, pos)
if "." in numM.group():
outputStack.append(float(numM.group()))
else:
outputStack.append(int(numM.group()))
pos = numM.end()
elif iExpr[pos] == "(":
operatorStack.appendleft("(")
pos += 1
elif iExpr[pos] == ")":
if len(operatorStack) == 0:
raise ConvertError()
top = operatorStack.popleft()
while top != "(" and not len(operatorStack) == 0:
outputStack.append(top)
top = operatorStack.popleft()
if top != "(":
raise ConvertError()
pos += 1

elif func.match(iExpr, pos):
funcM = func.match(iExpr, pos)
flag = False
try:
a = iExpr[funcM.end() + 1] != "("
except IndexError:
flag = True

if not flag and iExpr[funcM.end()] != "(":
raise ValueError("unknown function")
if flag:
raise ValueError("no argument in function")
operatorStack.appendleft(funcM.group())
pos = funcM.end()

elif iExpr[pos] == ",":
if operatorStack:
top = operatorStack.popleft()
if (op.match(top) or func.match(top)) and "(" in operatorStack:
while operatorStack:
outputStack.append(top)
top = operatorStack.popleft()
if top == "(":
operatorStack.appendleft(top)
break
elif not op.match(top):
operatorStack.appendleft(top)
else:
raise ConvertError()
outputStack.append(",")
pos += 1

elif op.match(iExpr, pos):
match = op.match(iExpr, pos)
if len(operatorStack) != 0:
top = operatorStack.popleft()

cond = (
func.match(top)
or (
op.match(top)
and (
definitions._operators[top].priority
> definitions._operators[match.group()].priority
)
)
or (
op.match(top)
and (
definitions._operators[top].priority
== definitions._operators[match.group()].priority
and definitions._operators[top].LAssos
)
)
) and top != "("
if not cond:
operatorStack.appendleft(top)
operatorStack.appendleft(match.group())
pos = pos + len(match.group())

while cond:

outputStack.append(top)
if len(operatorStack) != 0:
top = operatorStack.popleft()
cond = (
func.match(top)
or (
op.match(top)
and (
definitions._operators[top].priority
> definitions._operators[match.group()].priority
)
)
or (
op.match(top)
and (
definitions._operators[top].priority
== definitions._operators[
match.group()
].priority
and definitions._operators[top].LAssos
)
)
) and top != "("

else:
break
if not cond:
operatorStack.appendleft(top)
break

else:

operatorStack.appendleft(match.group())
pos = pos + len(match.group())

while len(operatorStack) != 0:
outputStack.append(operatorStack.popleft())
# print(outputStack)
if "(" in outputStack or ")" in outputStack:
raise ConvertError()

return outputStack
Loading