from ast_parser.errors import LinterException from ast_parser.token import ( Token, TokenKind, is_binary_operator, is_relational_operator, ) class Linter: """Linter enables real-time validation of the token chain. Args: source (str): The source string being tokenized. """ _source: str _variable_provided: bool _relation_provided: bool def __init__(self, source: str) -> None: self._source = source self._variable_provided = False self._relation_provided = False def lint(self, token: Token) -> None: """The lint method checks the token for integrity, validity and relevance. It raises a LinterException if the token is invalid or unexpected. Args: token (Token): The starting token. source (str): The source string being tokenized. Used for exception messages. Raises: 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. """ if token.kind != TokenKind.SOF and ( token.prev_token is None or token.prev_token.next_token is not token ): raise LinterException( self._source, token.location, "Crude modification of the token chain is detected", ) if token.kind == TokenKind.EOF: self._lint_eof(token) if is_binary_operator(token): self._lint_binary_operator(token) self._variable_provided = False if token.kind == TokenKind.MUL: self._lint_multiplication_operator(token) if is_relational_operator(token): self._lint_relational_operator(token) self._variable_provided = False self._relation_provided = True if token.kind == TokenKind.VARIABLE: self._lint_variable(token) self._variable_provided = True if token.kind == TokenKind.COMMA: self._lint_comma(token) self._variable_provided = False self._relation_provided = False def _lint_eof(self, token: Token) -> None: """Lint the EOF Token. Args: token (Token): The EOF Token. Raises: LinterException: Equation must contain a relational operator. LinterException: Unexpected binary operator at the end of the equation. LinterException: Unexpected EOF, right side of the equation is missed. LinterException: Unexpected comma at the end of the equation. """ if token.prev_token.kind != TokenKind.SOF and not self._relation_provided: raise LinterException( self._source, token.location, "Equation must contain a relational operator", ) if is_binary_operator(token.prev_token): raise LinterException( self._source, token.prev_token.location, "Unexpected binary operator at the end of the equation", ) if token.prev_token.kind == TokenKind.MUL: raise LinterException( self._source, token.location, "Unexpected EOF, multiplier missed", ) if is_relational_operator(token.prev_token): raise LinterException( self._source, token.location, "Unexpected EOF, right side of the equation is missed", ) if token.prev_token.kind == TokenKind.COMMA: raise LinterException( self._source, token.location, "Unexpected comma at the end of the equation", ) def _lint_binary_operator(self, token: Token) -> None: """Lint the binary operator Token. Args: token (Token): The binary operator Token. Raises: LinterException: Unexpected binary operator, term missed. """ if is_binary_operator(token.prev_token): raise LinterException( self._source, token.location, "Unexpected binary operator, term missed", ) if token.prev_token.kind == TokenKind.MUL: raise LinterException( self._source, token.location, "Unexpected binary operator, multiplier missed", ) def _lint_multiplication_operator(self, token: Token) -> None: """Lint the multiplication operator Token. Args: token (Token): The multiplication operator Token. Raises: LinterException: Unexpected multiplication operator, term missed. """ if ( is_binary_operator(token.prev_token) or is_relational_operator(token.prev_token) or token.prev_token.kind == TokenKind.COMMA ): raise LinterException( self._source, token.location, "Unexpected multiplication operator, term missed", ) if token.prev_token.kind == TokenKind.MUL: raise LinterException( self._source, token.location, "Unexpected multiplication operator, multiplier missed", ) def _lint_relational_operator(self, token: Token) -> None: """Lint the relational operator Token. Args: token (Token): The relational operator Token. Raises: LinterException: Equation must contain only one relational operator. LinterException: Unexpected binary operator, term missed. LinterException: Unexpected relational operator, left side of the equation is missed. """ if token.prev_token.kind in (TokenKind.SOF, TokenKind.COMMA): raise LinterException( self._source, token.location, "Unexpected relational operator, left side of the equation is missed", ) if self._relation_provided: raise LinterException( self._source, token.location, "Equation must contain only one relational operator", ) if is_binary_operator(token.prev_token): raise LinterException( self._source, token.location, "Unexpected binary operator, term missed", ) if token.prev_token.kind == TokenKind.MUL: raise LinterException( self._source, token.location, "Unexpected relational operator, multiplier missed", ) def _lint_variable(self, token: Token) -> None: """Lint the variable Token. Args: token (Token): The variable Token. Raises: LinterException: Term must contain no more than one variable. """ if self._variable_provided: raise LinterException( self._source, token.location, "Term must contain no more than one variable", ) def _lint_comma(self, token: Token) -> None: """Lint the comma Token. Args: token (Token): The comma Token. Raises: LinterException: Unexpected comma at the beginning of the equation. LinterException: Unexpected comma, equation missed. LinterException: Unexpected comma at the end of the equation. """ if token.prev_token.kind == TokenKind.SOF: raise LinterException( self._source, token.location, "Unexpected comma at the beginning of the equation", ) if not self._relation_provided: raise LinterException( self._source, token.location, "Equation must contain a relational operator", ) if is_binary_operator(token.prev_token): raise LinterException( self._source, token.prev_token, "Unexpected binary operator at the end of the equation", ) if token.prev_token.kind == TokenKind.MUL: raise LinterException( self._source, token.location, "Unexpected comma, multiplier missed", ) if is_relational_operator(token.prev_token): raise LinterException( self._source, token.location, "Unexpected comma, right side of the equation is missed", ) if token.prev_token.kind == TokenKind.COMMA: raise LinterException( self._source, token.location, "Unexpected comma, equation missed", ) __all__ = ("Linter",)