| Server IP : 54.37.205.81 / Your IP : 216.73.216.76 Web Server : nginx/1.22.1 System : Linux vps-249481fa 6.1.0-50-cloud-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.176-1 (2026-07-02) x86_64 User : debian ( 1000) PHP Version : 8.2.32 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /lib/python3/dist-packages/lunr/ |
Upload File : |
from lunr.query_lexer import QueryLexer
from lunr.query import Clause, QueryPresence
from lunr.exceptions import QueryParseError
class QueryParser:
def __init__(self, string, query):
self.lexer = QueryLexer(string)
self.query = query
self.current_clause = Clause()
self.lexeme_idx = 0
def parse(self):
self.lexer.run()
self.lexemes = self.lexer.lexemes
state = self.__class__.parse_clause
while state:
state = state(self)
return self.query
def peek_lexeme(self):
try:
return self.lexemes[self.lexeme_idx]
except IndexError:
return None
def consume_lexeme(self):
lexeme = self.peek_lexeme()
self.lexeme_idx += 1
return lexeme
def next_clause(self):
self.query.clause(self.current_clause)
self.current_clause = Clause()
@classmethod
def parse_clause(cls, parser):
lexeme = parser.peek_lexeme()
if lexeme is None:
return
if lexeme["type"] == QueryLexer.FIELD:
return cls.parse_field
elif lexeme["type"] == QueryLexer.TERM:
return cls.parse_term
elif lexeme["type"] == QueryLexer.PRESENCE:
return cls.parse_presence
else:
raise QueryParseError(
"Expected either a field or a term, found {}{}".format(
lexeme["type"],
'with value "' + lexeme["string"] + '"'
if len(lexeme["string"])
else "",
)
)
@classmethod
def parse_field(cls, parser):
lexeme = parser.consume_lexeme()
if lexeme["string"] not in parser.query.all_fields:
raise QueryParseError(
'Unrecognized field "{}", possible fields {}'.format(
lexeme["string"], ", ".join(parser.query.all_fields)
)
)
parser.current_clause.fields = [lexeme["string"]]
next_lexeme = parser.peek_lexeme()
if next_lexeme is None:
raise QueryParseError("Expected term, found nothing")
if next_lexeme["type"] == QueryLexer.TERM:
return cls.parse_term
else:
raise QueryParseError("Expected term, found {}".format(next_lexeme["type"]))
@classmethod
def parse_term(cls, parser):
lexeme = parser.consume_lexeme()
parser.current_clause.term = lexeme["string"].lower()
if "*" in lexeme["string"]:
parser.current_clause.use_pipeline = False
return cls._peek_next_lexeme(parser)
@classmethod
def parse_presence(cls, parser):
lexeme = parser.consume_lexeme()
if lexeme is None:
return
if lexeme["string"] == "-":
parser.current_clause.presence = QueryPresence.PROHIBITED
elif lexeme["string"] == "+":
parser.current_clause.presence = QueryPresence.REQUIRED
else:
raise QueryParseError(
"Unrecognized parser operator: {}, expected `+` or `-`".format(
lexeme.str
)
)
next_lexeme = parser.peek_lexeme()
if next_lexeme is None:
raise QueryParseError("Expected either a field or a term, found nothing")
if next_lexeme["type"] == QueryLexer.FIELD:
return cls.parse_field
elif next_lexeme["type"] == QueryLexer.TERM:
return cls.parse_term
else:
raise QueryParseError(
"Expected either a field or a term, found {}".format(lexeme["type"])
)
@classmethod
def parse_edit_distance(cls, parser):
lexeme = parser.consume_lexeme()
try:
edit_distance = int(lexeme["string"])
except ValueError as e:
raise QueryParseError("Edit distance must be numeric") from e
parser.current_clause.edit_distance = edit_distance
return cls._peek_next_lexeme(parser)
@classmethod
def parse_boost(cls, parser):
lexeme = parser.consume_lexeme()
try:
boost = int(lexeme["string"])
except ValueError as e:
raise QueryParseError("Boost must be numeric") from e
parser.current_clause.boost = boost
return cls._peek_next_lexeme(parser)
@classmethod
def _peek_next_lexeme(cls, parser):
next_lexeme = parser.peek_lexeme()
if next_lexeme is None:
parser.next_clause()
return
if next_lexeme["type"] == QueryLexer.TERM:
parser.next_clause()
return cls.parse_term
elif next_lexeme["type"] == QueryLexer.FIELD:
parser.next_clause()
return cls.parse_field
elif next_lexeme["type"] == QueryLexer.EDIT_DISTANCE:
return cls.parse_edit_distance
elif next_lexeme["type"] == QueryLexer.BOOST:
return cls.parse_boost
elif next_lexeme["type"] == QueryLexer.PRESENCE:
parser.next_clause()
return cls.parse_presence
else:
raise QueryParseError(
"Unexpected lexeme type {}".format(next_lexeme["type"])
)