mirror of
https://github.com/TagStudioDev/TagStudio.git
synced 2026-02-02 16:19:10 +00:00
refactor: addresss type hints in query_lang
This commit is contained in:
@@ -1,6 +1,11 @@
|
||||
# Copyright (C) 2025
|
||||
# Licensed under the GPL-3.0 License.
|
||||
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio
|
||||
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from enum import Enum
|
||||
from typing import Generic, TypeVar, Union
|
||||
from typing import Generic, TypeVar, override
|
||||
|
||||
|
||||
class ConstraintType(Enum):
|
||||
@@ -12,7 +17,7 @@ class ConstraintType(Enum):
|
||||
Special = 5
|
||||
|
||||
@staticmethod
|
||||
def from_string(text: str) -> Union["ConstraintType", None]:
|
||||
def from_string(text: str) -> "ConstraintType | None":
|
||||
return {
|
||||
"tag": ConstraintType.Tag,
|
||||
"tag_id": ConstraintType.TagID,
|
||||
@@ -24,14 +29,16 @@ class ConstraintType(Enum):
|
||||
|
||||
|
||||
class AST:
|
||||
parent: Union["AST", None] = None
|
||||
parent: "AST | None" = None
|
||||
|
||||
@override
|
||||
def __str__(self):
|
||||
class_name = self.__class__.__name__
|
||||
fields = vars(self) # Get all instance variables as a dictionary
|
||||
field_str = ", ".join(f"{key}={value}" for key, value in fields.items())
|
||||
return f"{class_name}({field_str})"
|
||||
|
||||
@override
|
||||
def __repr__(self) -> str:
|
||||
return self.__str__()
|
||||
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
# Copyright (C) 2025
|
||||
# Licensed under the GPL-3.0 License.
|
||||
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio
|
||||
|
||||
|
||||
from tagstudio.core.query_lang.ast import (
|
||||
AST,
|
||||
ANDList,
|
||||
@@ -27,7 +32,7 @@ class Parser:
|
||||
if self.next_token.type == TokenType.EOF:
|
||||
return ORList([])
|
||||
out = self.__or_list()
|
||||
if self.next_token.type != TokenType.EOF:
|
||||
if self.next_token.type != TokenType.EOF: # pyright: ignore[reportUnnecessaryComparison]
|
||||
raise ParsingError(self.next_token.start, self.next_token.end, "Syntax Error")
|
||||
return out
|
||||
|
||||
@@ -41,7 +46,7 @@ class Parser:
|
||||
return ORList(terms) if len(terms) > 1 else terms[0]
|
||||
|
||||
def __is_next_or(self) -> bool:
|
||||
return self.next_token.type == TokenType.ULITERAL and self.next_token.value.upper() == "OR"
|
||||
return self.next_token.type == TokenType.ULITERAL and self.next_token.value.upper() == "OR" # pyright: ignore
|
||||
|
||||
def __and_list(self) -> AST:
|
||||
elements = [self.__term()]
|
||||
@@ -67,7 +72,7 @@ class Parser:
|
||||
raise self.__syntax_error("Unexpected AND")
|
||||
|
||||
def __is_next_and(self) -> bool:
|
||||
return self.next_token.type == TokenType.ULITERAL and self.next_token.value.upper() == "AND"
|
||||
return self.next_token.type == TokenType.ULITERAL and self.next_token.value.upper() == "AND" # pyright: ignore
|
||||
|
||||
def __term(self) -> AST:
|
||||
if self.__is_next_not():
|
||||
@@ -85,11 +90,14 @@ class Parser:
|
||||
return self.__constraint()
|
||||
|
||||
def __is_next_not(self) -> bool:
|
||||
return self.next_token.type == TokenType.ULITERAL and self.next_token.value.upper() == "NOT"
|
||||
return self.next_token.type == TokenType.ULITERAL and self.next_token.value.upper() == "NOT" # pyright: ignore
|
||||
|
||||
def __constraint(self) -> Constraint:
|
||||
if self.next_token.type == TokenType.CONSTRAINTTYPE:
|
||||
self.last_constraint_type = self.__eat(TokenType.CONSTRAINTTYPE).value
|
||||
constraint = self.__eat(TokenType.CONSTRAINTTYPE).value
|
||||
if not isinstance(constraint, ConstraintType):
|
||||
raise self.__syntax_error()
|
||||
self.last_constraint_type = constraint
|
||||
|
||||
value = self.__literal()
|
||||
|
||||
@@ -98,7 +106,7 @@ class Parser:
|
||||
self.__eat(TokenType.SBRACKETO)
|
||||
properties.append(self.__property())
|
||||
|
||||
while self.next_token.type == TokenType.COMMA:
|
||||
while self.next_token.type == TokenType.COMMA: # pyright: ignore[reportUnnecessaryComparison]
|
||||
self.__eat(TokenType.COMMA)
|
||||
properties.append(self.__property())
|
||||
|
||||
@@ -110,11 +118,16 @@ class Parser:
|
||||
key = self.__eat(TokenType.ULITERAL).value
|
||||
self.__eat(TokenType.EQUALS)
|
||||
value = self.__literal()
|
||||
if not isinstance(key, str):
|
||||
raise self.__syntax_error()
|
||||
return Property(key, value)
|
||||
|
||||
def __literal(self) -> str:
|
||||
if self.next_token.type in [TokenType.QLITERAL, TokenType.ULITERAL]:
|
||||
return self.__eat(self.next_token.type).value
|
||||
literal = self.__eat(self.next_token.type).value
|
||||
if not isinstance(literal, str):
|
||||
raise self.__syntax_error()
|
||||
return literal
|
||||
raise self.__syntax_error()
|
||||
|
||||
def __eat(self, type: TokenType) -> Token:
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# Copyright (C) 2025
|
||||
# Licensed under the GPL-3.0 License.
|
||||
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio
|
||||
|
||||
|
||||
from enum import Enum
|
||||
from typing import Any
|
||||
from typing import override
|
||||
|
||||
from tagstudio.core.query_lang.ast import ConstraintType
|
||||
from tagstudio.core.query_lang.util import ParsingError
|
||||
@@ -21,12 +26,14 @@ class TokenType(Enum):
|
||||
|
||||
class Token:
|
||||
type: TokenType
|
||||
value: Any
|
||||
value: str | ConstraintType | None
|
||||
|
||||
start: int
|
||||
end: int
|
||||
|
||||
def __init__(self, type: TokenType, value: Any, start: int, end: int) -> None:
|
||||
def __init__(
|
||||
self, type: TokenType, value: str | ConstraintType | None, start: int, end: int
|
||||
) -> None:
|
||||
self.type = type
|
||||
self.value = value
|
||||
self.start = start
|
||||
@@ -40,9 +47,11 @@ class Token:
|
||||
def EOF(pos: int) -> "Token": # noqa: N802
|
||||
return Token.from_type(TokenType.EOF, pos)
|
||||
|
||||
@override
|
||||
def __str__(self) -> str:
|
||||
return f"Token({self.type}, {self.value}, {self.start}, {self.end})" # pragma: nocover
|
||||
|
||||
@override
|
||||
def __repr__(self) -> str:
|
||||
return self.__str__() # pragma: nocover
|
||||
|
||||
|
||||
@@ -1,15 +1,26 @@
|
||||
# Copyright (C) 2025
|
||||
# Licensed under the GPL-3.0 License.
|
||||
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio
|
||||
|
||||
|
||||
from typing import override
|
||||
|
||||
|
||||
class ParsingError(BaseException):
|
||||
start: int
|
||||
end: int
|
||||
msg: str
|
||||
|
||||
def __init__(self, start: int, end: int, msg: str = "Syntax Error") -> None:
|
||||
super().__init__()
|
||||
self.start = start
|
||||
self.end = end
|
||||
self.msg = msg
|
||||
|
||||
@override
|
||||
def __str__(self) -> str:
|
||||
return f"Syntax Error {self.start}->{self.end}: {self.msg}" # pragma: nocover
|
||||
|
||||
@override
|
||||
def __repr__(self) -> str:
|
||||
return self.__str__() # pragma: nocover
|
||||
|
||||
Reference in New Issue
Block a user