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

Last change on this file was 1834, checked in by Alec Thomas, 17 years ago

Fix for missing set() under 2.3, closes #39

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