source: tagsplugin/tags/0.5/tractags/expr.py

Last change on this file was 2923, checked in by Alec Thomas, 16 years ago

Whoops. UTF-8 fix.

File size: 3.0 KB
Line 
1import compiler
2import operator
3import parser
4import re
5from trac.core import TracError
6from trac.util.text import to_unicode
7
8try:
9    set = set
10except:
11    from sets import Set as set
12
13class Expression:
14    """ Pass a set of tags through a basic expression filter.
15
16        Expressions are valid, basic Python expressions.
17
18        eg. Match all objects tagged with ticket and workflow, and not tagged
19        with wiki or closed.
20
21            (ticket and workflow) and not (wiki or closed)
22        """
23    __slots__ = ['ast', 'expression']
24    __visitors = {}
25
26    def __init__(self, expression):
27        self.expression = expression.encode('utf-8')
28        self.ast = compiler.parse(self.expression, 'eval')
29
30    def get_tags(self):
31        """ Fetch tags used in this expression. """
32        tags = set()
33        def walk(node):
34            if node[0] == 1:
35                tags.add(node[1])
36            elif node[0] == 3:
37                tags.add(eval(node[1]))
38            else:
39                for i in range(1, len(node)):
40                    if isinstance(node[i], tuple):
41                        walk(node[i])
42        tree = parser.expr(self.expression).totuple()
43        walk(tree)
44        return tags
45
46    def __call__(self, context):
47        context = [tag.encode('utf-8') for tag in context] 
48        return self._visit(self.ast, context)
49
50    def _visit(self, node, context):
51        v = self.__visitors.get(node.__class__)
52        if not v:
53            try:
54                v = getattr(self, '_visit_%s' % node.__class__.__name__.lower())
55            except AttributeError:
56                raise TracError('invalid expression node "%s"' % node.__class__.__name__.lower()) 
57            self.__visitors[node.__class__] = v
58        return v(node, context)
59
60    def _visit_expression(self, node, context):
61        for child in node.getChildNodes():
62            return self._visit(child, context)
63
64    def _visit_sub(self, node, context):
65        return self._visit(node.left, context) \
66               and not self._visit(node.right, context)
67
68    def _visit_not(self, node, context):
69        return not self._visit(node.expr, context)
70
71    def _visit_and(self, node, context):
72        result = True
73        for arg in node.nodes:
74            result = result and self._visit(arg, context)
75            if not result:
76                return False
77        return True
78
79    _visit_add = _visit_and
80
81    def _visit_or(self, node, context):
82        result = False
83        for arg in node.nodes:
84            result = result or self._visit(arg, context)
85            if result:
86                return True
87        return False
88
89    _visit_bitor = _visit_or
90
91    def _visit_name(self, node, context):
92        return node.name in context
93
94    def _visit_const(self, node, context):
95        return node.value in context
96
97    def _visit_unarysub(self, node, context):
98        return not self._visit(node.expr, context)
99
100    def _visit_tuple(self, node, context):
101        return reduce(operator.and_, [self._visit(n, context) for n in node.nodes])
Note: See TracBrowser for help on using the repository browser.