File: Synopsis/Processors/Comments/Grouper.py 1
2
3
4
5
6
7
8from Synopsis import ASG
9from Synopsis.QualifiedName import QualifiedName
10from Synopsis.Processors.Transformer import Transformer
11import re
12
13class Grouper(Transformer):
14 """A class that detects grouping tags and moves the enclosed nodes
15 into a subnode (a 'Group')"""
16
17 tags = r'^\s*((?P<open>@group\s*(?P<name>.*){)|(?P<close>\s*}))\s*\Z'
18
19 def __init__(self, **kwds):
20
21 Transformer.__init__(self, **kwds)
22 self.__group_stack = [[]]
23 self.tags = re.compile(Grouper.tags, re.M)
24
25 def strip_dangling_groups(self):
26 """As groups must not overlap with 'real' scopes,
27 make sure all groups created in the current scope are closed
28 when leaving the scope."""
29
30 if self.__group_stack[-1]:
31 print 'Warning: group stack is non-empty !'
32 while (self.__group_stack[-1]):
33 group = self.__group_stack[-1][-1]
34 print 'forcing closing of group %s (opened near %s:%d)'%(group.name, group.file.name, group.line)
35 self.pop_group()
36
37 def finalize(self):
38 """replace the ASG with the newly created one"""
39
40 self.strip_dangling_groups()
41 super(Grouper, self).finalize()
42
43 def push(self):
44 """starts a new group stack to be able to validate group scopes"""
45
46 Transformer.push(self)
47 self.__group_stack.append([])
48
49 def pop(self, decl):
50 """Make sure the current group stack is empty."""
51
52 self.strip_dangling_groups()
53 self.__group_stack.pop()
54 Transformer.pop(self, decl)
55
56 def push_group(self, group):
57 """Push new group scope to the stack."""
58
59 self.__group_stack[-1].append(group)
60 Transformer.push(self)
61
62 def pop_group(self, decl=None):
63 """Pop a group scope from the stack.
64
65 decl -- an optional declaration from which to extract the context,
66 used for the error message if needed.
67 """
68
69 if self.__group_stack[-1]:
70 group = self.__group_stack[-1].pop()
71 group.declarations = self.current_scope()
72 Transformer.pop(self, group)
73 else:
74 if decl:
75 print "Warning: no group open in current scope (near %s:%d), ignoring."%(decl.file.name, decl.line)
76 else:
77 print "Warning: no group open in current scope, ignoring."
78
79
80 def process_comments(self, decl):
81 """Checks for grouping tags.
82 If an opening tag is found in the middle of a comment, a new Group is generated, the preceeding
83 comments are associated with it, and is pushed onto the scope stack as well as the groups stack.
84 """
85
86 comments = []
87 for c in decl.annotations.get('comments', []):
88 if c is None:
89 comments.append(None)
90 continue
91 tag = self.tags.search(c)
92 if not tag:
93 comments.append(c)
94 continue
95 elif tag.group('open'):
96
97 if self.debug:
98 print 'found group open tag in', decl.name
99
100
101 label = tag.group('name') or 'unnamed'
102 label = label.strip()
103
104 if tag.start('open') > 0:
105 c = c[:tag.start('open')]
106 comments.append(c)
107 group = ASG.Group(decl.file, decl.line, 'group', QualifiedName((label,)))
108 group.annotations['comments'] = comments
109 comments = []
110 self.push_group(group)
111
112 elif tag.group('close'):
113
114 if self.debug:
115 print 'found group close tag in', decl.name
116
117 self.pop_group(decl)
118
119 decl.annotations['comments'] = comments
120
121
122 def visit_declaration(self, decl):
123
124 self.process_comments(decl)
125 self.add(decl)
126
127 def visit_scope(self, scope):
128 """Visits all children of the scope in a new scope. The value of
129 current_scope() at the end of the list is used to replace scope's list of
130 declarations - hence you can remove (or insert) declarations from the
131 list."""
132
133 self.process_comments(scope)
134 self.push()
135 for d in scope.declarations: d.accept(self)
136 scope.declarations = self.current_scope()
137 self.pop(scope)
138
139 def visit_enum(self, enum):
140 """Does the same as visit_scope, but for the enum's list of
141 enumerators"""
142
143 self.process_comments(enum)
144 self.push()
145 for enumor in enum.enumerators:
146 enumor.accept(self)
147 enum.enumerators = self.current_scope()
148 self.pop(enum)
149
150 def visit_enumerator(self, enumor):
151 """Removes dummy enumerators"""
152
153 if enumor.type == "dummy": return
154 if not len(enumor.name): return
155 self.add(enumor)
156
157
Generated on Thu Apr 16 16:27:16 2009 by
synopsis (version devel)