Browse Source

Add sort keys, fix a bug, add tests.

tags/v1.0^2
Ben Kurtovic 10 years ago
parent
commit
7e876c835f
4 changed files with 61 additions and 3 deletions
  1. +2
    -2
      bitshift/query/__init__.py
  2. +34
    -1
      bitshift/query/nodes.py
  3. +4
    -0
      bitshift/query/tree.py
  4. +21
    -0
      test/test_query_parser.py

+ 2
- 2
bitshift/query/__init__.py View File

@@ -223,7 +223,7 @@ class _QueryParser(object):
elif UnaryOp.NOT in nest: elif UnaryOp.NOT in nest:
index = nest.index(UnaryOp.NOT) index = nest.index(UnaryOp.NOT)
right = UnaryOp(UnaryOp.NOT, self._parse_nest(nest[index + 1:])) right = UnaryOp(UnaryOp.NOT, self._parse_nest(nest[index + 1:]))
if index > 1:
if index > 0:
left = self._parse_nest(nest[:index]) left = self._parse_nest(nest[:index])
return BinaryOp(left, BinaryOp.AND, right) return BinaryOp(left, BinaryOp.AND, right)
return right return right
@@ -240,7 +240,7 @@ class _QueryParser(object):
if isinstance(node, BinaryOp): if isinstance(node, BinaryOp):
self._balance_tree(node.left) self._balance_tree(node.left)
self._balance_tree(node.right) self._balance_tree(node.right)
if repr(node.right) < repr(node.left):
if node.right.sortkey() < node.left.sortkey():
node.left, node.right = node.right, node.left node.left, node.right = node.right, node.left
elif isinstance(node, UnaryOp): elif isinstance(node, UnaryOp):
self._balance_tree(node.node) self._balance_tree(node.node)


+ 34
- 1
bitshift/query/nodes.py View File

@@ -10,7 +10,10 @@ class _Node(object):
a :py:class:`~.Language` node represents a constraint where only codelets a :py:class:`~.Language` node represents a constraint where only codelets
of a specific language are selected. of a specific language are selected.
""" """
pass

def sortkey(self):
"""Return a string sort key for the node."""
return ""




class _Literal(object): class _Literal(object):
@@ -33,6 +36,9 @@ class String(_Literal):
def __repr__(self): def __repr__(self):
return "String({0!r})".format(self.string) return "String({0!r})".format(self.string)


def sortkey(self):
return self.string



class Regex(_Literal): class Regex(_Literal):
"""Represents a regular expression literal.""" """Represents a regular expression literal."""
@@ -46,6 +52,9 @@ class Regex(_Literal):
def __repr__(self): def __repr__(self):
return "Regex({0!r})".format(self.regex) return "Regex({0!r})".format(self.regex)


def sortkey(self):
return self.regex



class Text(_Node): class Text(_Node):
"""Represents a text node. """Represents a text node.
@@ -63,6 +72,9 @@ class Text(_Node):
def __repr__(self): def __repr__(self):
return "Text({0})".format(self.text) return "Text({0})".format(self.text)


def sortkey(self):
return self.text.sortkey()



class Language(_Node): class Language(_Node):
"""Represents a language node. """Represents a language node.
@@ -79,6 +91,9 @@ class Language(_Node):
def __repr__(self): def __repr__(self):
return "Language({0})".format(LANGS[self.lang]) return "Language({0})".format(LANGS[self.lang])


def sortkey(self):
return LANGS[self.lang]



class Author(_Node): class Author(_Node):
"""Represents a author node. """Represents a author node.
@@ -87,11 +102,17 @@ class Author(_Node):
""" """


def __init__(self, name): def __init__(self, name):
"""
:type name: :py:class:`_Literal`
"""
self.name = name self.name = name


def __repr__(self): def __repr__(self):
return "Author({0})".format(self.name) return "Author({0})".format(self.name)


def sortkey(self):
return self.name.sortkey()



class Date(_Node): class Date(_Node):
"""Represents a date node. """Represents a date node.
@@ -120,6 +141,9 @@ class Date(_Node):
tm = "Date({0}, {1}, {2})" tm = "Date({0}, {1}, {2})"
return tm.format(types[self.type], relations[self.relation], self.date) return tm.format(types[self.type], relations[self.relation], self.date)


