diff -r 78d4fe132061 -r 58b645cde6e3 ExtensionCorba/idlclbr.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ExtensionCorba/idlclbr.py Mon Dec 12 16:43:08 2022 +0100 @@ -0,0 +1,423 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2005 - 2022 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Parse a CORBA IDL file and retrieve modules, interfaces, methods and +attributes. + +Parse enough of a CORBA IDL file to recognize module, interface and method +definitions and to find out the superclasses of an interface as well as its +attributes. + +It is based on the Python class browser found in this package. +""" + +import os +import re + +from eric7 import Utilities +from eric7.Utilities.ClassBrowsers import ClbrBaseClasses + +_getnext = re.compile( + r""" + (?P<String> + " [^"\\\n]* (?: \\. [^"\\\n]*)* " + ) + +| (?P<Comment> + ^ [ \t]* // .*? $ + | + ^ [ \t]* /\* .*? \*/ + ) + +| (?P<Method> + ^ + (?P<MethodIndent> [ \t]* ) + (?: oneway [ \t]+ )? + (?: [a-zA-Z0-9_:]+ | void ) [ \t]* + (?P<MethodName> [a-zA-Z_] [a-zA-Z0-9_]* ) + [ \t]* + \( + (?P<MethodSignature> [^)]*? ) + \); + [ \t]* + ) + +| (?P<Interface> + ^ + (?P<InterfaceIndent> [ \t]* ) + (?: abstract [ \t]+ )? + interface [ \t]+ + (?P<InterfaceName> [a-zA-Z_] [a-zA-Z0-9_]* ) + [ \t]* + (?P<InterfaceSupers> : [^{]+? )? + [ \t]* { + ) + +| (?P<Module> + ^ + (?P<ModuleIndent> [ \t]* ) + module [ \t]+ + (?P<ModuleName> [a-zA-Z_] [a-zA-Z0-9_]* ) + [ \t]* { + ) + +| (?P<Attribute> + ^ + (?P<AttributeIndent> [ \t]* ) + (?P<AttributeReadonly> readonly [ \t]+ )? + attribute [ \t]+ + (?P<AttributeType> (?: [a-zA-Z0-9_:]+ [ \t]+ )+ ) + (?P<AttributeNames> [^;]* ) + ; + ) + +| (?P<Begin> + [ \t]* { + ) + +| (?P<End> + [ \t]* } [ \t]* ; + )""", + re.VERBOSE | re.DOTALL | re.MULTILINE, +).search + +# function to replace comments +_commentsub = re.compile(r"""//[^\n]*\n|//[^\n]*$""").sub +# function to normalize whitespace +_normalize = re.compile(r"""[ \t]{2,}""").sub + + +class VisibilityMixin(ClbrBaseClasses.ClbrVisibilityMixinBase): + """ + Mixin class implementing the notion of visibility. + """ + + def __init__(self): + """ + Constructor + """ + self.setPublic() + + +class Module(ClbrBaseClasses.Module, VisibilityMixin): + """ + Class to represent a CORBA IDL module. + """ + + def __init__(self, module, name, file, lineno): + """ + Constructor + + @param module name of the module containing this module + @type str + @param name name of this module + @type str + @param file filename containing this module + @type str + @param lineno line number of the module definition + @type int + """ + ClbrBaseClasses.Module.__init__(self, module, name, file, lineno) + VisibilityMixin.__init__(self) + + +class Interface(ClbrBaseClasses.Class, VisibilityMixin): + """ + Class to represent a CORBA IDL interface. + """ + + def __init__(self, module, name, superClasses, file, lineno): + """ + Constructor + + @param module name of the module containing this interface + @type str + @param name name of this interface + @type str + @param superClasses list of interface names this interface is + inherited from + @type list of str + @param file filename containing this interface + @type str + @param lineno line number of the interface definition + @type int + """ + ClbrBaseClasses.Class.__init__(self, module, name, superClasses, file, lineno) + VisibilityMixin.__init__(self) + + +class Function(ClbrBaseClasses.Function, VisibilityMixin): + """ + Class to represent a CORBA IDL function. + """ + + def __init__(self, module, name, file, lineno, signature="", separator=","): + """ + Constructor + + @param module name of the module containing this function + @type str + @param name name of this function + @type str + @param file filename containing this function + @type str + @param lineno line number of the function definition + @type int + @param signature parameter list of the function + @type str + @param separator string separating the parameters + @type str + """ + ClbrBaseClasses.Function.__init__( + self, module, name, file, lineno, signature, separator + ) + VisibilityMixin.__init__(self) + + +class Attribute(ClbrBaseClasses.Attribute, VisibilityMixin): + """ + Class to represent a CORBA IDL attribute. + """ + + def __init__(self, module, name, file, lineno): + """ + Constructor + + @param module name of the module containing this attribute + @type str + @param name name of this attribute + @type str + @param file filename containing this attribute + @type str + @param lineno line number of the attribute definition + @type int + """ + ClbrBaseClasses.Attribute.__init__(self, module, name, file, lineno) + VisibilityMixin.__init__(self) + + +def readmodule_ex(module, path=None): + """ + Read a CORBA IDL file and return a dictionary of classes, functions and + modules. + + @param module name of the IDL file + @type str + @param path path the file should be searched in + @type list of str + @return the resulting dictionary + @rtype dict + """ + path = [] if path is None else path[:] + for p in path: # search in path + pathname = os.path.join(p, module) + if os.path.exists(pathname): + filename = pathname + try: + src = Utilities.readEncodedFile(filename)[0] + except (UnicodeError, OSError): + # can't do anything with this module + return {} + + return scan(src, filename, module) + return {} + + +def scan(src, file, module): + """ + Public method to scan the given source text. + + @param src source text to be scanned + @type str + @param file file name associated with the source text + @type str + @param module module name associated with the source text + @type str + @return dictionary containing the extracted data + @rtype dict + """ + + def calculateEndline(lineno, lines): + """ + Function to calculate the end line. + + @param lineno line number to start at (one based) + @type int + @param lines list of source lines + @type list of str + @return end line (one based) + @rtype int + """ + # convert lineno to be zero based + lineno -= 1 + # 1. search for opening brace '{' + while lineno < len(lines) and "{" not in lines[lineno]: + lineno += 1 + depth = lines[lineno].count("{") - lines[lineno].count("}") + # 2. search for ending line, i.e. matching closing brace '}' + while depth > 0 and lineno < len(lines) - 1: + lineno += 1 + depth += lines[lineno].count("{") - lines[lineno].count("}") + if depth == 0: + # found a matching brace + return lineno + 1 + else: + # nothing found + return -1 + + def calculateMethodEndline(lineno, lines): + """ + Function to calculate the end line. + + @param lineno line number to start at (one based) + @type int + @param lines list of source lines + @type list of str + @return end line (one based) + @rtype int + """ + # convert lineno to be zero based + lineno -= 1 + while lineno < len(lines) and ";" not in lines[lineno]: + lineno += 1 + if ";" in lines[lineno]: + # found an end indicator, i.e. ';' + return lineno + 1 + else: + return -1 + + # convert eol markers the Python style + src = src.replace("\r\n", "\n").replace("\r", "\n") + srcLines = src.splitlines() + + dictionary = {} + dict_counts = {} + + classstack = [] # stack of (class, indent) pairs + indent = 0 + + lineno, last_lineno_pos = 1, 0 + i = 0 + while True: + m = _getnext(src, i) + if not m: + break + start, i = m.span() + + if m.start("Method") >= 0: + # found a method definition or function + thisindent = indent + meth_name = m.group("MethodName") + meth_sig = m.group("MethodSignature") + meth_sig = meth_sig and meth_sig.replace("\\\n", "") or "" + meth_sig = _commentsub("", meth_sig) + meth_sig = _normalize(" ", meth_sig) + lineno += src.count("\n", last_lineno_pos, start) + last_lineno_pos = start + # close all interfaces/modules indented at least as much + while classstack and classstack[-1][1] >= thisindent: + del classstack[-1] + if classstack: + # it's an interface/module method + cur_class = classstack[-1][0] + if isinstance(cur_class, (Interface, Module)): + # it's a method + f = Function(None, meth_name, file, lineno, meth_sig) + cur_class._addmethod(meth_name, f) + # else it's a nested def + else: + f = None + else: + # it's a function + f = Function(module, meth_name, file, lineno, meth_sig) + if meth_name in dict_counts: + dict_counts[meth_name] += 1 + meth_name = "{0}_{1:d}".format(meth_name, dict_counts[meth_name]) + else: + dict_counts[meth_name] = 0 + dictionary[meth_name] = f + if f: + endline = calculateMethodEndline(lineno, srcLines) + f.setEndLine(endline) + classstack.append((f, thisindent)) # Marker for nested fns + + elif m.start("String") >= 0 or m.start("Comment") >= 0: + pass + + elif m.start("Interface") >= 0: + # we found an interface definition + thisindent = indent + indent += 1 + # close all interfaces/modules indented at least as much + while classstack and classstack[-1][1] >= thisindent: + del classstack[-1] + lineno += src.count("\n", last_lineno_pos, start) + last_lineno_pos = start + class_name = m.group("InterfaceName") + inherit = m.group("InterfaceSupers") + if inherit: + # the interface inherits from other interfaces + inherit = inherit[1:].strip() + inherit = [_commentsub("", inherit)] + # remember this interface + cur_class = Interface(module, class_name, inherit, file, lineno) + endline = calculateEndline(lineno, srcLines) + cur_class.setEndLine(endline) + if not classstack: + dictionary[class_name] = cur_class + else: + cls = classstack[-1][0] + cls._addclass(class_name, cur_class) + classstack.append((cur_class, thisindent)) + + elif m.start("Module") >= 0: + # we found a module definition + thisindent = indent + indent += 1 + # close all interfaces/modules indented at least as much + while classstack and classstack[-1][1] >= thisindent: + del classstack[-1] + lineno += src.count("\n", last_lineno_pos, start) + last_lineno_pos = start + module_name = m.group("ModuleName") + # remember this module + cur_class = Module(module, module_name, file, lineno) + endline = calculateEndline(lineno, srcLines) + cur_class.setEndLine(endline) + if not classstack: + dictionary[module_name] = cur_class + classstack.append((cur_class, thisindent)) + + elif m.start("Attribute") >= 0: + lineno += src.count("\n", last_lineno_pos, start) + last_lineno_pos = start + index = -1 + while index >= -len(classstack): + if ( + classstack[index][0] is not None + and not isinstance(classstack[index][0], Function) + and classstack[index][1] < indent + ): + attributes = m.group("AttributeNames").split(",") + ro = m.group("AttributeReadonly") + for attribute in attributes: + attr = Attribute(module, attribute, file, lineno) + if ro: + attr.setPrivate() + classstack[index][0]._addattribute(attr) + break + else: + index -= 1 + + elif m.start("Begin") >= 0: + # a begin of a block we are not interested in + indent += 1 + + elif m.start("End") >= 0: + # an end of a block + indent -= 1 + + return dictionary