From 9682b2f4f4645150aa9e065c7c9ec885ba58367f Mon Sep 17 00:00:00 2001 From: Darius Rapalis Date: Mon, 8 Sep 2025 14:36:12 +0300 Subject: [PATCH] calculator --- calculator/main.py | 24 +++++++ .../__pycache__/calculator.cpython-312.pyc | Bin 0 -> 3125 bytes .../pkg/__pycache__/render.cpython-312.pyc | Bin 0 -> 1452 bytes calculator/pkg/calculator.py | 59 ++++++++++++++++++ calculator/pkg/render.py | 21 +++++++ calculator/tests.py | 47 ++++++++++++++ 6 files changed, 151 insertions(+) create mode 100644 calculator/main.py create mode 100644 calculator/pkg/__pycache__/calculator.cpython-312.pyc create mode 100644 calculator/pkg/__pycache__/render.cpython-312.pyc create mode 100644 calculator/pkg/calculator.py create mode 100644 calculator/pkg/render.py create mode 100644 calculator/tests.py diff --git a/calculator/main.py b/calculator/main.py new file mode 100644 index 0000000..beb4cec --- /dev/null +++ b/calculator/main.py @@ -0,0 +1,24 @@ +import sys +from pkg.calculator import Calculator +from pkg.render import render + + +def main(): + calculator = Calculator() + if len(sys.argv) <= 1: + print("Calculator App") + print('Usage: python main.py ""') + print('Example: python main.py "3 + 5"') + return + + expression = " ".join(sys.argv[1:]) + try: + result = calculator.evaluate(expression) + to_print = render(expression, result) + print(to_print) + except Exception as e: + print(f"Error: {e}") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/calculator/pkg/__pycache__/calculator.cpython-312.pyc b/calculator/pkg/__pycache__/calculator.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f069b8c1de4c9252d3732c25a44e10aed7adb246 GIT binary patch literal 3125 zcmb_eOKcNI7@paO<2bfAPMqMp=;j4kLSjK_5D*e19z_%)B&4d1T3L&C6P!4zqhpP7HY@1Om{<#He>Eq8ypaL0zwpR{2WE>~IcK_!70^e$4+RixnCOm?Ij4KorO zwDjp4=Nc;wP)Q&abz=o}GYYWD$dupxl-*7q#z*x2@Hux35FWaB@G>jyBS1`9^Q);I737VsIY^`ZGjZg4!YUbTm+3gaZ%w0@ye+|{f$j+Zs7H&hMm2! zsm-wErYKMphrr#IDP6)YXvceo_OjXS^uYne5GTYn{Y3=QAugWQ`YukojA{10mOWJ3<#b3yeW0|t z3?XtuXUQiztKbS4e4$n!j8HUeDwbW&b}J2zj`e>8R6Pkkwm3AMg=5KCVg#$%XL)_$ zYUYVgEKo!t^7oCOk$CX$6nXSULc7h0jv^ZM)Te&*;-%3gEFc%m#$d2N<8kSc8d zeu!I99Fj=3DEYarV4YET8ryTI9>vW|lIk=dB4D)6g3-CJ(YJZvU%{8qW%fgK2}`!F z#eboU2%!i-J*0>xoDYhm9Q=%wSiNh2U!)UA1%zI=giZ}Ckz=v&aKOqph7H>TyD%#9+f}MB+^rkveK@qXbhm)1AR*xkT{+RLYTwh|nCVT%VaNB4t@g zSE}s*PLH9Crxdv2EgvsjD63jVuEHaD{CGz2PI9;0Q|`2|$XBWJ_d2dtN+caGMcq(SMm-?tg#qv= zStzYe9KLz@tIl!z<07|-3eV@A6cleAx#zp*NY%VIS8_V#JiXv7xpC^+sq3f5F_`76 zcJ~d}HCL*<^-CoYxfw}*kS=eXzBpSuSHAC|{T=ge>5>ZQhe1RhPy+$O5eN)KmBBEr zI|G4>gK{`KLaJa{q@D_q3c8i-rycz$%YM^QbPy`&VQZAGSJ5)#b^9KUVxh z(%MP`I0ip`ocVzR@Et9jf!UynQdR&Do`d^f4h!LNnAsQXbuY)nAXVK zw2=}tu%xTOZGcT1DSpl4I5n?>3~bX@b!--we9g>eJd0~Sho!YT$N&!Sm=AY+PHj9k z4+&W!0kWh)CK-p_v^>re1~CuoN0~f{W=91Bf8e-`qinZ99NWFYn}^NiJ!o#|Qh%jO z@lf#}IbOu!>HoBw%fRb>I(hO&-%ovV--%mjIoU0~HQC<0F$Wo>b5BzsbS82@!hnum z?Ox%PZF9EG>*#&%6&3=48WuP}57&+?=x!%+?EsN&f5EoJ8b*h_7A>lxq-vZL7cAGq zl+0*NC=8Ws2ZH$$|BO-tVN^*$#QddA&-f4L@_C(~h8H<2WMHL6os136-a#~;&o>N3!Wv69X8{mb<1DrU(B?L)h<6J7P zr#MYk*vz8@tH5mqC7hW<`yi-vK`r#Pskh*q1G}E~eD7+o;t8xpzXpfQ;7}zvyzB2? z?I=dyp5OKdm*{K%!6Loi)=?NQ(#v<&r%FBVV%u$lWz1|F$xVEtTXN$&r02!t zv<0uNUXcu6~p0FAbW^l?r*)4((7~#mgpjX-zGOA1;=Y?^!cE@%g$LzHRnJOe(T< zH)>MRp9Jo5AG(2yvgOp`y*E!yrnlrbnQ(<ym-kv98AO=h&Lo6L<0d9zu_ zADE}Cb%7 literal 0 HcmV?d00001 diff --git a/calculator/pkg/calculator.py b/calculator/pkg/calculator.py new file mode 100644 index 0000000..6f5eb6c --- /dev/null +++ b/calculator/pkg/calculator.py @@ -0,0 +1,59 @@ +class Calculator: + def __init__(self): + self.operators = { + "+": lambda a, b: a + b, + "-": lambda a, b: a - b, + "*": lambda a, b: a * b, + "/": lambda a, b: a / b, + } + self.precedence = { + "+": 1, + "-": 1, + "*": 2, + "/": 2, + } + + def evaluate(self, expression): + if not expression or expression.isspace(): + return None + tokens = expression.strip().split() + return self._evaluate_infix(tokens) + + def _evaluate_infix(self, tokens): + values = [] + operators = [] + + for token in tokens: + if token in self.operators: + while ( + operators + and operators[-1] in self.operators + and self.precedence[operators[-1]] >= self.precedence[token] + ): + self._apply_operator(operators, values) + operators.append(token) + else: + try: + values.append(float(token)) + except ValueError: + raise ValueError(f"invalid token: {token}") + + while operators: + self._apply_operator(operators, values) + + if len(values) != 1: + raise ValueError("invalid expression") + + return values[0] + + def _apply_operator(self, operators, values): + if not operators: + return + + operator = operators.pop() + if len(values) < 2: + raise ValueError(f"not enough operands for operator {operator}") + + b = values.pop() + a = values.pop() + values.append(self.operators[operator](a, b)) \ No newline at end of file diff --git a/calculator/pkg/render.py b/calculator/pkg/render.py new file mode 100644 index 0000000..917ea6c --- /dev/null +++ b/calculator/pkg/render.py @@ -0,0 +1,21 @@ +def render(expression, result): + if isinstance(result, float) and result.is_integer(): + result_str = str(int(result)) + else: + result_str = str(result) + + box_width = max(len(expression), len(result_str)) + 4 + + box = [] + box.append("┌" + "─" * box_width + "┐") + box.append( + "│" + " " * 2 + expression + " " * (box_width - len(expression) - 2) + "│" + ) + box.append("│" + " " * box_width + "│") + box.append("│" + " " * 2 + "=" + " " * (box_width - 3) + "│") + box.append("│" + " " * box_width + "│") + box.append( + "│" + " " * 2 + result_str + " " * (box_width - len(result_str) - 2) + "│" + ) + box.append("└" + "─" * box_width + "┘") + return "\n".join(box) \ No newline at end of file diff --git a/calculator/tests.py b/calculator/tests.py new file mode 100644 index 0000000..f184589 --- /dev/null +++ b/calculator/tests.py @@ -0,0 +1,47 @@ +import unittest +from pkg.calculator import Calculator + + +class TestCalculator(unittest.TestCase): + def setUp(self): + self.calculator = Calculator() + + def test_addition(self): + result = self.calculator.evaluate("3 + 5") + self.assertEqual(result, 8) + + def test_subtraction(self): + result = self.calculator.evaluate("10 - 4") + self.assertEqual(result, 6) + + def test_multiplication(self): + result = self.calculator.evaluate("3 * 4") + self.assertEqual(result, 12) + + def test_division(self): + result = self.calculator.evaluate("10 / 2") + self.assertEqual(result, 5) + + def test_nested_expression(self): + result = self.calculator.evaluate("3 * 4 + 5") + self.assertEqual(result, 17) + + def test_complex_expression(self): + result = self.calculator.evaluate("2 * 3 - 8 / 2 + 5") + self.assertEqual(result, 7) + + def test_empty_expression(self): + result = self.calculator.evaluate("") + self.assertIsNone(result) + + def test_invalid_operator(self): + with self.assertRaises(ValueError): + self.calculator.evaluate("$ 3 5") + + def test_not_enough_operands(self): + with self.assertRaises(ValueError): + self.calculator.evaluate("+ 3") + + +if __name__ == "__main__": + unittest.main() \ No newline at end of file