| @@ -5,7 +5,7 @@ frontend into trees that can be used by the database backend. | |||||
| from __future__ import unicode_literals | from __future__ import unicode_literals | ||||
| from re import IGNORECASE, search | from re import IGNORECASE, search | ||||
| from shlex import split | |||||
| from sys import maxsize | |||||
| from dateutil.parser import parse as parse_date | from dateutil.parser import parse as parse_date | ||||
| @@ -146,12 +146,109 @@ class _QueryParser(object): | |||||
| :raises: :py:class:`.QueryParseException` | :raises: :py:class:`.QueryParseException` | ||||
| """ | """ | ||||
| root = None | |||||
| ## TODO: balance tree | |||||
| ## -------------------------------------------------------------------- | |||||
| def SCAN_FOR_MARKER(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_MARKER(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_MARKER(string, " \"'()") | |||||
| if not marker: | |||||
| return [string] | |||||
| before = [string[:index]] if index > 0 else [] | |||||
| after = string[index + 1:] | |||||
| if marker == " ": | |||||
| return before + SPLIT_QUERY_STRING(after, parens) | |||||
| elif marker in ('"', "'"): | |||||
| close_marker, close_index = SCAN_FOR_MARKER(after, marker) | |||||
| if not close_marker: | |||||
| return before + [after] | |||||
| quoted, after = after[:close_index], after[close_index + 1:] | |||||
| return before + [quoted] + SPLIT_QUERY_STRING(after, parens) | |||||
| elif marker == "(": | |||||
| inner = SPLIT_QUERY_STRING(after, True) | |||||
| if inner and isinstance(inner[-1], tuple): | |||||
| after, inner = inner.pop()[0], [inner] if inner else [] | |||||
| return before + inner + SPLIT_QUERY_STRING(after, parens) | |||||
| return before + [inner] | |||||
| elif marker == ")": | |||||
| if parens: | |||||
| return before + [(after,)] | |||||
| return before + SPLIT_QUERY_STRING(after) | |||||
| nest = SPLIT_QUERY_STRING(query.rstrip()) | |||||
| if not nest: | |||||
| raise QueryParseException('Empty query: "%s"' % query) | |||||
| return nest | |||||
| ########### | |||||
| group = _NodeGroup() | |||||
| for term in split(query): | for term in split(query): | ||||
| node = self._parse_term(term) | |||||
| root = BinaryOp(root, BinaryOp.AND, node) if root else node | |||||
| tree = Tree(root) | |||||
| return tree | |||||
| while term.startswith("("): | |||||
| group = _NodeList(group, explicit=True) | |||||
| term = term[1:] | |||||
| closes = 0 | |||||
| while term.endswith(")"): | |||||
| closes += 1 | |||||
| term = term[:-1] | |||||
| if not term: | |||||
| for i in xrange(closes): | |||||
| group = reduce_group(group, explicit=True) | |||||
| continue | |||||
| lcase = term.lower() | |||||
| if lcase == "not": | |||||
| UnaryOp.NOT | |||||
| elif lcase == "or": | |||||
| BinaryOp.OR | |||||
| elif lcase == "and": | |||||
| if group.pending_op: | |||||
| pass | |||||
| else: | |||||
| group.pending_op = BinaryOP.AND | |||||
| else: | |||||
| group.nodes.append(self._parse_term(term)) | |||||
| return Tree(reduce_group(group, explicit=False)) | |||||
| ## -------------------------------------------------------------------- | |||||
| # root = None | |||||
| # for node in reversed(nodes): | |||||
| # root = BinaryOp(node, BinaryOp.AND, root) if root else node | |||||
| # tree = Tree(root) | |||||
| # return tree | |||||
| class _NodeGroup(object): | |||||
| def __init__(self, parent=None): | |||||
| self.parent = parent | |||||
| self.op = None | |||||
| self.left = None | |||||
| self.right = None | |||||
| parse_query = _QueryParser().parse | parse_query = _QueryParser().parse | ||||