ExtensionCorba/idlclbr.py

changeset 12
58b645cde6e3
child 32
cab6795a8df6
equal deleted inserted replaced
11:78d4fe132061 12:58b645cde6e3
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2005 - 2022 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Parse a CORBA IDL file and retrieve modules, interfaces, methods and
8 attributes.
9
10 Parse enough of a CORBA IDL file to recognize module, interface and method
11 definitions and to find out the superclasses of an interface as well as its
12 attributes.
13
14 It is based on the Python class browser found in this package.
15 """
16
17 import os
18 import re
19
20 from eric7 import Utilities
21 from eric7.Utilities.ClassBrowsers import ClbrBaseClasses
22
23 _getnext = re.compile(
24 r"""
25 (?P<String>
26 " [^"\\\n]* (?: \\. [^"\\\n]*)* "
27 )
28
29 | (?P<Comment>
30 ^ [ \t]* // .*? $
31 |
32 ^ [ \t]* /\* .*? \*/
33 )
34
35 | (?P<Method>
36 ^
37 (?P<MethodIndent> [ \t]* )
38 (?: oneway [ \t]+ )?
39 (?: [a-zA-Z0-9_:]+ | void ) [ \t]*
40 (?P<MethodName> [a-zA-Z_] [a-zA-Z0-9_]* )
41 [ \t]*
42 \(
43 (?P<MethodSignature> [^)]*? )
44 \);
45 [ \t]*
46 )
47
48 | (?P<Interface>
49 ^
50 (?P<InterfaceIndent> [ \t]* )
51 (?: abstract [ \t]+ )?
52 interface [ \t]+
53 (?P<InterfaceName> [a-zA-Z_] [a-zA-Z0-9_]* )
54 [ \t]*
55 (?P<InterfaceSupers> : [^{]+? )?
56 [ \t]* {
57 )
58
59 | (?P<Module>
60 ^
61 (?P<ModuleIndent> [ \t]* )
62 module [ \t]+
63 (?P<ModuleName> [a-zA-Z_] [a-zA-Z0-9_]* )
64 [ \t]* {
65 )
66
67 | (?P<Attribute>
68 ^
69 (?P<AttributeIndent> [ \t]* )
70 (?P<AttributeReadonly> readonly [ \t]+ )?
71 attribute [ \t]+
72 (?P<AttributeType> (?: [a-zA-Z0-9_:]+ [ \t]+ )+ )
73 (?P<AttributeNames> [^;]* )
74 ;
75 )
76
77 | (?P<Begin>
78 [ \t]* {
79 )
80
81 | (?P<End>
82 [ \t]* } [ \t]* ;
83 )""",
84 re.VERBOSE | re.DOTALL | re.MULTILINE,
85 ).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
93 class VisibilityMixin(ClbrBaseClasses.ClbrVisibilityMixinBase):
94 """
95 Mixin class implementing the notion of visibility.
96 """
97
98 def __init__(self):
99 """
100 Constructor
101 """
102 self.setPublic()
103
104
105 class Module(ClbrBaseClasses.Module, VisibilityMixin):
106 """
107 Class to represent a CORBA IDL module.
108 """
109
110 def __init__(self, module, name, file, lineno):
111 """
112 Constructor
113
114 @param module name of the module containing this module
115 @type str
116 @param name name of this module
117 @type str
118 @param file filename containing this module
119 @type str
120 @param lineno line number of the module definition
121 @type int
122 """
123 ClbrBaseClasses.Module.__init__(self, module, name, file, lineno)
124 VisibilityMixin.__init__(self)
125
126
127 class Interface(ClbrBaseClasses.Class, VisibilityMixin):
128 """
129 Class to represent a CORBA IDL interface.
130 """
131
132 def __init__(self, module, name, superClasses, file, lineno):
133 """
134 Constructor
135
136 @param module name of the module containing this interface
137 @type str
138 @param name name of this interface
139 @type str
140 @param superClasses list of interface names this interface is
141 inherited from
142 @type list of str
143 @param file filename containing this interface
144 @type str
145 @param lineno line number of the interface definition
146 @type int
147 """
148 ClbrBaseClasses.Class.__init__(self, module, name, superClasses, file, lineno)
149 VisibilityMixin.__init__(self)
150
151
152 class Function(ClbrBaseClasses.Function, VisibilityMixin):
153 """
154 Class to represent a CORBA IDL function.
155 """
156
157 def __init__(self, module, name, file, lineno, signature="", separator=","):
158 """
159 Constructor
160
161 @param module name of the module containing this function
162 @type str
163 @param name name of this function
164 @type str
165 @param file filename containing this function
166 @type str
167 @param lineno line number of the function definition
168 @type int
169 @param signature parameter list of the function
170 @type str
171 @param separator string separating the parameters
172 @type str
173 """
174 ClbrBaseClasses.Function.__init__(
175 self, module, name, file, lineno, signature, separator
176 )
177 VisibilityMixin.__init__(self)
178
179
180 class Attribute(ClbrBaseClasses.Attribute, VisibilityMixin):
181 """
182 Class to represent a CORBA IDL attribute.
183 """
184
185 def __init__(self, module, name, file, lineno):
186 """
187 Constructor
188
189 @param module name of the module containing this attribute
190 @type str
191 @param name name of this attribute
192 @type str
193 @param file filename containing this attribute
194 @type str
195 @param lineno line number of the attribute definition
196 @type int
197 """
198 ClbrBaseClasses.Attribute.__init__(self, module, name, file, lineno)
199 VisibilityMixin.__init__(self)
200
201
202 def readmodule_ex(module, path=None):
203 """
204 Read a CORBA IDL file and return a dictionary of classes, functions and
205 modules.
206
207 @param module name of the IDL file
208 @type str
209 @param path path the file should be searched in
210 @type list of str
211 @return the resulting dictionary
212 @rtype dict
213 """
214 path = [] if path is None else path[:]
215 for p in path: # search in path
216 pathname = os.path.join(p, module)
217 if os.path.exists(pathname):
218 filename = pathname
219 try:
220 src = Utilities.readEncodedFile(filename)[0]
221 except (UnicodeError, OSError):
222 # can't do anything with this module
223 return {}
224
225 return scan(src, filename, module)
226 return {}
227
228
229 def scan(src, file, module):
230 """
231 Public method to scan the given source text.
232
233 @param src source text to be scanned
234 @type str
235 @param file file name associated with the source text
236 @type str
237 @param module module name associated with the source text
238 @type str
239 @return dictionary containing the extracted data
240 @rtype dict
241 """
242
243 def calculateEndline(lineno, lines):
244 """
245 Function to calculate the end line.
246
247 @param lineno line number to start at (one based)
248 @type int
249 @param lines list of source lines
250 @type list of str
251 @return end line (one based)
252 @rtype int
253 """
254 # convert lineno to be zero based
255 lineno -= 1
256 # 1. search for opening brace '{'
257 while lineno < len(lines) and "{" not in lines[lineno]:
258 lineno += 1
259 depth = lines[lineno].count("{") - lines[lineno].count("}")
260 # 2. search for ending line, i.e. matching closing brace '}'
261 while depth > 0 and lineno < len(lines) - 1:
262 lineno += 1
263 depth += lines[lineno].count("{") - lines[lineno].count("}")
264 if depth == 0:
265 # found a matching brace
266 return lineno + 1
267 else:
268 # nothing found
269 return -1
270
271 def calculateMethodEndline(lineno, lines):
272 """
273 Function to calculate the end line.
274
275 @param lineno line number to start at (one based)
276 @type int
277 @param lines list of source lines
278 @type list of str
279 @return end line (one based)
280 @rtype int
281 """
282 # convert lineno to be zero based
283 lineno -= 1
284 while lineno < len(lines) and ";" not in lines[lineno]:
285 lineno += 1
286 if ";" in lines[lineno]:
287 # found an end indicator, i.e. ';'
288 return lineno + 1
289 else:
290 return -1
291
292 # convert eol markers the Python style
293 src = src.replace("\r\n", "\n").replace("\r", "\n")
294 srcLines = src.splitlines()
295
296 dictionary = {}
297 dict_counts = {}
298
299 classstack = [] # stack of (class, indent) pairs
300 indent = 0
301
302 lineno, last_lineno_pos = 1, 0
303 i = 0
304 while True:
305 m = _getnext(src, i)
306 if not m:
307 break
308 start, i = m.span()
309
310 if m.start("Method") >= 0:
311 # found a method definition or function
312 thisindent = indent
313 meth_name = m.group("MethodName")
314 meth_sig = m.group("MethodSignature")
315 meth_sig = meth_sig and meth_sig.replace("\\\n", "") or ""
316 meth_sig = _commentsub("", meth_sig)
317 meth_sig = _normalize(" ", meth_sig)
318 lineno += src.count("\n", last_lineno_pos, start)
319 last_lineno_pos = start
320 # close all interfaces/modules indented at least as much
321 while classstack and classstack[-1][1] >= thisindent:
322 del classstack[-1]
323 if classstack:
324 # it's an interface/module method
325 cur_class = classstack[-1][0]
326 if isinstance(cur_class, (Interface, Module)):
327 # it's a method
328 f = Function(None, meth_name, file, lineno, meth_sig)
329 cur_class._addmethod(meth_name, f)
330 # else it's a nested def
331 else:
332 f = None
333 else:
334 # it's a function
335 f = Function(module, meth_name, file, lineno, meth_sig)
336 if meth_name in dict_counts:
337 dict_counts[meth_name] += 1
338 meth_name = "{0}_{1:d}".format(meth_name, dict_counts[meth_name])
339 else:
340 dict_counts[meth_name] = 0
341 dictionary[meth_name] = f
342 if f:
343 endline = calculateMethodEndline(lineno, srcLines)
344 f.setEndLine(endline)
345 classstack.append((f, thisindent)) # Marker for nested fns
346
347 elif m.start("String") >= 0 or m.start("Comment") >= 0:
348 pass
349
350 elif m.start("Interface") >= 0:
351 # we found an interface definition
352 thisindent = indent
353 indent += 1
354 # close all interfaces/modules indented at least as much
355 while classstack and classstack[-1][1] >= thisindent:
356 del classstack[-1]
357 lineno += src.count("\n", last_lineno_pos, start)
358 last_lineno_pos = start
359 class_name = m.group("InterfaceName")
360 inherit = m.group("InterfaceSupers")
361 if inherit:
362 # the interface inherits from other interfaces
363 inherit = inherit[1:].strip()
364 inherit = [_commentsub("", inherit)]
365 # remember this interface
366 cur_class = Interface(module, class_name, inherit, file, lineno)
367 endline = calculateEndline(lineno, srcLines)
368 cur_class.setEndLine(endline)
369 if not classstack:
370 dictionary[class_name] = cur_class
371 else:
372 cls = classstack[-1][0]
373 cls._addclass(class_name, cur_class)
374 classstack.append((cur_class, thisindent))
375
376 elif m.start("Module") >= 0:
377 # we found a module definition
378 thisindent = indent
379 indent += 1
380 # close all interfaces/modules indented at least as much
381 while classstack and classstack[-1][1] >= thisindent:
382 del classstack[-1]
383 lineno += src.count("\n", last_lineno_pos, start)
384 last_lineno_pos = start
385 module_name = m.group("ModuleName")
386 # remember this module
387 cur_class = Module(module, module_name, file, lineno)
388 endline = calculateEndline(lineno, srcLines)
389 cur_class.setEndLine(endline)
390 if not classstack:
391 dictionary[module_name] = cur_class
392 classstack.append((cur_class, thisindent))
393
394 elif m.start("Attribute") >= 0:
395 lineno += src.count("\n", last_lineno_pos, start)
396 last_lineno_pos = start
397 index = -1
398 while index >= -len(classstack):
399 if (
400 classstack[index][0] is not None
401 and not isinstance(classstack[index][0], Function)
402 and classstack[index][1] < indent
403 ):
404 attributes = m.group("AttributeNames").split(",")
405 ro = m.group("AttributeReadonly")
406 for attribute in attributes:
407 attr = Attribute(module, attribute, file, lineno)
408 if ro:
409 attr.setPrivate()
410 classstack[index][0]._addattribute(attr)
411 break
412 else:
413 index -= 1
414
415 elif m.start("Begin") >= 0:
416 # a begin of a block we are not interested in
417 indent += 1
418
419 elif m.start("End") >= 0:
420 # an end of a block
421 indent -= 1
422
423 return dictionary

eric ide

mercurial