Spaces:
Runtime error
Runtime error
| from __future__ import annotations | |
| from dataclasses import dataclass, field | |
| from enum import Enum | |
| from ast_parser.lexer import Lexer | |
| from ast_parser.linter import Linter | |
| from ast_parser.token import Token, TokenKind | |
| class EquationKind(str, Enum): | |
| """Kind of relationship between the left and right parts of the | |
| Equation.""" | |
| EQ = "=" | |
| """Equality operator.""" | |
| LEQ = "<=" | |
| """Less than or equal to operator.""" | |
| GEQ = ">=" | |
| """Greater than or equal to operator.""" | |
| class Equation: | |
| """Equation definition.""" | |
| kind: EquationKind | |
| """Kind of relationship between the left and right parts of the | |
| Equation. | |
| """ | |
| variables: dict[str, float] | |
| """Equation variables. The names are the keys, the coefficients are | |
| the values. | |
| """ | |
| bound: float | |
| """Bound of the Equation. Must be non-negative.""" | |
| class EquationAccumulator: | |
| """The EquationAccumulator collects equation Tokens: variables, | |
| coefficients, and bounds. | |
| """ | |
| kind: EquationKind | None = field(default=None) | |
| """Kind of relationship between the left and right parts of the | |
| Equation. | |
| """ | |
| variables: dict[str, float] = field(default_factory=dict) | |
| """Equation variables. The names are the keys, the coefficients are | |
| the values. | |
| """ | |
| bound: float = field(default=0.0) | |
| """Bound of the Equation. Must be non-negative.""" | |
| coefficient: float | None = field(default=None) | |
| """Coefficient of the current variable.""" | |
| variable: str | None = field(default=None) | |
| """Name of the current variable.""" | |
| class Parser: | |
| """Source Parser. It parses the source and returns a list of | |
| Equation objects found in the source. | |
| Args: | |
| source (str): The source to parse. | |
| """ | |
| _lexer: Lexer | |
| """Lexer of the source.""" | |
| _linter: Linter | |
| """Linter of the source.""" | |
| _accumulator: EquationAccumulator | |
| """Equation accumulator.""" | |
| def __init__(self, source: str) -> None: | |
| self._lexer = Lexer(source) | |
| self._linter = Linter(source) | |
| self._accumulator = EquationAccumulator() | |
| def __iter__(self) -> Parser: | |
| """Gets an iterator over the Equations in the source. | |
| Returns: | |
| Parser: _description_ | |
| """ | |
| return self | |
| def __next__(self) -> Equation: | |
| """Gets the next Equation in the source. | |
| Accumulation, processing and validation of Equations in | |
| real-time. The source is parsed sequentially, token by token. | |
| Raises: | |
| StopIteration: The end of the source has been reached. | |
| LexerException: Unexpected character, less than operator is | |
| not allowed. | |
| LexerException: Unexpected character, greater than operator | |
| is not allowed. | |
| LexerException: Invalid character: <code>. | |
| LexerException: Invalid coefficient, unexpected digit after | |
| 0: <code>. | |
| LexerException: Invalid coefficient, expected digit but | |
| got: <code>. | |
| LinterException: Unexpected binary operator, term missed. | |
| LinterException: Equation must contain only one relational | |
| operator. | |
| LinterException: Term must contain no more than one | |
| variable. | |
| LinterException: Unexpected comma at the beginning of the | |
| equation. | |
| LinterException: Unexpected comma, equation missed. | |
| LinterException: Unexpected comma at the end of the | |
| equation. | |
| LinterException: Equation must contain a relational | |
| operator. | |
| Returns: | |
| Equation: The next Equation from the source. | |
| """ | |
| while True: | |
| token = next(self._lexer) | |
| self._linter.lint(token) | |
| match token.kind: | |
| case TokenKind.SOF | TokenKind.MUL: | |
| continue | |
| case TokenKind.EOF: | |
| if token.prev_token.kind == TokenKind.SOF: | |
| raise StopIteration | |
| return self._derive_equation() | |
| case TokenKind.ADD: | |
| self._parse_addition_operator() | |
| continue | |
| case TokenKind.SUB: | |
| self._parse_subtraction_operator() | |
| continue | |
| case TokenKind.EQ | TokenKind.LEQ | TokenKind.GEQ: | |
| self._parse_relational_operator(token) | |
| continue | |
| case TokenKind.COEFFICIENT: | |
| self._parse_coefficient(token) | |
| continue | |
| case TokenKind.VARIABLE: | |
| self._parse_variable(token) | |
| continue | |
| case TokenKind.COMMA: | |
| return self._derive_equation() | |
| def _extend_variables(self) -> None: | |
| """Extend the variables with the current variable and | |
| coefficient. | |
| """ | |
| if self._accumulator.coefficient: | |
| if self._accumulator.kind: | |
| self._accumulator.coefficient *= -1.0 | |
| if self._accumulator.variable: | |
| if self._accumulator.variable in self._accumulator.variables: | |
| self._accumulator.variables[ | |
| self._accumulator.variable | |
| ] += self._accumulator.coefficient | |
| else: | |
| self._accumulator.variables[ | |
| self._accumulator.variable | |
| ] = self._accumulator.coefficient | |
| else: | |
| self._accumulator.bound -= self._accumulator.coefficient | |
| def _derive_equation(self) -> Equation: | |
| """Derive the Equation from the EquationAccumulator. | |
| Returns: | |
| Equation: The derived Equation. | |
| """ | |
| self._extend_variables() | |
| equation = Equation( | |
| self._accumulator.kind, self._accumulator.variables, self._accumulator.bound | |
| ) | |
| self._accumulator = EquationAccumulator() | |
| return equation | |
| def _parse_addition_operator(self) -> None: | |
| """Parse the addition operator Token.""" | |
| self._extend_variables() | |
| self._accumulator.variable = None | |
| self._accumulator.coefficient = 1.0 | |
| def _parse_subtraction_operator(self) -> None: | |
| """Parse the subtraction operator Token.""" | |
| self._extend_variables() | |
| self._accumulator.variable = None | |
| self._accumulator.coefficient = -1.0 | |
| def _parse_relational_operator(self, token: Token) -> None: | |
| """Parse the relational operator Token. | |
| Args: | |
| token (Token): The relational operator Token. | |
| """ | |
| self._extend_variables() | |
| self._accumulator.kind = EquationKind(token.kind.value) | |
| self._accumulator.variable = None | |
| self._accumulator.coefficient = None | |
| def _parse_coefficient(self, token: Token) -> None: | |
| """Parse the coefficient Token. | |
| Args: | |
| token (Token): The coefficient Token. | |
| """ | |
| if not self._accumulator.coefficient: | |
| self._accumulator.coefficient = 1.0 | |
| self._accumulator.coefficient *= float(token.value) | |
| def _parse_variable(self, token: Token) -> None: | |
| """Parse the variable Token. | |
| Args: | |
| token (Token): The variable Token. | |
| """ | |
| if not self._accumulator.coefficient: | |
| self._accumulator.coefficient = 1.0 | |
| self._accumulator.variable = token.value | |
| __all__ = ("EquationKind", "Equation", "Parser") | |