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

Implement calculator

parent 491f8398
No related branches found
No related tags found
No related merge requests found
"""
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))()
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]
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(),
}
# 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.
File added
File added
File added
File added
File added
File added
fastapi[all]
uvicorn[standard]
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)
<!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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment