| @@ -209,6 +209,9 @@ class _QueryParser(object): | |||||
| def parse_binary_op(op): | def parse_binary_op(op): | ||||
| """Parse a binary operator in a nested query list.""" | """Parse a binary operator in a nested query list.""" | ||||
| index = nest.index(op) | index = nest.index(op) | ||||
| if index == 0 or index == len(nest) - 1: | |||||
| err = "Invalid query: '%s' given without argument." | |||||
| raise QueryParseException(err % BinaryOp.OPS[op]) | |||||
| left = self._parse_nest(nest[:index]) | left = self._parse_nest(nest[:index]) | ||||
| right = self._parse_nest(nest[index + 1:]) | right = self._parse_nest(nest[index + 1:]) | ||||
| return BinaryOp(left, op, right) | return BinaryOp(left, op, right) | ||||
| @@ -222,6 +225,9 @@ class _QueryParser(object): | |||||
| return parse_binary_op(BinaryOp.AND) | return parse_binary_op(BinaryOp.AND) | ||||
| elif UnaryOp.NOT in nest: | elif UnaryOp.NOT in nest: | ||||
| index = nest.index(UnaryOp.NOT) | index = nest.index(UnaryOp.NOT) | ||||
| if index == len(nest) - 1: | |||||
| err = "Invalid query: '%s' given without argument." | |||||
| raise QueryParseException(err % UnaryOp.OPS[op]) | |||||
| right = UnaryOp(UnaryOp.NOT, self._parse_nest(nest[index + 1:])) | right = UnaryOp(UnaryOp.NOT, self._parse_nest(nest[index + 1:])) | ||||
| if index > 0: | if index > 0: | ||||
| left = self._parse_nest(nest[:index]) | left = self._parse_nest(nest[:index]) | ||||
| @@ -176,6 +176,7 @@ class BinaryOp(_Node): | |||||
| """Represents a relationship between two nodes: ``and``, ``or``.""" | """Represents a relationship between two nodes: ``and``, ``or``.""" | ||||
| AND = object() | AND = object() | ||||
| OR = object() | OR = object() | ||||
| OPS = {AND: "AND", OR: "OR"} | |||||
| def __init__(self, left, op, right): | def __init__(self, left, op, right): | ||||
| self.left = left | self.left = left | ||||
| @@ -183,9 +184,8 @@ class BinaryOp(_Node): | |||||
| self.right = right | self.right = right | ||||
| def __repr__(self): | def __repr__(self): | ||||
| ops = {self.AND: "AND", self.OR: "OR"} | |||||
| tmpl = "BinaryOp({0}, {1}, {2})" | tmpl = "BinaryOp({0}, {1}, {2})" | ||||
| return tmpl.format(self.left, ops[self.op], self.right) | |||||
| return tmpl.format(self.left, self.OPS[self.op], self.right) | |||||
| def sortkey(self): | def sortkey(self): | ||||
| return self.left.sortkey() + self.right.sortkey() | return self.left.sortkey() + self.right.sortkey() | ||||
| @@ -194,14 +194,14 @@ class BinaryOp(_Node): | |||||
| class UnaryOp(_Node): | class UnaryOp(_Node): | ||||
| """Represents a transformation applied to one node: ``not``.""" | """Represents a transformation applied to one node: ``not``.""" | ||||
| NOT = object() | NOT = object() | ||||
| OPS = {NOT: "NOT"} | |||||
| def __init__(self, op, node): | def __init__(self, op, node): | ||||
| self.op = op | self.op = op | ||||
| self.node = node | self.node = node | ||||
| def __repr__(self): | def __repr__(self): | ||||
| ops = {self.NOT: "NOT"} | |||||
| return "UnaryOp({0}, {1})".format(ops[self.op], self.node) | |||||
| return "UnaryOp({0}, {1})".format(self.OPS[self.op], self.node) | |||||
| def sortkey(self): | def sortkey(self): | ||||
| return self.node.sortkey() | return self.node.sortkey() | ||||
| @@ -9,6 +9,11 @@ class Tree(object): | |||||
| def __repr__(self): | def __repr__(self): | ||||
| return "Tree({0})".format(self._root) | return "Tree({0})".format(self._root) | ||||
| @property | |||||
| def root(self): | |||||
| """The root node of the tree.""" | |||||
| return self._root | |||||
| def sortkey(self): | def sortkey(self): | ||||
| """Return a string sort key for the query tree.""" | """Return a string sort key for the query tree.""" | ||||
| return self._root.sortkey() | return self._root.sortkey() | ||||