src/eric7/Utilities/ClassBrowsers/idlclbr.py

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

eric ide

mercurial