Skip to content
Snippets Groups Projects
Commit 42ed25b9 authored by Florentin Labelle's avatar Florentin Labelle
Browse files

Add calculator with perfect lint score

parent 4e0c96c5
Branches
No related tags found
No related merge requests found
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(),
}
"""
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.
This Calculator holds the logic for the calculator.
"""
from Operators import Operator, STANDARD_OPERATORS
from Expression import Token, Term, Expression, TermExpression, OperatorExpression
from calculator.operators import Operator, STANDARD_OPERATORS
from calculator.expression import Token, Term, Expression, TermExpression, OperatorExpression
class Calculator:
def __init__(self, operators = STANDARD_OPERATORS):
"""
Calculator class is a simple calculator that can parse and evaluate expressions of the form:
expression ::= term | expression operator expression
operator ::= + | - | * | /
with the usual precedence rules.
"""
def __init__(self, operators=None):
if operators is None:
operators = STANDARD_OPERATORS
self.operators = operators
def _tokenize(self, line: str) -> list[Token]:
def tokenize(self, line: str) -> list[Token]:
"""
Tokenize an expression into a list of tokens.
"""
......@@ -24,11 +30,14 @@ class Calculator:
try:
term = float(token)
tokens.append(term)
except ValueError:
raise ValueError(f"Unknown token: {token}")
except ValueError as exc:
raise ValueError(f"Invalid token {token}") from exc
return tokens
def _parse(self, tokens: list[Token]) -> Expression:
def parse(self, tokens: list[Token]) -> Expression:
"""
Parse a list of tokens into an ordered expression.
"""
if not tokens:
raise ValueError("Empty expression")
if len(tokens) == 1:
......@@ -51,7 +60,7 @@ class Calculator:
right = tokens[operator_index + 1:]
# Parse the left and right parts recursively
return OperatorExpression(operator, self._parse(left), self._parse(right))
return OperatorExpression(operator, self.parse(left), self.parse(right))
def __call__(self, expression: str) -> Term:
return self._parse(self._tokenize(expression))()
return self.parse(self.tokenize(expression))()
"""
Expression module defines the structure of an expression.
"""
from typing import Union
from Operators import Operator
from calculator.operators import Operator
Term: type = float
Token: type = Union[Operator, Term]
class OperatorExpression:
"""
OperatorExpression class is an expression that contains an operator and two sub-expressions.
"""
def __init__(self, operator: Operator, left, right):
self.operator = operator
self.left = left
......@@ -19,6 +25,9 @@ class OperatorExpression:
class TermExpression:
"""
TermExpression class is an expression that contains a single term.
"""
def __init__(self, value: Term):
self.value = value
......
"""
Operator module contains the Operator class and a list of standard operators.
"""
class Operator:
"""
Operator class is a binary operator with a symbol, a precedence and an evaluation function.
"""
def __init__(self, symbol, precedence, evaluate_function):
self.symbol = symbol
self.precedence = precedence
self.evaluate_function = evaluate_function
def __repr__(self):
return self.symbol
def __call__(self, left, right):
return self.evaluate_function(left, right)
STANDARD_OPERATORS = {
'+': Operator('+', 1, lambda a, b: a + b),
'-': Operator('-', 1, lambda a, b: a - b),
'*': Operator('×', 2, lambda a, b: a * b),
'/': Operator('/', 2, lambda a, b: a / b),
}
fastapi[all]
uvicorn[standard]
pylint
pytest
"""
Server module for the web calculator.
"""
from fastapi import FastAPI
from fastapi.requests import Request
from fastapi.templating import Jinja2Templates
from Calculator import Calculator
from calculator.calculator import Calculator
app = FastAPI()
templates = Jinja2Templates(directory="templates")
calc = Calculator()
@app.get("/icon")
@app.get("/")
async def root(request: Request):
"""
Default and only route of the web calculator.
Expects either no query parameters or a single query parameter named "expression".
"""
expression = request.query_params.get("expression", "")
context = {"request": request}
if expression:
result = calc(expression)
context = { "request": request, "expression": expression, "result": result}
context = {"request": request,
"expression": expression, "result": result}
return templates.TemplateResponse("index.html", context)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment