| @@ -151,7 +151,11 @@ class _QueryParser(object): | |||||
| return best_marker, best_index | return best_marker, best_index | ||||
| def _split_query(self, query, parens=False): | def _split_query(self, query, parens=False): | ||||
| """Split a query string into a nested list of query terms.""" | |||||
| """Split a query string into a nested list of query terms. | |||||
| Returns a list of terms and/or nested sublists of terms. Each term and | |||||
| sublist is guarenteed to be non-empty. | |||||
| """ | |||||
| query = query.lstrip() | query = query.lstrip() | ||||
| if not query: | if not query: | ||||
| return [] | return [] | ||||
| @@ -187,6 +191,63 @@ class _QueryParser(object): | |||||
| nest += self._split_query(after) | nest += self._split_query(after) | ||||
| return nest | return nest | ||||
| def _parse_nest(self, nest): | |||||
| """Recursively parse a nested list of search query terms.""" | |||||
| class _NodeGroup(object): | |||||
| def __init__(self, left=None, op=None, right=None): | |||||
| self.left = left or [] | |||||
| self.op = op | |||||
| self.right = right or [] | |||||
| def append(self, node): | |||||
| self.right.append(node) if self.op else self.left.append(node) | |||||
| " a AND b OR c AND d NOT e f" | |||||
| "((a) AND (b)) OR ((c) AND (d (NOT (e f))))" | |||||
| "((a) AND (b)) OR ((c) AND (d" | |||||
| group = _NodeGroup() | |||||
| for term in nest: | |||||
| if isinstance(term, list): | |||||
| group.append(self._parse_nest(term)) | |||||
| else: | |||||
| lcase = term.lower() | |||||
| if lcase == "not": | |||||
| if group.op: | |||||
| group.append(_NodeGroup(None, UnaryOp.NOT) ## TODO | |||||
| else: | |||||
| pass | |||||
| elif lcase == "or": | |||||
| if group.op == BinaryOp.AND: | |||||
| group = _NodeGroup(group, BinaryOp.OR) | |||||
| elif group.op == BinaryOp.OR: | |||||
| pass | |||||
| elif group.op == UnaryOp.NOT: | |||||
| pass | |||||
| else: | |||||
| group.op = BinaryOp.OR | |||||
| elif lcase == "and": | |||||
| if group.op == BinaryOp.OR: | |||||
| group.right = _NodeGroup(group.right, BinaryOp.AND) | |||||
| elif group.op == BinaryOp.AND: | |||||
| pass | |||||
| elif group.op == UnaryOp.NOT: | |||||
| pass | |||||
| else: | |||||
| group.op = BinaryOP.AND | |||||
| else: | |||||
| group.append(self._parse_term(term)) | |||||
| def parse(self, query): | def parse(self, query): | ||||
| """ | """ | ||||
| Parse a search query. | Parse a search query. | ||||
| @@ -204,63 +265,10 @@ class _QueryParser(object): | |||||
| :raises: :py:class:`.QueryParseException` | :raises: :py:class:`.QueryParseException` | ||||
| """ | """ | ||||
| ## TODO: balance tree | ## TODO: balance tree | ||||
| ## -------------------------------------------------------------------- | |||||
| nest = self._split_query(query.rstrip()) | nest = self._split_query(query.rstrip()) | ||||
| if not nest: | if not nest: | ||||
| raise QueryParseException('Empty query: "%s"' % query) | raise QueryParseException('Empty query: "%s"' % query) | ||||
| return nest | |||||
| ########### | |||||
| group = _NodeGroup() | |||||
| for term in split(query): | |||||
| 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 | |||||
| return Tree(self._parse_nest(nest)) | |||||
| parse_query = _QueryParser().parse | parse_query = _QueryParser().parse | ||||