+
+def _test_post(post, tree):
+ """Test if the list of tags in the post satisfies condition
+
+ Recursively evaluate the tree against the list of tags in the post.
+
+ """
+ op = tree[0]
+ if op == 'NAME':
+ tag = tree[1]
+ assert isinstance(tag, str)
+ _tags = post[3]
+ return tag in _tags
+ elif op in ('AND', 'OR'):
+ value1 = _test_post(post, tree[1])
+ value2 = _test_post(post, tree[2])
+ if op == 'AND':
+ return value1 and value2
+ if op == 'OR':
+ return value1 or value2
+ elif op == 'NOT':
+ return not _test_post(post, tree[1])
+ elif op == 'PARENS':
+ return _test_post(post, tree[1])
+ else:
+ raise ValueError("Cannot get there")
+
+def find_tags(tree):
+ """Test every blog post against parsed expression
+
+ Return all posts that passed the test.
+
+ """
+ _posts = []
+ for (year, month, day), posts in blog_dict.iteritems():
+ for post in posts:
+ if _test_post(post, tree):
+ _posts.append((
+ year, month, day,
+ '/'.join((year, month, day, post[0][:-len("tmpl")] + "html")),
+ post[1]))
+ _posts.sort(reverse=True)
+ return _posts