Browse Source

Finish _scan_query(), _split_query().

tags/v1.0^2
Ben Kurtovic 10 years ago
parent
commit
bcab13f027
1 changed files with 58 additions and 54 deletions
  1. +58
    -54
      bitshift/query/__init__.py

+ 58
- 54
bitshift/query/__init__.py View File

@@ -130,6 +130,63 @@ class _QueryParser(object):
return meth(arg)
return Text(self._parse_literal(term))

def _scan_query(self, query, markers):
"""Scan a query (sub)string for the first occurance of some markers.

Returns a 2-tuple of (first_marker_found, marker_index).
"""
def _is_escaped(query, index):
"""Return whether a query marker is backslash-escaped."""
return (index > 0 and query[index - 1] == "\\" and
(index < 2 or query[index - 2] != "\\"))

best_marker, best_index = None, maxsize
for marker in markers:
index = query.find(marker)
if _is_escaped(query, index):
_, new_index = self._scan_query(query[index + 1:], marker)
index += new_index + 1
if index >= 0 and index < best_index:
best_marker, best_index = marker, index
return best_marker, best_index

def _split_query(self, query, parens=False):
"""Split a query string into a nested list of query terms."""
query = query.lstrip()
if not query:
return []
marker, index = self._scan_query(query, " \"'()")
if not marker:
return [query]
nest = [query[:index]] if index > 0 else []
after = query[index + 1:]

if marker == " ":
nest += self._split_query(after, parens)
elif marker in ('"', "'"):
close_marker, close_index = self._scan_query(after, marker)
if close_marker:
if close_index > 0:
nest.append(after[:close_index])
after = after[close_index + 1:]
nest += self._split_query(after, parens)
elif after:
nest.append(after)
elif marker == "(":
inner, after = self._split_query(after, True), []
if inner and isinstance(inner[-1], tuple):
after = self._split_query(inner.pop()[0], parens)
if inner:
nest.append(inner)
if after:
nest += after
elif marker == ")":
if parens:
nest.append((after,))
else:
nest += self._split_query(after)
return nest

def parse(self, query):
"""
Parse a search query.
@@ -149,60 +206,7 @@ class _QueryParser(object):
## TODO: balance tree
## --------------------------------------------------------------------

def SCAN_FOR_MARKERS(string, markers):
best_marker, best_index = None, maxsize
for marker in markers:
index = string.find(marker)
if index > 0 and string[index - 1] == "\\" and (index == 1 or string[index - 2] != "\\"):
_, new_index = SCAN_FOR_MARKERS(string[index + 1:], marker)
index += new_index + 1
if index >= 0 and index < best_index:
best_marker, best_index = marker, index
return best_marker, best_index

def SPLIT_QUERY_STRING(string, parens=False):
string = string.lstrip()
if not string:
return []
marker, index = SCAN_FOR_MARKERS(string, " \"'()")

if not marker:
return [string]

nest = [string[:index]] if index > 0 else []
after = string[index + 1:]

if marker == " ":
nest += SPLIT_QUERY_STRING(after, parens)

elif marker in ('"', "'"):
close_marker, close_index = SCAN_FOR_MARKERS(after, marker)
if close_marker:
if close_index > 0:
nest.append(after[:close_index])
after = after[close_index + 1:]
nest += SPLIT_QUERY_STRING(after, parens)
elif after:
nest.append(after)

elif marker == "(":
inner, after = SPLIT_QUERY_STRING(after, True), []
if inner and isinstance(inner[-1], tuple):
after = SPLIT_QUERY_STRING(inner.pop()[0], parens)
if inner:
nest.append(inner)
if after:
nest += after

elif marker == ")":
if parens:
nest.append((after,))
else:
nest += SPLIT_QUERY_STRING(after)

return nest

nest = SPLIT_QUERY_STRING(query.rstrip())
nest = self._split_query(query.rstrip())
if not nest:
raise QueryParseException('Empty query: "%s"' % query)



Loading…
Cancel
Save