@@ -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() | ||||