Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/tagstudio/core/library/alchemy/visitors.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from typing import TYPE_CHECKING, override

import structlog
from sqlalchemy import ColumnElement, and_, distinct, func, or_, select
from sqlalchemy import ColumnElement, and_, distinct, false, func, or_, select, true
from sqlalchemy.orm import Session
from sqlalchemy.sql.operators import ilike_op

Expand All @@ -18,6 +18,7 @@
AST,
ANDList,
BaseVisitor,
Boolean,
Constraint,
ConstraintType,
Not,
Expand Down Expand Up @@ -116,6 +117,12 @@ def visit_constraint(self, node: Constraint) -> ColumnElement[bool]: # type: ig
def visit_property(self, node: Property) -> ColumnElement[bool]: # type: ignore
raise NotImplementedError("This should never be reached!")

def visit_boolean(self, node: Boolean) -> ColumnElement[bool]:
if node.value:
return true()
else:
return false()

@override
def visit_not(self, node: Not) -> ColumnElement[bool]: # type: ignore
return ~self.visit(node.child)
Expand Down
12 changes: 12 additions & 0 deletions src/tagstudio/core/query_lang/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ def __init__(self, child: AST) -> None:
super().__init__()
self.child = child

class Boolean(AST):
value: bool

def __init__(self, value: bool) -> None:
super().__init__()
self.value = value

T = TypeVar("T")

Expand All @@ -110,6 +116,8 @@ def visit(self, node: AST) -> T:
return self.visit_property(node)
elif isinstance(node, Not):
return self.visit_not(node)
elif isinstance(node, Boolean):
return self.visit_boolean(node)
raise Exception(f"Unknown Node Type of {node}") # pragma: nocover

@abstractmethod
Expand All @@ -131,3 +139,7 @@ def visit_property(self, node: Property) -> T:
@abstractmethod
def visit_not(self, node: Not) -> T:
raise NotImplementedError() # pragma: nocover

@abstractmethod
def visit_boolean(self, node: Boolean) -> T:
raise NotImplementedError() # pragma: nocover
19 changes: 19 additions & 0 deletions src/tagstudio/core/query_lang/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from tagstudio.core.query_lang.ast import (
AST,
ANDList,
Boolean,
Constraint,
ConstraintType,
Not,
Expand Down Expand Up @@ -81,6 +82,12 @@ def __term(self) -> AST:
if isinstance(term, Not): # instead of Not(Not(child)) return child
return term.child
return Not(term)
if self.__is_next_true():
self.__eat(TokenType.ULITERAL)
return Boolean(value = True)
if self.__is_next_false():
self.__eat(TokenType.ULITERAL)
return Boolean(value = False)
if self.next_token.type == TokenType.RBRACKETO:
self.__eat(TokenType.RBRACKETO)
out = self.__or_list()
Expand All @@ -92,6 +99,18 @@ def __term(self) -> AST:
def __is_next_not(self) -> bool:
return self.next_token.type == TokenType.ULITERAL and self.next_token.value.upper() == "NOT" # pyright: ignore

def __is_next_true(self) -> bool:
return (
self.next_token.type == TokenType.ULITERAL
and self.next_token.value.upper() == "TRUE" # pyright: ignore
)

def __is_next_false(self) -> bool:
return (
self.next_token.type == TokenType.ULITERAL
and self.next_token.value.upper() == "TRUE" # pyright: ignore
)

def __constraint(self) -> Constraint:
if self.next_token.type == TokenType.CONSTRAINTTYPE:
constraint = self.__eat(TokenType.CONSTRAINTTYPE).value
Expand Down