""" This Calculator holds the logic for the calculator. """ from calculator.operators import Operator, STANDARD_OPERATORS from calculator.expression import Term, Expression, TermExpression, OperatorExpression class Calculator: """ 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): """ 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: if int(token) == float(token): tokens.append(int(token)) except ValueError as exc: raise ValueError(f"Invalid token {token}") from exc return tokens def parse(self, tokens) -> Expression: """ Parse a list of tokens into an ordered 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): ##################################################################### ### ATTENTION: Est-ce que c'est vraiment un < (non c'est un <=) ? ### ##################################################################### 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))()