From 4e0c96c59607fd366b7bb2430706351820b2369d Mon Sep 17 00:00:00 2001
From: Florentin Labelle <florentin.labelle@student-cs.fr>
Date: Sun, 9 Oct 2022 16:45:56 +0200
Subject: [PATCH] Implement calculator

---
 calculator/Calculator.py                      |  57 ++++++++++++++++++
 calculator/Expression.py                      |  32 ++++++++++
 calculator/Operators.py                       |  46 ++++++++++++++
 calculator/README.md                          |   3 +
 .../__pycache__/Calculator.cpython-310.pyc    | Bin 0 -> 2037 bytes
 .../__pycache__/Expression.cpython-310.pyc    | Bin 0 -> 1616 bytes
 .../__pycache__/Operator.cpython-310.pyc      | Bin 0 -> 2145 bytes
 .../__pycache__/Operators.cpython-310.pyc     | Bin 0 -> 2146 bytes
 .../__pycache__/calculator.cpython-310.pyc    | Bin 0 -> 2036 bytes
 calculator/__pycache__/server.cpython-310.pyc | Bin 0 -> 722 bytes
 calculator/requirements.txt                   |   2 +
 calculator/server.py                          |  19 ++++++
 calculator/templates/index.html               |  25 ++++++++
 13 files changed, 184 insertions(+)
 create mode 100644 calculator/Calculator.py
 create mode 100644 calculator/Expression.py
 create mode 100644 calculator/Operators.py
 create mode 100644 calculator/README.md
 create mode 100644 calculator/__pycache__/Calculator.cpython-310.pyc
 create mode 100644 calculator/__pycache__/Expression.cpython-310.pyc
 create mode 100644 calculator/__pycache__/Operator.cpython-310.pyc
 create mode 100644 calculator/__pycache__/Operators.cpython-310.pyc
 create mode 100644 calculator/__pycache__/calculator.cpython-310.pyc
 create mode 100644 calculator/__pycache__/server.cpython-310.pyc
 create mode 100644 calculator/requirements.txt
 create mode 100644 calculator/server.py
 create mode 100644 calculator/templates/index.html

diff --git a/calculator/Calculator.py b/calculator/Calculator.py
new file mode 100644
index 0000000..f69d723
--- /dev/null
+++ b/calculator/Calculator.py
@@ -0,0 +1,57 @@
+"""
+This Calculator module is a simple calculator that can parse and evaluate expressions of the form:
+expression ::= term | expression operator expression
+operator ::= + | - | * | /
+with the usual precedence rules.
+"""
+
+from Operators import Operator, STANDARD_OPERATORS
+from Expression import Token, Term, Expression, TermExpression, OperatorExpression
+
+class Calculator:
+    def __init__(self, operators = STANDARD_OPERATORS):
+        self.operators = operators
+
+    def _tokenize(self, line: str) -> list[Token]:
+        """
+        Tokenize an expression into a list of tokens.
+        """
+        tokens = []
+        for token in line.split():
+            if token in self.operators:
+                tokens.append(self.operators[token])
+            else:
+                try:
+                    term = float(token)
+                    tokens.append(term)
+                except ValueError:
+                    raise ValueError(f"Unknown token: {token}")
+        return tokens
+
+    def _parse(self, tokens: list[Token]) -> Expression:
+        if not tokens:
+            raise ValueError("Empty expression")
+        if len(tokens) == 1:
+            if isinstance(tokens[0], Term):
+                return TermExpression(tokens[0])
+            raise ValueError(f"Expected a term, got {tokens[0]}")
+        if len(tokens) == 2:
+            raise ValueError("Invalid expression")
+
+        # Find the rightest operator with the lowest precedence
+        operator = None
+        for i, token in enumerate(tokens):
+            if isinstance(token, Operator):
+                if operator is None or token.precedence <= operator.precedence:
+                    operator = token
+                    operator_index = i
+
+        # Split the expression into two parts
+        left = tokens[:operator_index]
+        right = tokens[operator_index + 1:]
+
+        # Parse the left and right parts recursively
+        return OperatorExpression(operator, self._parse(left), self._parse(right))
+
+    def __call__(self, expression: str) -> Term:
+        return self._parse(self._tokenize(expression))()
diff --git a/calculator/Expression.py b/calculator/Expression.py
new file mode 100644
index 0000000..d19ea88
--- /dev/null
+++ b/calculator/Expression.py
@@ -0,0 +1,32 @@
+from typing import Union
+from Operators import Operator
+
+Term: type = float
+Token: type = Union[Operator, Term]
+
+
+class OperatorExpression:
+    def __init__(self, operator: Operator, left, right):
+        self.operator = operator
+        self.left = left
+        self.right = right
+
+    def __repr__(self):
+        return f"({self.left} {self.operator} {self.right})"
+
+    def __call__(self) -> Term:
+        return self.operator(self.left(), self.right())
+
+
+class TermExpression:
+    def __init__(self, value: Term):
+        self.value = value
+
+    def __repr__(self):
+        return str(self.value)
+
+    def __call__(self) -> Term:
+        return self.value
+
+
+Expression: type = Union[OperatorExpression, TermExpression]
diff --git a/calculator/Operators.py b/calculator/Operators.py
new file mode 100644
index 0000000..fdd2090
--- /dev/null
+++ b/calculator/Operators.py
@@ -0,0 +1,46 @@
+class Operator:
+    def __init__(self, symbol, precedence):
+        self.symbol = symbol
+        self.precedence = precedence
+
+    def __repr__(self):
+        return self.symbol
+
+    def __call__(self):
+        raise NotImplemented
+
+class Adder(Operator):
+    def __init__(self):
+        super().__init__('+', 1)
+
+    def __call__(self, a, b):
+        return a + b
+
+class Subtracter(Operator):
+    def __init__(self):
+        super().__init__('-', 1)
+
+    def __call__(self, a, b):
+        return a - b
+
+class Multiplyer(Operator):
+    def __init__(self):
+        super().__init__('*', 2)
+
+    def __call__(self, a, b):
+        return a * b
+
+class Divider(Operator):
+    def __init__(self):
+        super().__init__('/', 2)
+
+    def __call__(self, a, b):
+        return a / b
+
+
+STANDARD_OPERATORS = {
+    '+': Adder(),
+    '-': Subtracter(),
+    '*': Multiplyer(),
+    '/': Divider(),
+}
diff --git a/calculator/README.md b/calculator/README.md
new file mode 100644
index 0000000..c8389d8
--- /dev/null
+++ b/calculator/README.md
@@ -0,0 +1,3 @@
+# ViaRézo Calculator
+
+This is a simple two-operand calculator. It is a good example of how to use the [GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html) to lint, test and deploy a project.
diff --git a/calculator/__pycache__/Calculator.cpython-310.pyc b/calculator/__pycache__/Calculator.cpython-310.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..24c0a7f8401cd6d66646eb6938fc36c5b8dfbf5d
GIT binary patch
literal 2037
zcmd1j<>g{vU|<No>y)g{!NBks#6iZ)3=9ko3=9m#P7DkTDGX5zDU2yhIgGhXQB1ka
zQOt}WK64IBE^8EPE?X2^E=v?kE_)PvE=LpxBZE6b3QG!W3quNPGgB023R^IPCVSOY
zu8@q(Vg={KoaEA+#FG3Xh1~p<(wtO<%wmN^h2qTIf}B)^WQ1%<Mq-IVa$=rBL1IyH
zszPF3ib85xVoqsdNvc9>ML|(&adBpTUa>-cnnFoNszO?RQLYsiLPEjH%2uHywJ2Ai
z21&U>enDyx*m8tQF1Q#-i?%|Ig04c1f|f#!f<9MyW=RIv_|oFi#2kf!qSWNnl+?WB
zRE46_oYZ1Hu9u)d(`35E;SV+VmQZkrqo0dokW0LOfNPLrh<{M9ChINMko@e_yjv_G
zsYSWBxLo1xzr_a<LE;O+3_^${gX}=Ytgy%lV_;xNWr$)-VTfW%Wlm!RMN$i66iW(o
zDr*W$DqAYMBtsfY3Tp~m3riG73Oht3g(a0UjU|O6g|meviYtW+td={CC51bMr-dbo
zCxthdL6h$m7b2>XnUJgpu|e1wlsrJGsD`nIA)X<HA(&w$qn{?@ElyZ~7HcxyVku6|
zNh{)JU|?9uP{hl?!0^jRKO;XkRX;5!zg)j8GqEVODqlZ2GdV>c5&8Oh`UuPQ3My}L
z#K&jmWtPOp^MEV`+0MmSC5)s7CVz`1Co?bg7F$tjNoi3Yin~Ff#>v3I0Csl@0|P@k
zLoHJc;{t|-3@!|@nzhU|%ry*IjAaZ(Vl_+)m_Y0rW=V#HjEoG0Y*pMP%r(r-jI}Ia
zITny;I71;D3quJ@r9LAA2$ry<FxD{7W|+&=%-F<O&QQY?%%I6sWzVGm1>iK0Sp`bW
z$Z0Y&uOwd~Q6VR@xCES<K`EtJ53Z|<KQu2pFTXrbp#&sjr2tA!#RWN;CE#Ga#g<r5
zkeZiri#07LKe6N%S6E_BX{u{cQGSsoYY`~CiUb%K7;dqFbr;`a1=F`!KzRckArOKO
zq=qvdESXuA$`1->F$M+(78V{xE=D0nCMFI>K1Lo!K1QbhEKF4*$YBrDR0Pt166PEX
z3=C|rFh9Y-z)-_j%vQpX!UzhX62=<FX2ukzY^EZm5~do)6lO_=66R*6BFPdK2(Otj
zn4yFr1<HekEL0zBHdB#K2}24S$gC8GTBZ_Kka;c)vEngIwam3FB}_FeC2Y-1MV>WG
zDGa3y#jZ6hS?o1TS?t-2MV>V*wM;e4wXB6kHH;|?HLR^nk_@$MHH<Z^DNN!FDGXpf
zdkIGkQw_rc&KkCbj9{95A!9Q$s6g|p5^&8eD5*q>$(M`_3=BmeqDs`Yq98T7BsE1L
z5nKxCD5U3?D7*xv+$tf@yt2fc%oHS@np{Pqpa@{jNzJ>(m06sbS6q^qmz-JzO0-4n
zASuq&ywY4y;gwnhGUXN*qA)4~1@kSocyLLn$pMLUP+Z*NfMvy7jG0B^3=9lWd@zyt
z%)FG;id!r>sc9v*Sc@{#GfE&S0hCFLK*^y<fq{WR9+VJNKna17104N4j6#e&j2w(C
z%mR#Dj3SIIj4X^y4Lr;Oj1tUMg2>4N?3`OjWim?A0C@uBJ8*Us0woQG1q?L|3m9t{
z7cwqnY-R{%C<2u(noMAK6iG5LFlaJC{H6*DXRtfL1jw<VA|g34CnrAM0_0+l=|bp5
z1Vr9XlNY2oFEKYYKK>S0e0(mr9*U2@#S<T2SelpvktqUIfkmKvaErOPqzF{76qgjS
zfNTair3jR)Aua>?qDT!S0d@_70J*COROEn)zhaPQI2d>s#hAqe#3aGuewtjj*yH0<
z@{{A^Z*js({$g+hL+l6J0=5)v3n(RkV;ig;DZap#g6zA+VFU4x9Vi_ZgHi?yg8;h>
HqYx7S3B~oB

literal 0
HcmV?d00001

diff --git a/calculator/__pycache__/Expression.cpython-310.pyc b/calculator/__pycache__/Expression.cpython-310.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..39cc4147cc27f4e3f9656a4e48fe56674b8f5f41
GIT binary patch
literal 1616
zcmd1j<>g{vU|?vy>y&(%iGkrUh=Yuo85kHG7#J9e^B5QyLK#vRQW$d>av7r-89{8O
z9Hw06C}u{6RF){#RJK(16lNQSRK`^1RMs>mNros6uo#OCgF8bCYYJNnLke3nQxvB=
zLkfEeM+-v=M>A6tS1MyFXDSz1JvUfArwv02S1^Mn_e+pVG#PKPhUR7F=V>zD;_xp>
zElMoOFS^ANl3J8|i!~%aJ2fvEqzoCu9c{qCz>vxi#hAhn#gxLB%ACfO!j!_?!V<-j
z!ji(;!Vtxp!j{UG#sqc_OB8zwXE1{%*DWEKHLeu}MXANbnfZCQIP#%ZC$k_~1!6NX
zFfcfSf=G;kfuV-6h9RDzhN*@jp0S3xh9RCQg&~+>C8OU;<|1|m28LTKIjLzSw^)lZ
z(=$pmS#Gfur{<)AWr{!%QN+!_z_5~`h?jwZ;g^wqMt*Lpep*g`xqew@Vo_>UzJ79M
za*BR(Voq{t4k#S-_4E;L)GMgG#StH$nU`4-AI}ML5gP*o0~;e7BNt<pG^U^QU}}>Y
zk-P+AgIo&sk{kmALkeRJLl(nqh7_h6hAc)X%`}@Kg?TP}FoPzG-!DduUyKUB7&SGS
zA)WwfhxmlCh=+jz;vKM`iugcbz!4u`lv+>}9}nUcgThY^mjfZ{Zm|`mmXsFdVK|f(
z7WN{buxD7nP{Xi*aUqCgTF9`FDVU*%17traFp7jhEKtCrx+FO<Cnr8$5ENV>mx$nU
z2}GTrCI={t^AdAY<Ku5}#mDF7r<CS^*gWy^g{6r(5Sb!S5-j2e*$8r35h$G$fzk)W
zZd6wkfuafI&SDV3!NAAJ!79WI7Nsz0gAzO_r?Et_L6bHgDBmEG%q`Zk#GKO9WRTBc
zX#m6qVQ>NfB}zz|2Bl+gXs%=|l4f9F&}4$dEr^ZeD^QezJr45iXOKBuj8)>Ow&G5B
zAoXy2OBg^2u9*=Ui<(Tgn2Sq_z?Nf+%py=oOM-%2mVto*i=B|PR3ys4z<`mEK&c*V
zF(@uT=^bimkqpQ{EJ+6(n;=PW`enoBM_7tsFA@hCA^{@6?gOU=MB)I26jBI+69+gL
zr5G3(K(0lk3uYnaB9Q1UE<_yqY4Y4+E2%8V%u6o<h2JesSXM6vC&OE;X*v0cCEzr8
zi>0KpAoUi1e0*YFUVcepNoIataeRCcC?A0n9N6?Cd5}}V?m!SA7v18pfuwgkP<R!C
Tg!mYE7<rgP82Om_cqO<2V~9fl

literal 0
HcmV?d00001

diff --git a/calculator/__pycache__/Operator.cpython-310.pyc b/calculator/__pycache__/Operator.cpython-310.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..53418f864c433e3db52a95846f613ed4d9581a61
GIT binary patch
literal 2145
zcmd1j<>g{vU|`6(?UY>3%)sy%#6iZa3=9ko3=9m#84L^z?hGjmDU2-)DU8ibQ6L^u
z3UdoX3Uew$GjkN9J3|Uf3Tq2P3M-h;<j#=7mcrh`kirh;GrKdSaHMdyFr;vT`7Ei7
z%?znb%?zo`%?zn5%?v4Ab6BESQ@Dc}G<lMd-2h@U!Q7$3z`&5o5XG3n5XF?jn8MV;
z5XGFroWjz=5XF+hn!?t?5XG9p9?YQ0af`#hAhjs5B)=$`8Ds<$gV-R`oI$=4U|?XV
zVXR?@XQ*MSVTfl;VF+ee$>^uabc?OHGB+td=N4B%QEGB(N@`wmswVR-mg3Z$v?7oZ
zMJx;q3@aIm*cccXei`Uz<maa9r{(0A>z8FF7Nu6@>nCR>r|2gq<|LQqfZU+3rw?<E
zUP0w8j`;Y@yv&mLc#u<yL1wWraxqp3!qvd!lNq7z22mh32!q`YvblyKiy@ey2xN#R
zBiLOl8H+%126h*OU}s=p0NGcRT2K@p57Jc(QqP8J8$=ejZ6GI>Ff=hj?YhP1mtW$U
zTac5Qo0?aWno`8czyJvhR7;Z+b8_P2L6TrgSx_y7$ogrrf!vpun420Oe~T+VJ~uz5
zGzY}yiH|QVP0WGF6mfv8043KV9uN!U6NqJC7RZ}Lpuhp)Vvt%61`b9J79M7>C`O6`
zMItCnKO?88c7`-YaH`@!q^foX7KSL+U<OUrTda;LDXB#mfdmR0ka3@d7#J8z7@8Rt
zfDKv5#K-_jP@2qszZkV&g50%|=@x5oX+dfc#5169S;=^dGd?~!C$YFVJ|5y9uvb7n
z;ALQ7_za3HA;u~`u-$s_bOEvu<bDtau|dkf=>nv&hOvf89F{V`hOT4=r;A&RiMJS&
zz#<R=Y!@gzL0JK87YCAEkO*4IRs?cM5g#bn`9TE84sa~o;)S|BITaMq5HrE?Ar4Xp
zHeHZ`fdOR0XOK7t0}rDBa}h`kXOt9y+@i^Piz~P^siY_|xdcy`7lFbY9KyOqA|NY8
zK?EpZi@+`b*;xd32FT4|6_N}L3{}Divq=b336wA`k^<QUwgF6ltpWKIY_lw;HIU#e
zl0(%7Rt?sx$iToL43bm;5g;DnF!C+UDakCzsU#wdZZT@T1SOLqykR5_vKtac2(w8D
zBN5`l2pkBIFha40P#7sQFff3_9UMj=9>FkTcgZZv#FudqiAcXl736)q0i*;{0S+Jm
zsKF$Jjx=$h19k;Cbdc;I6f|lK3=E(|Qv?ng5Koh(2%Mh4P6icpMd}O;41Sty;4%@M
z3yZ)yK;c*f(s4^DIK<J<#WBbw-ao)K$T7q}C>SYM!_)aK4jV`@Z3imXia{BRk3oQ2
PfE@%`I9V7$kn0ZsT!CPI

literal 0
HcmV?d00001

diff --git a/calculator/__pycache__/Operators.cpython-310.pyc b/calculator/__pycache__/Operators.cpython-310.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..6e8f7a6a441d4e1396401e05cfb89d4727635010
GIT binary patch
literal 2146
zcmd1j<>g{vU|`6(?UY>3%)sy%#6iZa3=9ko3=9m#84L^z?hGjmDU2-)DU8ibQ6L^u
z3UdoX3Uew$GjkN9J3|Uf3Tq2P3M-h;<j#=7mcrh`kirh;GrKdSaHMdyFr;vT`7Ei7
z%?znb%?zo`%?zn5%?v4Ab6BESQ@Dc}G<lMd-2h@U!Q7$3z`&5o5XG3n5XF?jn8MV;
z5XGFroWjz=5XF+hn!?t?5XG9p9?YQ0af`#hAhjs5B)=$`8Ds<$gV-R`oI$=4U|?XV
zVXR?@XQ*MSVTfl;VF+ee$>^uabc?OHGB+td=N4B%QEGB(N@`wmswVR-mg3Z$v?7oZ
zMJx;q3@aIm*cccXei`a#<maa9r{(0A>z8FF7Nu6@>nCR>r|2gq<|LQqfZU+3rw?;Z
zv0g#tEsps3%)HE!_;`?8ib004F>*0h3BnZV!Q_(}p$-R8AT|ht9S^d)h9Qd~n4t({
zh$bW0VJjJnK#>M^7=&PFU|;~*SCm>%6dw=LRSZ(khH4u`7PoC6CzdcYF+%OS#pjn_
z;+b2JlbV~FSCX1i#L2(_2@O<BlM{1t;^RS*U`tt0ErrPXX|jRbmzS8E8Xtd)D?UCq
zKczGW#O8^QFDy;WfyfkbfUE$e*CHMe3*-}sWndP_n?<0&0pVhhS`G#dMh+GpX0Rwm
zk^;pdC`>;iC#iOZG)8c;;y@&;b_N!PDAr&GP1akijwvarMHqnu3LB7dpM@A07)ltL
z85V#IS;)l507_As%znQZwO@kVwUX%;YjJ5oY7xXUpm15qc#AVWJ~=0`xHvu@;vcYA
zKtAAQU|{$RiYy_<Dn78?h;#w65afOk2C+fP!07^{v4*jRNgS3kz=p152B(W#jET1x
zlfWVn0&Ev3JVAK@Y!?TTU62S`$yNk%Nf93?*!e*O$PREU+~S40JvkK=(GWAi@gWXU
z2R2=hfq?;J!)K5<2LlhI0CN#Y3}=)Sf!w0WdW$Q#G^wO0F}Va!m=}S<92~;BMIs<8
zML`58V2i*m0NGgtb_U4JU=@-K3=CDm2(w8DQwfwXEs_G+1-1c9fUN=f6l}9BrZte@
zERsXj238H$tH{8>APkaJ01+S_;V|+o%_+$&$f+bEjBYV%y#ytbBD`TF4YC^&MhLS>
z2qO{V!U!A)kT62AhENzOGcYiK!yOz(ARfUmVt2_b%fy#)5s65@NEPILyaA*HQUMMi
z0jR+wgpM?Ep#ydWICPNgAQUue3=9mQL{kI~8W2yDr3jp!z)l7gbVce63=DpnY~V5x
zoC}M<IzZuA1k!O!C^*E?&&4swCEh>4HOMi<KPVU}SHsi!Ee;z<F>MDb*NQ<Ii;qEo
QTYwz|SvXl3L6GYY0L?vM@&Et;

literal 0
HcmV?d00001

diff --git a/calculator/__pycache__/calculator.cpython-310.pyc b/calculator/__pycache__/calculator.cpython-310.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..e137347312a05e1267631372d1a44dda7fc76488
GIT binary patch
literal 2036
zcmd1j<>g{vU|@KE$0=EjgMr~Oh=Yuo85kHG7#J9eofsGxQW&BbQW#U1au{=&qL^}-
zqnH^%eC8aMT-GSoT(&5-T=ppTT#hJ?T+S#?Mh16=6qXd$7KRkoW~L~v6t-XnP4=p*
zTp<~m#R|@eImx9ti6!|(3c2|yr8%hznZ*i;3dNbZ1v#k-$q3n!jKmU!<itFMg2bZY
zRE5O66ou5X#GKN^l2nD%ih`ol;^NHwykdp?G=-9kRE4zsqFgI3goJ{Xm90WaYEiC2
z4U%$&{DRaXu;mDqTyQav7Hx$Z1zm+21ucad1%0ma%#sYS@ukJ3i8%@dMXAZDDXDqM
zsR~7<IjO~ZTrWX^rpa`R!yjt!Eur8LM?V+GAeVUm0M{VL5dWZHP1akiA^F*<dAC?X
zQj2nLak;|Xe~S+!g2Wes8H5l^2HAm(Sz(bA#=yXk$`Hkv!Vtxj%ACdsili3CD3%oF
zRMr%hRJK%hNrp6*6xI~B7M3WE6n2P63QH<y8cPaC3TF#T6jurtSS@!NOA2=iPYX*F
zPYQ1^gC^fCE<{u%Ga*?IVuP?VD0zTVQ4M1aLp(zYLomZiMn6r)Tb!@}E!JeZ#ZsJ_
zlUBsdz`(GQp@^4(f#H{tenx(7s(xBdez|^GW@1rlRla_5W^#%?BJ%b1kT`k;mA5$J
z<1_OzOXA~sK$e1R=VGi9MmSIpCVz`1Co?bg7F$tjNoi3Yin~Ff#>v3I0Csl@0|P@k
zLoHJc;{t|-3@!|@nzhU|%ry*IjAaZ(Vl_+)m_Y0rW=V#HjEoG0Y*pMP%r(r-jI}Ia
zITny;I71;D3quJ@r9LAA2$ry<FxD{7W|+&=%-F<O&QQY?%%I6sWzVGm1>iK0Sp`bW
z$Z0Y&uOwd~Q6VR@xCES<K`EtJ53Z|<KQu2pFTXrbp#&sjr2tA!#RWN;CE#Ga#g<r5
zkeZiri#07LKe6N%S6E_BX{u{cQGSsoYY`~CiUb%K7;dqFbr;`a1=F`!KzRckArOKO
zq=qvdESXuA$`1->F$M+(78V{xE=D0nCMFI>K1Lo!K1QbhEKF4*$YBrDR0Pt166PEX
z3=C|rFh9Y-z)-_j%vQpX!UzhX62=<FX2ukzY^EZm5~do)6lO_=66R*6BFPdK2(Otj
zn4yFr1<HekEL0zBHdB#K2}24S$gC8GTBZ_Kka;c)vEngIwam3FB}_FeC2Y-1MV>WG
zDGa3y#jZ6hS?o1TS?t-2MV>V*wM;e4wXB6kHH;|?HLR^nk_@$MHH<Z^DNN!FDGXpf
zdkIGkQw_rc&KkCbj9{95A!9Q$s6g|p5^&8eD5*q>$(M`_3=BmeqDs`Yq98T7BsE1L
z5nKxCD5U3?D7*xv+$tf@yt2fc%oHS@np{Pqpa@{jNzJ>(m06sbS6q^qmz-JzO0-4n
zASuq&ywY4y;gwnhGUXN*qA)4~1@kSocyLLn$pMLUP+Z*NfMvy7jG0B^3=9lWd@zyt
z%)FG;id!r>sc9v*Sc@{#GfE&S0hCFLK*^y<fq{WR9+VJNKna17104N4j6#e&j2w(C
z%mR#Dj3SIIj4X^y4Lr;Oj1tUMg2>4N?3`OjWim?A0C@uBJ8*Us0woQG1q?L|3m9t{
z7cwqnY-R{%C<2u(noMAK6iG5LFlaJC{H6*DXRtfL1jw<VA|g34CnrAM0_0+l=|bp5
z1Vr9XlNY2oFEKYYKK>S0e0(mr9*U2@#S<T2SelpvktqUIfkmKvaErOPqzF{76qgjS
zfNTair3jR)Aua>?qDT!S0d@_70J*COROEn)zhaPQI2d>s#hAqe#3aGuewy63*yH0<
z@{{A^ixfd7fZPqS8SF=}bzt+s)`1cNII_XIk)jK1C&;>695xW|*nyH^F(_fMFbJ^A
IFbXjN0HZzf=>Px#

literal 0
HcmV?d00001

diff --git a/calculator/__pycache__/server.cpython-310.pyc b/calculator/__pycache__/server.cpython-310.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..02f6dd8e95307bfb97827355b4dc24d6df06874c
GIT binary patch
literal 722
zcmd1j<>g{vU|^Vj&nY>SiGkrUh=Yt-85kHG7#J9ea~K#HQW#Pga~N_NqZk=MY^EHh
zT;?cdFq=7tC6_gd70hPIVasKUVq;`TWo%}MVozmFVNGG{WsKrTWou@L;!I^<z?H(j
zkP%FCEM!dKOl3}EO5sZ3ZeeL=Y-Wt&PT>h=(Bypya)&14Eq1rW;u6OIPY^RGwXig`
zxI~lj7Qa_!URI(}NNR3DPGU)Fu_og!F6YFY<kFnPlKi4uoFzySoGF<_smUezMU_>o
z`kBf3dA}I-Z?P9a%t~ei*#pHa3=9k)d>G_q8wLi3M#dV3EQSS)DU2yh3z=#eQ<!TQ
zG8t=`Y8Z<oN|<UGn;C1FYZz)6YnW45<}lYXl`t=0Nnx#FTF4a4pvmTUiz~IFpeVJt
zI5R)*7DEvy0|SF5Gngs@h5IeGqSWHjoRTW8%)FG;3cZYy+#F5TD4xR7)S}Axg2bZ4
z+~Qlz>8T~RSdtTSl8e|F7#N}iV15otEiTB<D^As9K{Am&IX|x?wW6emhk=1%B}0(_
z0|Ub^J^hUQ+*JLvocwbAvdqMy)T(^_<jmw0eUPb;5YyMwFHS8gOD)nXsJz8el%HP$
za$qqZ0|Nt710M?qBMT!3BM&1FW04R81B0I?PZWDvVsS}gL1qyX*i}%T9yC~si<m*e
zLNH-yd}QXO7l8t}2$ZmHF((!jfIU?NvZsibfq?;RJlNGpgdhV0!z~V*-29Z%oK!nd
dgcXZ0Ffi~ifE>ib$i~FQ$i~RQ#K9!S2mqh{uiXFu

literal 0
HcmV?d00001

diff --git a/calculator/requirements.txt b/calculator/requirements.txt
new file mode 100644
index 0000000..9c7788a
--- /dev/null
+++ b/calculator/requirements.txt
@@ -0,0 +1,2 @@
+fastapi[all]
+uvicorn[standard]
diff --git a/calculator/server.py b/calculator/server.py
new file mode 100644
index 0000000..8f5fed6
--- /dev/null
+++ b/calculator/server.py
@@ -0,0 +1,19 @@
+from fastapi import FastAPI
+from fastapi.requests import Request
+from fastapi.templating import Jinja2Templates
+from Calculator import Calculator
+
+app = FastAPI()
+templates = Jinja2Templates(directory="templates")
+calc = Calculator()
+
+@app.get("/icon")
+
+@app.get("/")
+async def root(request: Request):
+    expression = request.query_params.get("expression", "")
+    context = { "request": request }
+    if expression:
+        result = calc(expression)
+        context = { "request": request, "expression": expression, "result": result}
+    return templates.TemplateResponse("index.html", context)
diff --git a/calculator/templates/index.html b/calculator/templates/index.html
new file mode 100644
index 0000000..1806d83
--- /dev/null
+++ b/calculator/templates/index.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <title>Calculator</title>
+</head>
+<body>
+  <header>
+    <h1>ViaRézo Calculator</h1>
+  </header>
+  <div>
+    <form action="/" method="get">
+      {% if expression %}
+      <input type="text" name="expression" placeholder="12 / 2 + 6 * 8" value="{{ expression }}">
+      {% else %}
+      <input type="text" name="expression" placeholder="12 / 2 + 6 * 8">
+      {% endif %}
+      <button type="submit">Calculate</button>
+    </form>
+    {% if result %}
+    <p>Result: {{ result }}</p>
+    {% endif %}
+  </div>
+</body>
+</html
-- 
GitLab