From 5e6c89fecc31ade2ce2834b5eee467a987abcbb0 Mon Sep 17 00:00:00 2001 From: 1315groop Date: Sun, 2 Jun 2019 12:37:22 +0300 Subject: [PATCH 01/19] prefinal --- final_task/pycalc/calculator.py | 25 ++++ final_task/pycalc/calculator_test.py | 164 ++++++++++++++++++++ final_task/pycalc/cli.py | 23 +++ final_task/pycalc/converter.py | 194 ++++++++++++++++++++++++ final_task/pycalc/corrector.py | 214 +++++++++++++++++++++++++++ final_task/pycalc/corrector_test.py | 34 +++++ final_task/pycalc/definitions.py | 36 +++++ final_task/pycalc/evaluator.py | 62 ++++++++ final_task/pycalc/test.py | 13 ++ final_task/setup.py | 12 ++ 10 files changed, 777 insertions(+) create mode 100644 final_task/pycalc/calculator.py create mode 100644 final_task/pycalc/calculator_test.py create mode 100644 final_task/pycalc/cli.py create mode 100644 final_task/pycalc/converter.py create mode 100644 final_task/pycalc/corrector.py create mode 100644 final_task/pycalc/corrector_test.py create mode 100644 final_task/pycalc/definitions.py create mode 100644 final_task/pycalc/evaluator.py create mode 100644 final_task/pycalc/test.py diff --git a/final_task/pycalc/calculator.py b/final_task/pycalc/calculator.py new file mode 100644 index 00000000..83575dde --- /dev/null +++ b/final_task/pycalc/calculator.py @@ -0,0 +1,25 @@ +import corrector +import converter +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 + +#c = Calculator() +#s = "1-" +#print(c.calculate(s)) diff --git a/final_task/pycalc/calculator_test.py b/final_task/pycalc/calculator_test.py new file mode 100644 index 00000000..0b2d3ae1 --- /dev/null +++ b/final_task/pycalc/calculator_test.py @@ -0,0 +1,164 @@ +import unittest +from .calculator 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"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)" + #self.assertEqual( + # self.calculator.calculate(iExpr), + # eval( + # "math.sin(-math.cos(-math.sin(3.0)-math.cos(-math.sin(3.0*5.0)-math.sin(math.cos(math.log10(43.0))))+math.cos(math.sin(math.sin(34.0-2.0**2.0))))--math.cos(1.0)--math.cos(0.0)**3.0)" + # ), + #) + 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)") + ) + iExpr = r"sin(e^log(e^e^sin(23.0),45.0) + cos(3.0+log10(e^-e)))" + self.assertEqual( + self.calculator.calculate(iExpr), + eval( + "math.sin(math.e**math.log(math.e**math.e**math.sin(23.0),45.0) + math.cos(3.0+math.log10(math.e**-math.e)))" + ), + ) + + """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() diff --git a/final_task/pycalc/cli.py b/final_task/pycalc/cli.py new file mode 100644 index 00000000..b770e5d7 --- /dev/null +++ b/final_task/pycalc/cli.py @@ -0,0 +1,23 @@ +import argparse +import calculator + + +def create_parser(): + parser = argparse.ArgumentParser(description="Pure-python command-line calculator.") + parser.add_argument('EXPRESSION', help='iExpr string to evaluate',) + args = parser.parse_args() + iExpr = args.EXPRESSION + return iExpr + +def main(): + try: + iExpr = create_parser() + if iExpr: + calc = calculator.Calculator() + print(calc.calculate(iExpr)) + else: + raise Exception('empty expression') + except Exception as error: + print("ERROR: " + str(error)) +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/final_task/pycalc/converter.py b/final_task/pycalc/converter.py new file mode 100644 index 00000000..ee494bf4 --- /dev/null +++ b/final_task/pycalc/converter.py @@ -0,0 +1,194 @@ +import definitions +from collections import deque +import re + + +class ConvertError(Exception): + """class for error""" + + def __str__(self): + return "cannot convert input 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() + prev = None + posbrcl = 0 + + # br = 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() + #print("num") + #print(outputStack) + #print(operatorStack) + elif iExpr[pos] == "(": + operatorStack.appendleft("(") + pos += 1 + #print("lb") + #print(outputStack) + #print(operatorStack) + 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 + #print("lb") + #print(outputStack) + #print(operatorStack) + 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() + #print("func") + #print(outputStack) + #print(operatorStack) + elif iExpr[pos] == ",": + if operatorStack: + top = operatorStack.popleft() + if "(" in operatorStack: + while operatorStack: + outputStack.append(top) + top = operatorStack.popleft() + if top =="(": + operatorStack.appendleft(top) + break + else: + raise ConvertError() + outputStack.append(",") + pos += 1 + #print("comma") + #print(outputStack) + #print(operatorStack) + 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()) + #print("op") + #print(outputStack) + #print(operatorStack) + + while len(operatorStack) != 0: + outputStack.append(operatorStack.popleft()) + #print(outputStack) + if "(" in outputStack or ")" in outputStack: + raise ConvertError() + + return outputStack + + diff --git a/final_task/pycalc/corrector.py b/final_task/pycalc/corrector.py new file mode 100644 index 00000000..f9c96704 --- /dev/null +++ b/final_task/pycalc/corrector.py @@ -0,0 +1,214 @@ +import re +import math +import definitions + + + + + +class Corrector: + + """base class for correct input string for parsing """ + + def __init__(self): + """declare row(in regex) and compiled part of expression""" + self._expr = "" + self._funcPos = [] + + self.constantR = ( + r"(" + + "|".join([re.escape(const) for const in definitions._constants]) + + ")" + ) + self.functionR = ( + r"(" + + "|".join( + [ + re.escape(func) + for func in sorted( + definitions._functions, key=lambda func: len(func), reverse=True + ) + ] + ) + + ")" + ) + self.operatorR = ( + r"(" + + r"|".join( + [re.escape(op) + for op in definitions._operators if op not in ["+", "-"]] + ) + + r"|=" + r")" + ) + + self.numberR = definitions._number + self.constantC = re.compile(self.constantR) + self.functionC = re.compile(self.functionR) + self.numberC = re.compile(self.numberR) + self.operatorC = re.compile(self.operatorR) + + def findFuncPos(self): + """find functions positions in expression """ + self._funcPos = [] + for result in re.finditer(self.functionC, self._expr): + for pos in range(result.start(), result.end()): + self._funcPos.append(pos) + + def constInterpolation(self): + self.findFuncPos() + strin = self._expr + for result in re.finditer(self.constantC, self._expr): + if result.start() not in self._funcPos: + strin = re.sub( + result.group(), + str(definitions._constants.get(result.group()).value), + strin, + ) + self._expr = strin + + def plusMinusReduce(self): + strin = self._expr + while ( + re.search(re.compile(r"\-\-"), strin) + or re.search(re.compile(r"\+\+"), strin) + or re.search(re.compile(r"\+\-"), strin) + or re.search(re.compile(r"\-\+"), strin) + ): + strin = re.sub(r"\-\-", "+", strin) + strin = re.sub(r"\+\+", "+", strin) + strin = re.sub(r"\+\-", "-", strin) + strin = re.sub(r"\-\+", "-", strin) + self._expr = strin + + def spaceReduce(self): + spaceC = re.compile(self.numberR + r"[\s]+" + self.numberR) + spaceOC = re.compile(self.operatorR + r"[\s]+" + self.operatorR) + strin = self._expr + if re.search(spaceC, strin) or re.search(spaceOC, strin): + raise ValueError("space beatwin operators") + else: + strin = re.sub(r"[\s]+", "", strin) + self._expr = strin + + def unaryPlusReduce(self): + strin = self._expr + operatorMap = {(op + "+"): op for op in definitions._operators} + operatorMapC = re.compile( + r"(" + "|".join([re.escape(op) for op in operatorMap]) + ")" + ) + # print(operatorMap) + if re.search(re.compile(r"^\+"), strin): + strin = re.sub(r"^\+", r"", strin) + if re.search(re.compile(r"\(\+"), strin): + strin = re.sub(r"\(\+", "(", strin) + if re.search(re.compile(r"\,\+"), strin): + strin = re.sub(r"\,\+", ",", strin) + for result in re.finditer(operatorMapC, strin): + strin = re.sub( + re.compile(re.escape(result.group())), + operatorMap.get(result.group()), + strin, + ) + self._expr = strin + + def unaryMinusReduce(self): + minusPredecessors = [] + + minusPredecessors.extend( + [ + re.escape(op) + for op in sorted( + definitions._operators, key=lambda op: len(op), reverse=True + ) + ] + ) + + minusPredecessors.append(r"\A") + + minusPredecessors.append(re.escape("(")) + + unaryMinusBasicR = ( + "(" + "|".join(minusPredecessors) + ")" + + re.escape("-") + self.numberR + ) + unaryMinusBasicM = re.compile(unaryMinusBasicR) + minusPredecessorsM = re.compile("|".join(minusPredecessors)) + + offset = 0 + for result in re.finditer(unaryMinusBasicM, self._expr): + curUnaryMinusPos = 0 + if result.start() > 0: + curUnaryMinusPos = minusPredecessorsM.match( + self._expr, result.start() + offset + ).end() + self._expr = ( + self._expr[:curUnaryMinusPos] + + "(0" + self._expr[curUnaryMinusPos:] + ) + offset += 2 + self._expr = ( + self._expr[: result.end() + offset] + + ")" + + self._expr[result.end() + offset:] + ) + offset += 1 + + minusSuccessors = [] + minusSuccessors.extend( + [ + re.escape(func + "(") + for func in sorted( + definitions._functions, key=lambda func: len(func), reverse=True + ) + ] + ) + minusSuccessors.append(re.escape("(")) + + unaryMinusBracketR = ( + "(" + + "|".join(minusPredecessors) + + ")" + + re.escape("-") + + "(" + + "|".join(minusSuccessors) + + ")" + ) + unaryMinusBracketM = re.compile(unaryMinusBracketR) + + result = unaryMinusBracketM.search(self._expr) + while result: + curUnaryMinusPos = 0 + if result.start() > 0: + curUnaryMinusPos = minusPredecessorsM.match( + self._expr, result.start() + ).end() + self._expr = ( + self._expr[:curUnaryMinusPos] + + "(0" + self._expr[curUnaryMinusPos:] + ) + offset = 2 + + lastBracketPos = result.end() + offset + bracketCounter = 1 + while bracketCounter > 0: + if self._expr[lastBracketPos] == "(": + bracketCounter += 1 + elif self._expr[lastBracketPos] == ")": + bracketCounter -= 1 + lastBracketPos += 1 + self._expr = self._expr[:lastBracketPos] + \ + ")" + self._expr[lastBracketPos:] + result = unaryMinusBracketM.search(self._expr) + + def correct(self, iExpr): + self._expr = iExpr + self.spaceReduce() + self.constInterpolation() + self.plusMinusReduce() + self.unaryPlusReduce() + self.unaryMinusReduce() + + + + return self._expr diff --git a/final_task/pycalc/corrector_test.py b/final_task/pycalc/corrector_test.py new file mode 100644 index 00000000..91b00ec3 --- /dev/null +++ b/final_task/pycalc/corrector_test.py @@ -0,0 +1,34 @@ +import unittest +import corrector + +class TestCorrector(unittest.TestCase): + corrector = corrector.Corrector() + + + def testPlusMinusReduce(self): + self.corrector._expr = "---5+---2+++8+--4++5*--6+-+-+15-23--(--5)" + self.corrector._plusMinusReduce() + self.assertEqual(self.corrector._expr, \ + "-5-2+8+4+5*+6+15-23+(+5)") + + def testUnaryPlusReduce(self): + self.corrector._expr = "+8*+3+(+sin())" + self.corrector._unaryPlusReduce() + self.assertEqual(self.corrector._expr, \ + "8*3+(sin())") + + def testUnaryMinusReduce(self): + self.corrector._expr = "-1*-2-(-3)+3^-2+1" + self.corrector._unaryMinusReduce() + self.assertEqual(self.corrector._expr, \ + "(0-1)*(0-2)-((0-3))+3^(0-2)+1") + + self.corrector._expr = "-sin(-(1*-(2*3))-(-3))+3^(-2+1)" + self.corrector._unaryMinusReduce() + self.assertEqual(self.corrector._expr, \ + "(0-sin((0-(1*(0-(2*3))))-((0-3))))+3^((0-2)+1)") + + + +if __name__ == '__main__': + unittest.main() diff --git a/final_task/pycalc/definitions.py b/final_task/pycalc/definitions.py new file mode 100644 index 00000000..7cf9cd2c --- /dev/null +++ b/final_task/pycalc/definitions.py @@ -0,0 +1,36 @@ +import math +from collections import namedtuple + +""" +Definitions for all type of input +""" +Operator = namedtuple("Operator", ["LAssos", "priority", "func"]) +Constant = namedtuple("Constant", ["value"]) +_functions = { + attr: getattr(math, attr) for attr in dir(math) if callable(getattr(math, attr)) +} + +_functions["abs"] = abs +_functions["round"] = round +_number = r"\d*[\.]?\d+" +_operators = { + "^": Operator(LAssos=False, priority=4, func=lambda x,y: x ** y), + "*": Operator(LAssos=True, priority=3, func=lambda x,y: x * y), + "/": Operator(LAssos=True, priority=3, func=lambda x,y: x / y), + "//": Operator(LAssos=True, priority=3, func=lambda x,y: x // y), + "%": Operator(LAssos=True, priority=3, func=lambda x,y: x % y), + "+": Operator(LAssos=True, priority=2, func=lambda x,y: x + y), + "-": Operator(LAssos=True, priority=2, func=lambda x,y: x - y), + "==": Operator(LAssos=True, priority=1, func=lambda x,y: x == y), + "<=": Operator(LAssos=True, priority=1, func=lambda x,y: x <= y), + ">=": Operator(LAssos=True, priority=1, func=lambda x,y: x >= y), + "<": Operator(LAssos=True, priority=1, func=lambda x,y: x < y), + ">": Operator(LAssos=True, priority=1, func=lambda x,y: x > y), + "!=": Operator(LAssos=True, priority=1, func=lambda x,y: x != y), +} + +_constants = { + "e": Constant(value=math.e), + "pi": Constant(value=math.pi), + "tau": Constant(value=math.tau), +} diff --git a/final_task/pycalc/evaluator.py b/final_task/pycalc/evaluator.py new file mode 100644 index 00000000..51f727c7 --- /dev/null +++ b/final_task/pycalc/evaluator.py @@ -0,0 +1,62 @@ +import definitions +import re +from collections import deque + +class Evaluator(object): + 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 evaluate(self, iExpr): + func = self.func + num = self.num + op = self.op + outputStack = [] + args = [] + for token in iExpr: + if num.match(str(token)): + outputStack.append(token) + #print("num") + #print(outputStack) + elif op.match(token): + operand2 = outputStack.pop() + operand1 = outputStack.pop() + outputStack.append(definitions._operators[token].func(operand1,operand2)) + #print("op") + #print(outputStack) + elif token ==",": + outputStack.append(",") + elif func.match(token): + + if "," in outputStack and num.match(str(outputStack[-3])) and outputStack[-2] == "," and num.match(str(outputStack[-1])): + operand2 = outputStack.pop() + comma = outputStack.pop() + operand1 = outputStack.pop() + outputStack.append(definitions._functions[token](operand1,operand2)) + else: + operand = outputStack.pop() + outputStack.append(definitions._functions[token](operand)) + if len(outputStack)==0: + raise ValueError("empty expression") + + return outputStack[0] diff --git a/final_task/pycalc/test.py b/final_task/pycalc/test.py new file mode 100644 index 00000000..4caa8b45 --- /dev/null +++ b/final_task/pycalc/test.py @@ -0,0 +1,13 @@ +import corrector +import parser +import math +import re +import definitions +import inspect +import converter +import calculator + +print("") +c = calculator.Calculator() +s = "sin(30)" +print(c.calculate(s)) \ No newline at end of file diff --git a/final_task/setup.py b/final_task/setup.py index e69de29b..6bdf97c1 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -0,0 +1,12 @@ +from setuptools import setup, find_packages + +setup( + name='pycalc', + version='1.0', + author='Pavel Karshakevich', + author_email='pashakorsh@gmail.com', + packages=find_packages(), + entry_points={'console_scripts': ['pycalc = pycalc.cli:main']}, + description='Pure-python command-line calculator.', + py_modules=['pycalc'] +) \ No newline at end of file From 043b0981a35ba4f39788977a2393650aea6aeb4a Mon Sep 17 00:00:00 2001 From: 1315groop Date: Sun, 2 Jun 2019 13:05:34 +0300 Subject: [PATCH 02/19] style change with black --- final_task/pycalc/calculator.py | 9 +------ final_task/pycalc/calculator_test.py | 9 +------ final_task/pycalc/cli.py | 8 ++++-- final_task/pycalc/converter.py | 40 ++++++++-------------------- final_task/pycalc/corrector.py | 6 ----- final_task/pycalc/corrector_test.py | 19 +++++++------ final_task/pycalc/definitions.py | 26 +++++++++--------- final_task/pycalc/evaluator.py | 27 ++++++++++++------- final_task/setup.py | 2 +- 9 files changed, 59 insertions(+), 87 deletions(-) diff --git a/final_task/pycalc/calculator.py b/final_task/pycalc/calculator.py index 83575dde..3a73137a 100644 --- a/final_task/pycalc/calculator.py +++ b/final_task/pycalc/calculator.py @@ -9,17 +9,10 @@ def __init__(self, **kwargs): self.corrector = corrector.Corrector() self.converter = converter.Converter() self.evaluator = evaluator.Evaluator() - def calculate(self, iExpr): - "function for final calculate" - + """function for final calculate""" correctedExpr = self.corrector.correct(iExpr) convertedExpr = self.converter.convert(correctedExpr) evaluatedExpr = self.evaluator.evaluate(convertedExpr) - return evaluatedExpr - -#c = Calculator() -#s = "1-" -#print(c.calculate(s)) diff --git a/final_task/pycalc/calculator_test.py b/final_task/pycalc/calculator_test.py index 0b2d3ae1..46398f4d 100644 --- a/final_task/pycalc/calculator_test.py +++ b/final_task/pycalc/calculator_test.py @@ -3,7 +3,6 @@ import math - class TestCalculate(unittest.TestCase): def setUp(self): self.calculator = calculator.Calculator() @@ -105,13 +104,7 @@ def testCommon(self): self.calculator.calculate(iExpr), eval("10*math.e**0*math.log10(.4 -5/ -0.1-10) - -abs(-53/10) + -5"), ) - #iExpr = r"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)" - #self.assertEqual( - # self.calculator.calculate(iExpr), - # eval( - # "math.sin(-math.cos(-math.sin(3.0)-math.cos(-math.sin(3.0*5.0)-math.sin(math.cos(math.log10(43.0))))+math.cos(math.sin(math.sin(34.0-2.0**2.0))))--math.cos(1.0)--math.cos(0.0)**3.0)" - # ), - #) + 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)") diff --git a/final_task/pycalc/cli.py b/final_task/pycalc/cli.py index b770e5d7..4cdf71df 100644 --- a/final_task/pycalc/cli.py +++ b/final_task/pycalc/cli.py @@ -3,12 +3,14 @@ def create_parser(): - parser = argparse.ArgumentParser(description="Pure-python command-line calculator.") + parser = argparse.ArgumentParser( + description="Pure-python command-line calculator.") parser.add_argument('EXPRESSION', help='iExpr string to evaluate',) args = parser.parse_args() iExpr = args.EXPRESSION return iExpr + def main(): try: iExpr = create_parser() @@ -19,5 +21,7 @@ def main(): raise Exception('empty expression') except Exception as error: print("ERROR: " + str(error)) + + if __name__ == '__main__': - main() \ No newline at end of file + main() diff --git a/final_task/pycalc/converter.py b/final_task/pycalc/converter.py index ee494bf4..7b763a3d 100644 --- a/final_task/pycalc/converter.py +++ b/final_task/pycalc/converter.py @@ -7,7 +7,7 @@ class ConvertError(Exception): """class for error""" def __str__(self): - return "cannot convert input expression to RPN due to mismatched parentheses" + return "cannot convert expression to RPN due to mismatched parentheses" class Converter: @@ -35,6 +35,7 @@ def __init__(self): ] ) ) + def convert(self, iExpr): """ function for converting parsed expression in RPN @@ -42,14 +43,13 @@ def convert(self, iExpr): func = self.func num = self.num op = self.op - + pos = 0 operatorStack = deque() outputStack = deque() prev = None posbrcl = 0 - # br = deque() while pos < len(iExpr): if num.match(iExpr, pos): numM = num.match(iExpr, pos) @@ -58,15 +58,9 @@ def convert(self, iExpr): else: outputStack.append(int(numM.group())) pos = numM.end() - #print("num") - #print(outputStack) - #print(operatorStack) elif iExpr[pos] == "(": operatorStack.appendleft("(") pos += 1 - #print("lb") - #print(outputStack) - #print(operatorStack) elif iExpr[pos] == ")": if len(operatorStack) == 0: raise ConvertError() @@ -76,28 +70,23 @@ def convert(self, iExpr): top = operatorStack.popleft() if top != "(": raise ConvertError() - pos += 1 - #print("lb") - #print(outputStack) - #print(operatorStack) + elif func.match(iExpr, pos): funcM = func.match(iExpr, pos) flag = False try: - a = iExpr[funcM.end()+1]!="(" + 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() - #print("func") - #print(outputStack) - #print(operatorStack) + elif iExpr[pos] == ",": if operatorStack: top = operatorStack.popleft() @@ -105,16 +94,14 @@ def convert(self, iExpr): while operatorStack: outputStack.append(top) top = operatorStack.popleft() - if top =="(": + if top == "(": operatorStack.appendleft(top) break else: raise ConvertError() outputStack.append(",") pos += 1 - #print("comma") - #print(outputStack) - #print(operatorStack) + elif op.match(iExpr, pos): match = op.match(iExpr, pos) if len(operatorStack) != 0: @@ -179,16 +166,11 @@ def convert(self, iExpr): operatorStack.appendleft(match.group()) pos = pos + len(match.group()) - #print("op") - #print(outputStack) - #print(operatorStack) while len(operatorStack) != 0: outputStack.append(operatorStack.popleft()) - #print(outputStack) - if "(" in outputStack or ")" in outputStack: + # print(outputStack) + if "(" in outputStack or ")" in outputStack: raise ConvertError() return outputStack - - diff --git a/final_task/pycalc/corrector.py b/final_task/pycalc/corrector.py index f9c96704..8a87e5b5 100644 --- a/final_task/pycalc/corrector.py +++ b/final_task/pycalc/corrector.py @@ -3,9 +3,6 @@ import definitions - - - class Corrector: """base class for correct input string for parsing """ @@ -208,7 +205,4 @@ def correct(self, iExpr): self.plusMinusReduce() self.unaryPlusReduce() self.unaryMinusReduce() - - - return self._expr diff --git a/final_task/pycalc/corrector_test.py b/final_task/pycalc/corrector_test.py index 91b00ec3..696a4272 100644 --- a/final_task/pycalc/corrector_test.py +++ b/final_task/pycalc/corrector_test.py @@ -1,34 +1,33 @@ import unittest import corrector + class TestCorrector(unittest.TestCase): corrector = corrector.Corrector() - def testPlusMinusReduce(self): self.corrector._expr = "---5+---2+++8+--4++5*--6+-+-+15-23--(--5)" - self.corrector._plusMinusReduce() - self.assertEqual(self.corrector._expr, \ + self.corrector.plusMinusReduce() + self.assertEqual(self.corrector._expr, "-5-2+8+4+5*+6+15-23+(+5)") def testUnaryPlusReduce(self): self.corrector._expr = "+8*+3+(+sin())" - self.corrector._unaryPlusReduce() - self.assertEqual(self.corrector._expr, \ + self.corrector.unaryPlusReduce() + self.assertEqual(self.corrector._expr, "8*3+(sin())") def testUnaryMinusReduce(self): self.corrector._expr = "-1*-2-(-3)+3^-2+1" - self.corrector._unaryMinusReduce() - self.assertEqual(self.corrector._expr, \ + self.corrector.unaryMinusReduce() + self.assertEqual(self.corrector._expr, "(0-1)*(0-2)-((0-3))+3^(0-2)+1") self.corrector._expr = "-sin(-(1*-(2*3))-(-3))+3^(-2+1)" - self.corrector._unaryMinusReduce() - self.assertEqual(self.corrector._expr, \ + self.corrector.unaryMinusReduce() + self.assertEqual(self.corrector._expr, "(0-sin((0-(1*(0-(2*3))))-((0-3))))+3^((0-2)+1)") - if __name__ == '__main__': unittest.main() diff --git a/final_task/pycalc/definitions.py b/final_task/pycalc/definitions.py index 7cf9cd2c..320a6ad4 100644 --- a/final_task/pycalc/definitions.py +++ b/final_task/pycalc/definitions.py @@ -14,19 +14,19 @@ _functions["round"] = round _number = r"\d*[\.]?\d+" _operators = { - "^": Operator(LAssos=False, priority=4, func=lambda x,y: x ** y), - "*": Operator(LAssos=True, priority=3, func=lambda x,y: x * y), - "/": Operator(LAssos=True, priority=3, func=lambda x,y: x / y), - "//": Operator(LAssos=True, priority=3, func=lambda x,y: x // y), - "%": Operator(LAssos=True, priority=3, func=lambda x,y: x % y), - "+": Operator(LAssos=True, priority=2, func=lambda x,y: x + y), - "-": Operator(LAssos=True, priority=2, func=lambda x,y: x - y), - "==": Operator(LAssos=True, priority=1, func=lambda x,y: x == y), - "<=": Operator(LAssos=True, priority=1, func=lambda x,y: x <= y), - ">=": Operator(LAssos=True, priority=1, func=lambda x,y: x >= y), - "<": Operator(LAssos=True, priority=1, func=lambda x,y: x < y), - ">": Operator(LAssos=True, priority=1, func=lambda x,y: x > y), - "!=": Operator(LAssos=True, priority=1, func=lambda x,y: x != y), + "^": Operator(LAssos=False, priority=4, func=lambda x, y: x ** y), + "*": Operator(LAssos=True, priority=3, func=lambda x, y: x * y), + "/": Operator(LAssos=True, priority=3, func=lambda x, y: x / y), + "//": Operator(LAssos=True, priority=3, func=lambda x, y: x // y), + "%": Operator(LAssos=True, priority=3, func=lambda x, y: x % y), + "+": Operator(LAssos=True, priority=2, func=lambda x, y: x + y), + "-": Operator(LAssos=True, priority=2, func=lambda x, y: x - y), + "==": Operator(LAssos=True, priority=1, func=lambda x, y: x == y), + "<=": Operator(LAssos=True, priority=1, func=lambda x, y: x <= y), + ">=": Operator(LAssos=True, priority=1, func=lambda x, y: x >= y), + "<": Operator(LAssos=True, priority=1, func=lambda x, y: x < y), + ">": Operator(LAssos=True, priority=1, func=lambda x, y: x > y), + "!=": Operator(LAssos=True, priority=1, func=lambda x, y: x != y), } _constants = { diff --git a/final_task/pycalc/evaluator.py b/final_task/pycalc/evaluator.py index 51f727c7..315762c3 100644 --- a/final_task/pycalc/evaluator.py +++ b/final_task/pycalc/evaluator.py @@ -2,6 +2,7 @@ import re from collections import deque + class Evaluator(object): def __init__(self): self.func = re.compile( @@ -27,6 +28,7 @@ def __init__(self): ] ) ) + def evaluate(self, iExpr): func = self.func num = self.num @@ -36,27 +38,32 @@ def evaluate(self, iExpr): for token in iExpr: if num.match(str(token)): outputStack.append(token) - #print("num") - #print(outputStack) elif op.match(token): operand2 = outputStack.pop() operand1 = outputStack.pop() - outputStack.append(definitions._operators[token].func(operand1,operand2)) - #print("op") - #print(outputStack) - elif token ==",": + outputStack.append( + definitions._operators[token].func(operand1, operand2) + ) + elif token == ",": outputStack.append(",") elif func.match(token): - if "," in outputStack and num.match(str(outputStack[-3])) and outputStack[-2] == "," and num.match(str(outputStack[-1])): + if ( + "," in outputStack + and num.match(str(outputStack[-3])) + and outputStack[-2] == "," + and num.match(str(outputStack[-1])) + ): operand2 = outputStack.pop() comma = outputStack.pop() operand1 = outputStack.pop() - outputStack.append(definitions._functions[token](operand1,operand2)) + outputStack.append( + definitions._functions[token](operand1, operand2) + ) else: operand = outputStack.pop() outputStack.append(definitions._functions[token](operand)) - if len(outputStack)==0: + if len(outputStack) == 0: raise ValueError("empty expression") - + return outputStack[0] diff --git a/final_task/setup.py b/final_task/setup.py index 6bdf97c1..bd0a214e 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -9,4 +9,4 @@ entry_points={'console_scripts': ['pycalc = pycalc.cli:main']}, description='Pure-python command-line calculator.', py_modules=['pycalc'] -) \ No newline at end of file +) From 16d5880758ae077cb4126b7021938626eb628fd6 Mon Sep 17 00:00:00 2001 From: 1315groop Date: Sun, 2 Jun 2019 13:16:31 +0300 Subject: [PATCH 03/19] test setup --- final_task/pycalc/{cli.py => pycalc.py} | 4 ++-- final_task/setup.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename final_task/pycalc/{cli.py => pycalc.py} (81%) diff --git a/final_task/pycalc/cli.py b/final_task/pycalc/pycalc.py similarity index 81% rename from final_task/pycalc/cli.py rename to final_task/pycalc/pycalc.py index 4cdf71df..73e52c3c 100644 --- a/final_task/pycalc/cli.py +++ b/final_task/pycalc/pycalc.py @@ -5,17 +5,17 @@ def create_parser(): parser = argparse.ArgumentParser( description="Pure-python command-line calculator.") - parser.add_argument('EXPRESSION', help='iExpr string to evaluate',) + 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: - calc = calculator.Calculator() print(calc.calculate(iExpr)) else: raise Exception('empty expression') diff --git a/final_task/setup.py b/final_task/setup.py index bd0a214e..6715d6a0 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -6,7 +6,7 @@ author='Pavel Karshakevich', author_email='pashakorsh@gmail.com', packages=find_packages(), - entry_points={'console_scripts': ['pycalc = pycalc.cli:main']}, + entry_points={'console_scripts': ['pycalc = pycalc.pycalc:main']}, description='Pure-python command-line calculator.', py_modules=['pycalc'] ) From b02728926f99b8abd482db691ad9c721f34ab5ab Mon Sep 17 00:00:00 2001 From: 1315groop Date: Sun, 2 Jun 2019 13:21:38 +0300 Subject: [PATCH 04/19] setup fix --- __init__.py | 0 final_task/pycalc/__init__.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 __init__.py create mode 100644 final_task/pycalc/__init__.py diff --git a/__init__.py b/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/final_task/pycalc/__init__.py b/final_task/pycalc/__init__.py new file mode 100644 index 00000000..e69de29b From 4fb54e7b6a92c9854c3953d293c89afaccbfdc75 Mon Sep 17 00:00:00 2001 From: 1315groop Date: Sun, 2 Jun 2019 13:37:59 +0300 Subject: [PATCH 05/19] unresolved import test --- final_task/pycalc/{pycalc.py => cli.py} | 0 final_task/setup.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename final_task/pycalc/{pycalc.py => cli.py} (100%) diff --git a/final_task/pycalc/pycalc.py b/final_task/pycalc/cli.py similarity index 100% rename from final_task/pycalc/pycalc.py rename to final_task/pycalc/cli.py diff --git a/final_task/setup.py b/final_task/setup.py index 6715d6a0..bd0a214e 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -6,7 +6,7 @@ author='Pavel Karshakevich', author_email='pashakorsh@gmail.com', packages=find_packages(), - entry_points={'console_scripts': ['pycalc = pycalc.pycalc:main']}, + entry_points={'console_scripts': ['pycalc = pycalc.cli:main']}, description='Pure-python command-line calculator.', py_modules=['pycalc'] ) From 266ec2e5c46ff97161fc85f1d2b1d4db203b2098 Mon Sep 17 00:00:00 2001 From: 1315groop Date: Sun, 2 Jun 2019 13:46:08 +0300 Subject: [PATCH 06/19] import not working --- __init__.py | 0 final_task/pycalc/__init__.py | 1 + final_task/pycalc/calculator_test.py | 2 +- 3 files changed, 2 insertions(+), 1 deletion(-) delete mode 100644 __init__.py diff --git a/__init__.py b/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/final_task/pycalc/__init__.py b/final_task/pycalc/__init__.py index e69de29b..871f6fd8 100644 --- a/final_task/pycalc/__init__.py +++ b/final_task/pycalc/__init__.py @@ -0,0 +1 @@ +from .calculator import Calculator diff --git a/final_task/pycalc/calculator_test.py b/final_task/pycalc/calculator_test.py index 46398f4d..310a809a 100644 --- a/final_task/pycalc/calculator_test.py +++ b/final_task/pycalc/calculator_test.py @@ -1,5 +1,5 @@ import unittest -from .calculator import Calculator +import calculator import math From a4c41033220c86710da9e3dc69eeeebb7adb78c0 Mon Sep 17 00:00:00 2001 From: 1315groop Date: Sun, 2 Jun 2019 14:00:02 +0300 Subject: [PATCH 07/19] try to fix import --- final_task/pycalc/__init__.py | 2 +- final_task/pycalc/calculator.py | 6 +++--- final_task/pycalc/cli.py | 2 +- final_task/pycalc/converter.py | 2 +- final_task/pycalc/corrector.py | 2 +- final_task/pycalc/corrector_test.py | 2 +- final_task/pycalc/evaluator.py | 2 +- final_task/pycalc/test.py | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/final_task/pycalc/__init__.py b/final_task/pycalc/__init__.py index 871f6fd8..8b137891 100644 --- a/final_task/pycalc/__init__.py +++ b/final_task/pycalc/__init__.py @@ -1 +1 @@ -from .calculator import Calculator + diff --git a/final_task/pycalc/calculator.py b/final_task/pycalc/calculator.py index 3a73137a..bcb52472 100644 --- a/final_task/pycalc/calculator.py +++ b/final_task/pycalc/calculator.py @@ -1,6 +1,6 @@ -import corrector -import converter -import evaluator +from . import corrector +from . import converter +from . import evaluator class Calculator(object): diff --git a/final_task/pycalc/cli.py b/final_task/pycalc/cli.py index 73e52c3c..c74fa7f3 100644 --- a/final_task/pycalc/cli.py +++ b/final_task/pycalc/cli.py @@ -1,5 +1,5 @@ import argparse -import calculator +from . import calculator def create_parser(): diff --git a/final_task/pycalc/converter.py b/final_task/pycalc/converter.py index 7b763a3d..1aafe789 100644 --- a/final_task/pycalc/converter.py +++ b/final_task/pycalc/converter.py @@ -1,4 +1,4 @@ -import definitions +from . import definitions from collections import deque import re diff --git a/final_task/pycalc/corrector.py b/final_task/pycalc/corrector.py index 8a87e5b5..35d43cb1 100644 --- a/final_task/pycalc/corrector.py +++ b/final_task/pycalc/corrector.py @@ -1,6 +1,6 @@ import re import math -import definitions +from . import definitions class Corrector: diff --git a/final_task/pycalc/corrector_test.py b/final_task/pycalc/corrector_test.py index 696a4272..b15791b4 100644 --- a/final_task/pycalc/corrector_test.py +++ b/final_task/pycalc/corrector_test.py @@ -1,5 +1,5 @@ import unittest -import corrector +from . import corrector class TestCorrector(unittest.TestCase): diff --git a/final_task/pycalc/evaluator.py b/final_task/pycalc/evaluator.py index 315762c3..d6f50e9a 100644 --- a/final_task/pycalc/evaluator.py +++ b/final_task/pycalc/evaluator.py @@ -1,4 +1,4 @@ -import definitions +from . import definitions import re from collections import deque diff --git a/final_task/pycalc/test.py b/final_task/pycalc/test.py index 4caa8b45..6fd36489 100644 --- a/final_task/pycalc/test.py +++ b/final_task/pycalc/test.py @@ -10,4 +10,4 @@ print("") c = calculator.Calculator() s = "sin(30)" -print(c.calculate(s)) \ No newline at end of file +print(c.calculate(s)) From b2dba85714d8570e68e7c754b596990bd13cf999 Mon Sep 17 00:00:00 2001 From: 1315groop Date: Sun, 2 Jun 2019 14:02:37 +0300 Subject: [PATCH 08/19] import fix --- final_task/pycalc/calculator_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/final_task/pycalc/calculator_test.py b/final_task/pycalc/calculator_test.py index 310a809a..4c57e192 100644 --- a/final_task/pycalc/calculator_test.py +++ b/final_task/pycalc/calculator_test.py @@ -1,5 +1,5 @@ import unittest -import calculator +from . import calculator import math From 598299d0113bc24a9b0a224cc8070882cf27c7a6 Mon Sep 17 00:00:00 2001 From: 1315groop Date: Sun, 2 Jun 2019 14:04:53 +0300 Subject: [PATCH 09/19] fix import --- final_task/pycalc/test.py | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 final_task/pycalc/test.py diff --git a/final_task/pycalc/test.py b/final_task/pycalc/test.py deleted file mode 100644 index 6fd36489..00000000 --- a/final_task/pycalc/test.py +++ /dev/null @@ -1,13 +0,0 @@ -import corrector -import parser -import math -import re -import definitions -import inspect -import converter -import calculator - -print("") -c = calculator.Calculator() -s = "sin(30)" -print(c.calculate(s)) From b37689e33a50bf3ef9adc6d8ec82533563ae308d Mon Sep 17 00:00:00 2001 From: 1315groop Date: Sun, 2 Jun 2019 15:00:23 +0300 Subject: [PATCH 10/19] try to fix conversion --- final_task/pycalc/__init__.py | 1 - final_task/pycalc/converter.py | 169 +++++++++++---------------------- 2 files changed, 53 insertions(+), 117 deletions(-) diff --git a/final_task/pycalc/__init__.py b/final_task/pycalc/__init__.py index 8b137891..e69de29b 100644 --- a/final_task/pycalc/__init__.py +++ b/final_task/pycalc/__init__.py @@ -1 +0,0 @@ - diff --git a/final_task/pycalc/converter.py b/final_task/pycalc/converter.py index 1aafe789..881016c7 100644 --- a/final_task/pycalc/converter.py +++ b/final_task/pycalc/converter.py @@ -1,4 +1,4 @@ -from . import definitions +import definitions from collections import deque import re @@ -7,49 +7,30 @@ class ConvertError(Exception): """class for error""" def __str__(self): - return "cannot convert expression to RPN due to mismatched parentheses" + return 'cannot convert input 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 - ) - ] - ) - ) - +class Converter(): def convert(self, iExpr): - """ + + ''' function for converting parsed expression in RPN - """ - func = self.func - num = self.num - op = self.op - + ''' + + func = re.compile(r'(' + '|'.join( + [re.escape(func) for func in sorted( + definitions._functions, key=lambda func: len(func), reverse=True)] + ) + ')') + num = re.compile(definitions._number) + op = re.compile(r'|'.join( + [re.escape(op) for op in sorted(definitions._operators, key=lambda func: len(func), reverse=True)])) pos = 0 operatorStack = deque() outputStack = deque() prev = None posbrcl = 0 - + + #br = deque() while pos < len(iExpr): if num.match(iExpr, pos): numM = num.match(iExpr, pos) @@ -58,119 +39,75 @@ def convert(self, iExpr): else: outputStack.append(int(numM.group())) pos = numM.end() - elif iExpr[pos] == "(": - operatorStack.appendleft("(") + elif (iExpr[pos] == '('): + operatorStack.appendleft('(') pos += 1 - elif iExpr[pos] == ")": + 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 != "(": + if top != '(': raise ConvertError() - pos += 1 + 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()] != "(": + if 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 "(" in operatorStack: - while operatorStack: - outputStack.append(top) - top = operatorStack.popleft() - if top == "(": - operatorStack.appendleft(top) - break - else: - raise ConvertError() - outputStack.append(",") - pos += 1 - + elif iExpr[pos] ==',': + pos+=1 elif op.match(iExpr, pos): match = op.match(iExpr, pos) - if len(operatorStack) != 0: + 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 != "(" + 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()) + pos=pos+len(match.group()) + + - while cond: + + + while cond: + + outputStack.append(top) - if len(operatorStack) != 0: + 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 != "(" + 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: + 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 + \ No newline at end of file From b1d98076b34d936959f892a285851d67d69c0cb4 Mon Sep 17 00:00:00 2001 From: 1315groop Date: Sun, 2 Jun 2019 15:03:53 +0300 Subject: [PATCH 11/19] fix --- final_task/pycalc/converter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/final_task/pycalc/converter.py b/final_task/pycalc/converter.py index 881016c7..9be9d0b2 100644 --- a/final_task/pycalc/converter.py +++ b/final_task/pycalc/converter.py @@ -1,4 +1,4 @@ -import definitions +from . import definitions from collections import deque import re From 699848c27f4376336d6b3b5281fe2ccb93c04af6 Mon Sep 17 00:00:00 2001 From: 1315groop Date: Sun, 2 Jun 2019 15:08:25 +0300 Subject: [PATCH 12/19] return to pow --- final_task/pycalc/converter.py | 167 +++++++++++++++++++++++---------- 1 file changed, 115 insertions(+), 52 deletions(-) diff --git a/final_task/pycalc/converter.py b/final_task/pycalc/converter.py index 9be9d0b2..1aafe789 100644 --- a/final_task/pycalc/converter.py +++ b/final_task/pycalc/converter.py @@ -7,30 +7,49 @@ class ConvertError(Exception): """class for error""" def __str__(self): - return 'cannot convert input expression to RPN due to mismatched parentheses' + return "cannot convert expression to RPN due to mismatched parentheses" -class Converter(): +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 = re.compile(r'(' + '|'.join( - [re.escape(func) for func in sorted( - definitions._functions, key=lambda func: len(func), reverse=True)] - ) + ')') - num = re.compile(definitions._number) - op = re.compile(r'|'.join( - [re.escape(op) for op in sorted(definitions._operators, key=lambda func: len(func), reverse=True)])) + """ + func = self.func + num = self.num + op = self.op + pos = 0 operatorStack = deque() outputStack = deque() prev = None posbrcl = 0 - - #br = deque() + while pos < len(iExpr): if num.match(iExpr, pos): numM = num.match(iExpr, pos) @@ -39,75 +58,119 @@ def convert(self, iExpr): else: outputStack.append(int(numM.group())) pos = numM.end() - elif (iExpr[pos] == '('): - operatorStack.appendleft('(') + elif iExpr[pos] == "(": + operatorStack.appendleft("(") pos += 1 - elif iExpr[pos] == ')': + 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 != '(': + if top != "(": raise ConvertError() - pos += 1 + elif func.match(iExpr, pos): funcM = func.match(iExpr, pos) - if iExpr[funcM.end()] != '(': + 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] ==',': - pos+=1 + + elif iExpr[pos] == ",": + if operatorStack: + top = operatorStack.popleft() + if "(" in operatorStack: + while operatorStack: + outputStack.append(top) + top = operatorStack.popleft() + if top == "(": + operatorStack.appendleft(top) + break + else: + raise ConvertError() + outputStack.append(",") + pos += 1 + elif op.match(iExpr, pos): match = op.match(iExpr, pos) - if len(operatorStack)!=0: + 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!="(" + 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()) - - + pos = pos + len(match.group()) + while cond: - - - while cond: - - outputStack.append(top) - if len(operatorStack) !=0: + 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!="(" + 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: + 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 - \ No newline at end of file From 5b6750c1ded24be495b251d13ee4fb48c87f3ed4 Mon Sep 17 00:00:00 2001 From: 1315groop Date: Sun, 2 Jun 2019 16:09:09 +0300 Subject: [PATCH 13/19] fix in rpn --- final_task/pycalc/converter.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/final_task/pycalc/converter.py b/final_task/pycalc/converter.py index 1aafe789..59f9ba15 100644 --- a/final_task/pycalc/converter.py +++ b/final_task/pycalc/converter.py @@ -43,12 +43,10 @@ def convert(self, iExpr): func = self.func num = self.num op = self.op - pos = 0 operatorStack = deque() outputStack = deque() - prev = None - posbrcl = 0 + while pos < len(iExpr): if num.match(iExpr, pos): @@ -90,13 +88,15 @@ def convert(self, iExpr): elif iExpr[pos] == ",": if operatorStack: top = operatorStack.popleft() - if "(" in operatorStack: + if op.match(top) and "(" in operatorStack: while operatorStack: outputStack.append(top) top = operatorStack.popleft() if top == "(": operatorStack.appendleft(top) break + elif not op.match(top): + break else: raise ConvertError() outputStack.append(",") From e48b7fa6f81cf2e644c30fb9b1a14c84ceffb874 Mon Sep 17 00:00:00 2001 From: 1315groop Date: Sun, 2 Jun 2019 16:21:23 +0300 Subject: [PATCH 14/19] pow fix 2 --- final_task/pycalc/converter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/final_task/pycalc/converter.py b/final_task/pycalc/converter.py index 59f9ba15..c45ca81d 100644 --- a/final_task/pycalc/converter.py +++ b/final_task/pycalc/converter.py @@ -96,7 +96,7 @@ def convert(self, iExpr): operatorStack.appendleft(top) break elif not op.match(top): - break + operatorStack.appendleft(top) else: raise ConvertError() outputStack.append(",") From 8eaae4507f8bef97fdad44b721793d33e52770c4 Mon Sep 17 00:00:00 2001 From: 1315groop Date: Sun, 2 Jun 2019 16:46:53 +0300 Subject: [PATCH 15/19] fix error case --- final_task/pycalc/evaluator.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/final_task/pycalc/evaluator.py b/final_task/pycalc/evaluator.py index d6f50e9a..90131abf 100644 --- a/final_task/pycalc/evaluator.py +++ b/final_task/pycalc/evaluator.py @@ -35,18 +35,23 @@ def evaluate(self, iExpr): op = self.op outputStack = [] args = [] + lenexpr = len(iExpr) for token in iExpr: if num.match(str(token)): + lenexpr=-1 outputStack.append(token) elif op.match(token): + lenexpr=-1 operand2 = outputStack.pop() operand1 = outputStack.pop() outputStack.append( definitions._operators[token].func(operand1, operand2) ) elif token == ",": + lenexpr=-1 outputStack.append(",") elif func.match(token): + lenexpr=-1 if ( "," in outputStack @@ -65,5 +70,7 @@ def evaluate(self, iExpr): outputStack.append(definitions._functions[token](operand)) if len(outputStack) == 0: raise ValueError("empty expression") + if lenexpr!=0: + raise ValueError("not right nummer of operand") return outputStack[0] From b265896c940d7f04b8c44b33ed98526aa8850e97 Mon Sep 17 00:00:00 2001 From: 1315groop Date: Sun, 2 Jun 2019 16:57:23 +0300 Subject: [PATCH 16/19] fix pow oper --- final_task/pycalc/evaluator.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/final_task/pycalc/evaluator.py b/final_task/pycalc/evaluator.py index 90131abf..6b77bfaa 100644 --- a/final_task/pycalc/evaluator.py +++ b/final_task/pycalc/evaluator.py @@ -38,21 +38,16 @@ def evaluate(self, iExpr): lenexpr = len(iExpr) for token in iExpr: if num.match(str(token)): - lenexpr=-1 outputStack.append(token) elif op.match(token): - lenexpr=-1 operand2 = outputStack.pop() operand1 = outputStack.pop() outputStack.append( definitions._operators[token].func(operand1, operand2) ) elif token == ",": - lenexpr=-1 outputStack.append(",") elif func.match(token): - lenexpr=-1 - if ( "," in outputStack and num.match(str(outputStack[-3])) @@ -70,7 +65,7 @@ def evaluate(self, iExpr): outputStack.append(definitions._functions[token](operand)) if len(outputStack) == 0: raise ValueError("empty expression") - if lenexpr!=0: + if len(outputStack) != 1: raise ValueError("not right nummer of operand") return outputStack[0] From 3c4d95cd34472d218917781750d609a5d7693447 Mon Sep 17 00:00:00 2001 From: 1315groop Date: Sun, 2 Jun 2019 17:14:16 +0300 Subject: [PATCH 17/19] fix func issue --- final_task/pycalc/converter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/final_task/pycalc/converter.py b/final_task/pycalc/converter.py index c45ca81d..53f1ab26 100644 --- a/final_task/pycalc/converter.py +++ b/final_task/pycalc/converter.py @@ -88,7 +88,7 @@ def convert(self, iExpr): elif iExpr[pos] == ",": if operatorStack: top = operatorStack.popleft() - if op.match(top) and "(" in operatorStack: + if (op.match(top) or func.match(top)) and "(" in operatorStack: while operatorStack: outputStack.append(top) top = operatorStack.popleft() From 7b944f234730d0d4f9ddee9b67781289a74e5ff9 Mon Sep 17 00:00:00 2001 From: 1315groop Date: Sun, 2 Jun 2019 17:18:21 +0300 Subject: [PATCH 18/19] line len --- final_task/pycalc/calculator_test.py | 8 +------- final_task/pycalc/converter.py | 3 +-- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/final_task/pycalc/calculator_test.py b/final_task/pycalc/calculator_test.py index 4c57e192..311240ee 100644 --- a/final_task/pycalc/calculator_test.py +++ b/final_task/pycalc/calculator_test.py @@ -109,13 +109,7 @@ def testCommon(self): self.assertEqual( self.calculator.calculate(iExpr), eval("2.0**(2.0**2.0*2.0**2.0)") ) - iExpr = r"sin(e^log(e^e^sin(23.0),45.0) + cos(3.0+log10(e^-e)))" - self.assertEqual( - self.calculator.calculate(iExpr), - eval( - "math.sin(math.e**math.log(math.e**math.e**math.sin(23.0),45.0) + math.cos(3.0+math.log10(math.e**-math.e)))" - ), - ) + """def testError(self): iExpr = r"((1+2)" diff --git a/final_task/pycalc/converter.py b/final_task/pycalc/converter.py index 53f1ab26..9a740241 100644 --- a/final_task/pycalc/converter.py +++ b/final_task/pycalc/converter.py @@ -46,7 +46,6 @@ def convert(self, iExpr): pos = 0 operatorStack = deque() outputStack = deque() - while pos < len(iExpr): if num.match(iExpr, pos): @@ -74,7 +73,7 @@ def convert(self, iExpr): funcM = func.match(iExpr, pos) flag = False try: - a = iExpr[funcM.end()+1] != "(" + a = iExpr[funcM.end() + 1] != "(" except IndexError: flag = True From 7e15e583869e84a74523b145dbb52919c4ff449b Mon Sep 17 00:00:00 2001 From: 1315groop Date: Sun, 2 Jun 2019 17:21:46 +0300 Subject: [PATCH 19/19] funny)) --- final_task/pycalc/calculator_test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/final_task/pycalc/calculator_test.py b/final_task/pycalc/calculator_test.py index 311240ee..3f9240ba 100644 --- a/final_task/pycalc/calculator_test.py +++ b/final_task/pycalc/calculator_test.py @@ -109,7 +109,6 @@ def testCommon(self): self.assertEqual( self.calculator.calculate(iExpr), eval("2.0**(2.0**2.0*2.0**2.0)") ) - """def testError(self): iExpr = r"((1+2)"