def sortkey(self):
return self.date.strftime("%Y%m%d%H%M%S")



class Symbol(_Node): class Symbol(_Node):
"""Represents a symbol node. """Represents a symbol node.
@@ -144,6 +168,9 @@ class Symbol(_Node):
self.CLASS: "CLASS", self.VARIABLE: "VARIABLE"} self.CLASS: "CLASS", self.VARIABLE: "VARIABLE"}
return "Symbol({0}, {1})".format(types[self.type], self.name) return "Symbol({0}, {1})".format(types[self.type], self.name)


def sortkey(self):
return self.name.sortkey()



class BinaryOp(_Node): class BinaryOp(_Node):
"""Represents a relationship between two nodes: ``and``, ``or``.""" """Represents a relationship between two nodes: ``and``, ``or``."""
@@ -160,6 +187,9 @@ class BinaryOp(_Node):
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, ops[self.op], self.right)


def sortkey(self):
return self.left.sortkey() + self.right.sortkey()



class UnaryOp(_Node): class UnaryOp(_Node):
"""Represents a transformation applied to one node: ``not``.""" """Represents a transformation applied to one node: ``not``."""
@@ -172,3 +202,6 @@ class UnaryOp(_Node):
def __repr__(self): def __repr__(self):
ops = {self.NOT: "NOT"} ops = {self.NOT: "NOT"}
return "UnaryOp({0}, {1})".format(ops[self.op], self.node) return "UnaryOp({0}, {1})".format(ops[self.op], self.node)

def sortkey(self):
return self.node.sortkey()

+ 4
- 0
bitshift/query/tree.py View File

@@ -9,6 +9,10 @@ class Tree(object):
def __repr__(self): def __repr__(self):
return "Tree({0})".format(self._root) return "Tree({0})".format(self._root)


def sortkey(self):
"""Return a string sort key for the query tree."""
return self._root.sortkey()

def serialize(self): def serialize(self):
"""Create a string representation of the query for caching. """Create a string representation of the query for caching.




+ 21
- 0
test/test_query_parser.py View File

@@ -31,6 +31,27 @@ TESTS = [
("class:FooBar", "Tree(Symbol(CLASS, String(u'FooBar')))"), ("class:FooBar", "Tree(Symbol(CLASS, String(u'FooBar')))"),
("var:foobar", "Tree(Symbol(VARIABLE, String(u'foobar')))"), ("var:foobar", "Tree(Symbol(VARIABLE, String(u'foobar')))"),
("var:r:foobar", "Tree(Symbol(VARIABLE, Regex(u'foobar')))"), ("var:r:foobar", "Tree(Symbol(VARIABLE, Regex(u'foobar')))"),

# Composition
("(a and b) or (c and d)", ", ".join([
"Tree(BinaryOp(BinaryOp(Text(String(u'a'))", "AND",
"Text(String(u'b')))", "OR", "BinaryOp(Text(String(u'c'))", "AND",
"Text(String(u'd')))))"])),
("a and b or c and d", ", ".join([
"Tree(BinaryOp(BinaryOp(Text(String(u'a'))", "AND",
"Text(String(u'b')))", "OR", "BinaryOp(Text(String(u'c'))", "AND",
"Text(String(u'd')))))"])),
("a and b or c or d", ", ".join([
"Tree(BinaryOp(BinaryOp(Text(String(u'a'))", "AND",
"Text(String(u'b')))", "OR", "BinaryOp(Text(String(u'c'))", "OR",
"Text(String(u'd')))))"])),
("a and (b or c or d)", ", ".join([
"Tree(BinaryOp(Text(String(u'a'))", "AND",
"BinaryOp(Text(String(u'b'))", "OR", "BinaryOp(Text(String(u'c'))", "OR",
"Text(String(u'd'))))))"])),
("a not b", ", ".join([
"Tree(BinaryOp(Text(String(u'a'))", "AND", "UnaryOp(NOT",
"Text(String(u'b')))))"])),
] ]


class TestQueryParser(unittest.TestCase): class TestQueryParser(unittest.TestCase):


Loading…
Cancel
Save