|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2005 - 2009 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Parse a CORBA IDL file and retrieve modules, interfaces, methods and attributes. |
|
8 |
|
9 Parse enough of a CORBA IDL file to recognize module, interface and method definitions |
|
10 and to find out the superclasses of an interface as well as its attributes. |
|
11 |
|
12 It is based on the Python class browser found in this package. |
|
13 """ |
|
14 |
|
15 import sys |
|
16 import os |
|
17 import re |
|
18 |
|
19 import Utilities |
|
20 import Utilities.ClassBrowsers as ClassBrowsers |
|
21 import ClbrBaseClasses |
|
22 |
|
23 SUPPORTED_TYPES = [ClassBrowsers.IDL_SOURCE] |
|
24 |
|
25 _getnext = re.compile(r""" |
|
26 (?P<String> |
|
27 " [^"\\\n]* (?: \\. [^"\\\n]*)* " |
|
28 ) |
|
29 |
|
30 | (?P<Comment> |
|
31 ^ [ \t]* // .*? $ |
|
32 | |
|
33 ^ [ \t]* /\* .*? \*/ |
|
34 ) |
|
35 |
|
36 | (?P<Method> |
|
37 ^ |
|
38 (?P<MethodIndent> [ \t]* ) |
|
39 (?: oneway [ \t]+ )? |
|
40 (?: [a-zA-Z0-9_:]+ | void ) [ \t]* |
|
41 (?P<MethodName> [a-zA-Z_] [a-zA-Z0-9_]* ) |
|
42 [ \t]* |
|
43 \( |
|
44 (?P<MethodSignature> [^)]*? ) |
|
45 \); |
|
46 [ \t]* |
|
47 ) |
|
48 |
|
49 | (?P<Interface> |
|
50 ^ |
|
51 (?P<InterfaceIndent> [ \t]* ) |
|
52 (?: abstract [ \t]+ )? |
|
53 interface [ \t]+ |
|
54 (?P<InterfaceName> [a-zA-Z_] [a-zA-Z0-9_]* ) |
|
55 [ \t]* |
|
56 (?P<InterfaceSupers> : [^{]+? )? |
|
57 [ \t]* { |
|
58 ) |
|
59 |
|
60 | (?P<Module> |
|
61 ^ |
|
62 (?P<ModuleIndent> [ \t]* ) |
|
63 module [ \t]+ |
|
64 (?P<ModuleName> [a-zA-Z_] [a-zA-Z0-9_]* ) |
|
65 [ \t]* { |
|
66 ) |
|
67 |
|
68 | (?P<Attribute> |
|
69 ^ |
|
70 (?P<AttributeIndent> [ \t]* ) |
|
71 (?P<AttributeReadonly> readonly [ \t]+ )? |
|
72 attribute [ \t]+ |
|
73 (?P<AttributeType> (?: [a-zA-Z0-9_:]+ [ \t]+ )+ ) |
|
74 (?P<AttributeNames> [^;]* ) |
|
75 ; |
|
76 ) |
|
77 |
|
78 | (?P<Begin> |
|
79 [ \t]* { |
|
80 ) |
|
81 |
|
82 | (?P<End> |
|
83 [ \t]* } [ \t]* ; |
|
84 ) |
|
85 """, re.VERBOSE | re.DOTALL | re.MULTILINE).search |
|
86 |
|
87 # function to replace comments |
|
88 _commentsub = re.compile(r"""//[^\n]*\n|//[^\n]*$""").sub |
|
89 # function to normalize whitespace |
|
90 _normalize = re.compile(r"""[ \t]{2,}""").sub |
|
91 |
|
92 _modules = {} # cache of modules we've seen |
|
93 |
|
94 class VisibilityMixin(ClbrBaseClasses.ClbrVisibilityMixinBase): |
|
95 """ |
|
96 Mixin class implementing the notion of visibility. |
|
97 """ |
|
98 def __init__(self): |
|
99 """ |
|
100 Method to initialize the visibility. |
|
101 """ |
|
102 self.setPublic() |
|
103 |
|
104 class Module(ClbrBaseClasses.Module, VisibilityMixin): |
|
105 """ |
|
106 Class to represent a CORBA IDL module. |
|
107 """ |
|
108 def __init__(self, module, name, file, lineno): |
|
109 """ |
|
110 Constructor |
|
111 |
|
112 @param module name of the module containing this class |
|
113 @param name name of this class |
|
114 @param file filename containing this class |
|
115 @param lineno linenumber of the class definition |
|
116 """ |
|
117 ClbrBaseClasses.Module.__init__(self, module, name, file, lineno) |
|
118 VisibilityMixin.__init__(self) |
|
119 |
|
120 class Interface(ClbrBaseClasses.Class, VisibilityMixin): |
|
121 """ |
|
122 Class to represent a CORBA IDL interface. |
|
123 """ |
|
124 def __init__(self, module, name, super, file, lineno): |
|
125 """ |
|
126 Constructor |
|
127 |
|
128 @param module name of the module containing this class |
|
129 @param name name of this interface |
|
130 @param super list of interface names this interface is inherited from |
|
131 @param file filename containing this interface |
|
132 @param lineno linenumber of the interface definition |
|
133 """ |
|
134 ClbrBaseClasses.Class.__init__(self, module, name, super, file, lineno) |
|
135 VisibilityMixin.__init__(self) |
|
136 |
|
137 class Function(ClbrBaseClasses.Function, VisibilityMixin): |
|
138 """ |
|
139 Class to represent a CORBA IDL function. |
|
140 """ |
|
141 def __init__(self, module, name, file, lineno, signature = '', separator = ','): |
|
142 """ |
|
143 Constructor |
|
144 |
|
145 @param module name of the module containing this function |
|
146 @param name name of this function |
|
147 @param file filename containing this class |
|
148 @param lineno linenumber of the class definition |
|
149 @param signature parameterlist of the method |
|
150 @param separator string separating the parameters |
|
151 """ |
|
152 ClbrBaseClasses.Function.__init__(self, module, name, file, lineno, |
|
153 signature, separator) |
|
154 VisibilityMixin.__init__(self) |
|
155 |
|
156 class Attribute(ClbrBaseClasses.Attribute, VisibilityMixin): |
|
157 """ |
|
158 Class to represent a CORBA IDL attribute. |
|
159 """ |
|
160 def __init__(self, module, name, file, lineno): |
|
161 """ |
|
162 Constructor |
|
163 |
|
164 @param module name of the module containing this class |
|
165 @param name name of this class |
|
166 @param file filename containing this attribute |
|
167 @param lineno linenumber of the class definition |
|
168 """ |
|
169 ClbrBaseClasses.Attribute.__init__(self, module, name, file, lineno) |
|
170 VisibilityMixin.__init__(self) |
|
171 |
|
172 def readmodule_ex(module, path=[]): |
|
173 ''' |
|
174 Read a CORBA IDL file and return a dictionary of classes, functions and modules. |
|
175 |
|
176 @param module name of the CORBA IDL file (string) |
|
177 @param path path the file should be searched in (list of strings) |
|
178 @return the resulting dictionary |
|
179 ''' |
|
180 |
|
181 dict = {} |
|
182 dict_counts = {} |
|
183 |
|
184 if module in _modules: |
|
185 # we've seen this file before... |
|
186 return _modules[module] |
|
187 |
|
188 # search the path for the file |
|
189 f = None |
|
190 fullpath = list(path) |
|
191 f, file, (suff, mode, type) = ClassBrowsers.find_module(module, fullpath) |
|
192 if type not in SUPPORTED_TYPES: |
|
193 # not CORBA IDL source, can't do anything with this module |
|
194 f.close() |
|
195 _modules[module] = dict |
|
196 return dict |
|
197 |
|
198 _modules[module] = dict |
|
199 classstack = [] # stack of (class, indent) pairs |
|
200 indent = 0 |
|
201 src = Utilities.decode(f.read())[0] |
|
202 f.close() |
|
203 |
|
204 lineno, last_lineno_pos = 1, 0 |
|
205 i = 0 |
|
206 while 1: |
|
207 m = _getnext(src, i) |
|
208 if not m: |
|
209 break |
|
210 start, i = m.span() |
|
211 |
|
212 if m.start("Method") >= 0: |
|
213 # found a method definition or function |
|
214 thisindent = indent |
|
215 meth_name = m.group("MethodName") |
|
216 meth_sig = m.group("MethodSignature") |
|
217 meth_sig = meth_sig and meth_sig.replace('\\\n', '') or '' |
|
218 meth_sig = _commentsub('', meth_sig) |
|
219 meth_sig = _normalize(' ', meth_sig) |
|
220 lineno = lineno + src.count('\n', last_lineno_pos, start) |
|
221 last_lineno_pos = start |
|
222 # close all interfaces/modules indented at least as much |
|
223 while classstack and \ |
|
224 classstack[-1][1] >= thisindent: |
|
225 del classstack[-1] |
|
226 if classstack: |
|
227 # it's an interface/module method |
|
228 cur_class = classstack[-1][0] |
|
229 if isinstance(cur_class, Interface) or isinstance(cur_class, Module): |
|
230 # it's a method |
|
231 f = Function(None, meth_name, |
|
232 file, lineno, meth_sig) |
|
233 cur_class._addmethod(meth_name, f) |
|
234 # else it's a nested def |
|
235 else: |
|
236 # it's a function |
|
237 f = Function(module, meth_name, |
|
238 file, lineno, meth_sig) |
|
239 if dict_counts.has_key(meth_name): |
|
240 dict_counts[meth_name] += 1 |
|
241 meth_name = "%s_%d" % (meth_name, dict_counts[meth_name]) |
|
242 else: |
|
243 dict_counts[meth_name] = 0 |
|
244 dict[meth_name] = f |
|
245 classstack.append((f, thisindent)) # Marker for nested fns |
|
246 |
|
247 elif m.start("String") >= 0: |
|
248 pass |
|
249 |
|
250 elif m.start("Comment") >= 0: |
|
251 pass |
|
252 |
|
253 elif m.start("Interface") >= 0: |
|
254 # we found an interface definition |
|
255 thisindent = indent |
|
256 indent += 1 |
|
257 # close all interfaces/modules indented at least as much |
|
258 while classstack and \ |
|
259 classstack[-1][1] >= thisindent: |
|
260 del classstack[-1] |
|
261 lineno = lineno + src.count('\n', last_lineno_pos, start) |
|
262 last_lineno_pos = start |
|
263 class_name = m.group("InterfaceName") |
|
264 inherit = m.group("InterfaceSupers") |
|
265 if inherit: |
|
266 # the interface inherits from other interfaces |
|
267 inherit = inherit[1:].strip() |
|
268 inherit = [_commentsub('', inherit)] |
|
269 # remember this interface |
|
270 cur_class = Interface(module, class_name, inherit, |
|
271 file, lineno) |
|
272 if not classstack: |
|
273 dict[class_name] = cur_class |
|
274 else: |
|
275 cls = classstack[-1][0] |
|
276 cls._addclass(class_name, cur_class) |
|
277 classstack.append((cur_class, thisindent)) |
|
278 |
|
279 elif m.start("Module") >= 0: |
|
280 # we found a module definition |
|
281 thisindent = indent |
|
282 indent += 1 |
|
283 # close all interfaces/modules indented at least as much |
|
284 while classstack and \ |
|
285 classstack[-1][1] >= thisindent: |
|
286 del classstack[-1] |
|
287 lineno = lineno + src.count('\n', last_lineno_pos, start) |
|
288 last_lineno_pos = start |
|
289 module_name = m.group("ModuleName") |
|
290 # remember this module |
|
291 cur_class = Module(module, module_name, file, lineno) |
|
292 if not classstack: |
|
293 dict[module_name] = cur_class |
|
294 classstack.append((cur_class, thisindent)) |
|
295 |
|
296 elif m.start("Attribute") >= 0: |
|
297 lineno = lineno + src.count('\n', last_lineno_pos, start) |
|
298 last_lineno_pos = start |
|
299 index = -1 |
|
300 while index >= -len(classstack): |
|
301 if classstack[index][0] is not None and \ |
|
302 not isinstance(classstack[index][0], Function) and \ |
|
303 not classstack[index][1] >= indent: |
|
304 attributes = m.group("AttributeNames").split(',') |
|
305 ro = m.group("AttributeReadonly") |
|
306 for attribute in attributes: |
|
307 attr = Attribute(module, attribute, file, lineno) |
|
308 if ro: |
|
309 attr.setPrivate() |
|
310 classstack[index][0]._addattribute(attr) |
|
311 break |
|
312 else: |
|
313 index -= 1 |
|
314 |
|
315 elif m.start("Begin") >= 0: |
|
316 # a begin of a block we are not interested in |
|
317 indent += 1 |
|
318 |
|
319 elif m.start("End") >= 0: |
|
320 # an end of a block |
|
321 indent -= 1 |
|
322 |
|
323 else: |
|
324 assert 0, "regexp _getnext found something unexpected" |
|
325 |
|
326 return dict |