ExtensionCorba/idlclbr.py

changeset 12
58b645cde6e3
child 32
cab6795a8df6
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

eric ide

mercurial