src/eric7/Utilities/ModuleParser.py

branch
eric7
changeset 10477
11b5d533e4a2
parent 10471
28d4780d6a66
child 10482
72d9b5ea39b4
--- a/src/eric7/Utilities/ModuleParser.py	Thu Jan 04 10:43:29 2024 +0100
+++ b/src/eric7/Utilities/ModuleParser.py	Thu Jan 04 17:07:38 2024 +0100
@@ -23,6 +23,8 @@
 
 from functools import reduce
 
+from PyQt6.QtCore import QRegularExpression
+
 from eric7 import Utilities
 
 __all__ = [
@@ -66,7 +68,7 @@
         return -1
 
 
-_py_getnext = re.compile(
+_py_getnext = QRegularExpression(
     r"""
     (?P<Comment>
         \# .*? $   # ignore everything in comments
@@ -150,7 +152,7 @@
         ^
         (?P<MethodIndent> [ \t]* )
         (?: async [ \t]+ )? (?: cdef | cpdef | def) [ \t]+
-        (?P<MethodName> \w+ )
+        (?P<MethodName> [\pL_] \w* )
         (?: [ \t]* \[ [^\]]+ \] )?
         [ \t]* \(
         (?P<MethodSignature> (?: [^)] | \)[ \t]*,? )*? )
@@ -164,7 +166,7 @@
         (?P<ClassIndent> [ \t]* )
         (?: cdef [ \t]+ )?
         class [ \t]+
-        (?P<ClassName> \w+ )
+        (?P<ClassName> [\pL_] \w* )
         (?: [ \t]* \[ [^\]]+ \] )?
         [ \t]*
         (?P<ClassSupers> \( [^)]* \) )?
@@ -175,23 +177,30 @@
         ^
         (?P<AttributeIndent> [ \t]* )
         self [ \t]* \. [ \t]*
-        (?P<AttributeName> \w+ )
-        (?: [ \t]* : [^=\n]+ )?
+        (?P<AttributeName> [\pL_] \w* )
         [ \t]* =
     )
 
+|   (?P<TypedAttribute>
+        ^
+        (?P<TypedAttributeIndent> [ \t]* )
+        self [ \t]* \. [ \t]*
+        (?P<TypedAttributeName> [\pL_] \w* )
+        [ \t]* : [ \t]+
+    )
+
 |   (?P<Variable>
         ^
         (?P<VariableIndent> [ \t]* )
-        (?P<VariableName> \w+ )
+        (?P<VariableName> [\pL_] \w* )
         [ \t]* = [ \t]* (?P<VariableSignal> (?:pyqtSignal)? )
     )
 
 |   (?P<TypedVariable>
         ^
         (?P<TypedVariableIndent> [ \t]* )
-        (?P<TypedVariableName> \w+ )
-        [ \t]* :
+        (?P<TypedVariableName> [\pL_] \w* )
+        [ \t]* : [ \t]+
     )
 
 |   (?P<Main>
@@ -226,10 +235,12 @@
         (?: (?: if | elif ) [ \t]+ [^:]* | else [ \t]* ) :
         (?= \s* (?: async [ \t]+ )? def)
     )""",
-    re.VERBOSE | re.MULTILINE,
-).search
+    QRegularExpression.PatternOption.MultilineOption
+    | QRegularExpression.PatternOption.ExtendedPatternSyntaxOption
+    | QRegularExpression.PatternOption.UseUnicodePropertiesOption,
+).match
 
-_rb_getnext = re.compile(
+_rb_getnext = QRegularExpression(
     r"""
     (?P<Docstring>
         =begin [ \t]+ edoc (?P<DocstringContents> .*? ) =end
@@ -364,8 +375,10 @@
             end \b [^_]
         )
     )""",
-    re.VERBOSE | re.MULTILINE,
-).search
+    QRegularExpression.PatternOption.MultilineOption
+    | QRegularExpression.PatternOption.ExtendedPatternSyntaxOption
+    | QRegularExpression.PatternOption.UseUnicodePropertiesOption,
+).match
 
 _hashsub = re.compile(r"""^([ \t]*)#[ \t]?""", re.MULTILINE).sub
 
@@ -613,25 +626,25 @@
         modifierIndent = -1
         while True:
             m = self._getnext(src, i)
-            if not m:
+            if not m.hasMatch():
                 break
-            start, i = m.span()
+            start, i = m.capturedStart(), m.capturedEnd()
 
-            if m.start("MethodModifier") >= 0:
-                modifierIndent = _indent(m.group("MethodModifierIndent"))
-                modifierType = m.group("MethodModifierType")
+            if m.captured("MethodModifier"):
+                modifierIndent = _indent(m.captured("MethodModifierIndent"))
+                modifierType = m.captured("MethodModifierType")
 
-            elif m.start("Method") >= 0:
+            elif m.captured("Method"):
                 # found a method definition or function
-                thisindent = _indent(m.group("MethodIndent"))
-                meth_name = m.group("MethodName")
-                meth_sig = m.group("MethodSignature")
+                thisindent = _indent(m.captured("MethodIndent"))
+                meth_name = m.captured("MethodName")
+                meth_sig = m.captured("MethodSignature")
                 meth_sig = meth_sig.replace("\\\n", "")
-                meth_ret = m.group("MethodReturnAnnotation")
+                meth_ret = m.captured("MethodReturnAnnotation")
                 meth_ret = meth_ret.replace("\\\n", "")
-                if m.group("MethodPyQtSignature") is not None:
+                if m.captured("MethodPyQtSignature"):
                     meth_pyqtSig = (
-                        m.group("MethodPyQtSignature")
+                        m.captured("MethodPyQtSignature")
                         .replace("\\\n", "")
                         .split("result")[0]
                         .split("name")[0]
@@ -640,6 +653,13 @@
                 else:
                     meth_pyqtSig = None
                 lineno += src.count("\n", last_lineno_pos, start)
+                def_lineno = (
+                    lineno + src.count(
+                        "\n", start, m.capturedStart("MethodIndent")
+                    )
+                    if m.capturedStart("MethodIndent") > start
+                    else lineno
+                )
                 last_lineno_pos = start
                 if modifierType and modifierIndent == thisindent:
                     if modifierType == "@staticmethod":
@@ -719,7 +739,7 @@
                     )
                     self.__py_setVisibility(f)
                     self.addFunction(meth_name, f)
-                endlineno = calculateEndline(lineno, srcLines, thisindent)
+                endlineno = calculateEndline(def_lineno, srcLines, thisindent)
                 f.setEndLine(endlineno)
                 cur_obj = f
                 classstack.append((None, thisindent))  # Marker for nested fns
@@ -728,49 +748,49 @@
                 modifierType = Function.General
                 modifierIndent = -1
 
-            elif m.start("Docstring") >= 0:
-                contents = m.group("DocstringContents3")
-                if contents is not None:
+            elif m.captured("Docstring"):
+                contents = m.captured("DocstringContents3")
+                if contents:
                     contents = _hashsub(r"\1", contents)
                 else:
                     if self.file.lower().endswith(".ptl"):
                         contents = ""
                     else:
-                        contents = m.group("DocstringContents1") or m.group(
+                        contents = m.captured("DocstringContents1") or m.captured(
                             "DocstringContents2"
                         )
                 if cur_obj:
                     cur_obj.addDescription(contents)
 
-            elif m.start("String") >= 0:
+            elif m.captured("String"):
                 if modulelevel and (
                     src[start - len("\r\n") : start] == "\r\n"
                     or src[start - len("\n") : start] == "\n"
                     or src[start - len("\r") : start] == "\r"
                 ):
-                    contents = m.group("StringContents3")
-                    if contents is not None:
+                    contents = m.captured("StringContents3")
+                    if contents:
                         contents = _hashsub(r"\1", contents)
                     else:
                         if self.file.lower().endswith(".ptl"):
                             contents = ""
                         else:
-                            contents = m.group("StringContents1") or m.group(
+                            contents = m.captured("StringContents1") or m.captured(
                                 "StringContents2"
                             )
                     if cur_obj:
                         cur_obj.addDescription(contents)
 
-            elif m.start("Class") >= 0:
+            elif m.captured("Class"):
                 # we found a class definition
-                thisindent = _indent(m.group("ClassIndent"))
+                thisindent = _indent(m.captured("ClassIndent"))
                 lineno += src.count("\n", last_lineno_pos, start)
                 last_lineno_pos = start
                 # close all classes indented at least as much
                 while classstack and classstack[-1][1] >= thisindent:
                     del classstack[-1]
-                class_name = m.group("ClassName")
-                inherit = m.group("ClassSupers")
+                class_name = m.captured("ClassName")
+                inherit = m.captured("ClassSupers")
                 if inherit:
                     # the class inherits from other classes
                     inherit = inherit[1:-1].strip()
@@ -819,13 +839,16 @@
                 # add nested classes to the module
                 classstack.append((cur_class, thisindent))
 
-            elif m.start("Attribute") >= 0:
+            elif m.captured("Attribute") or m.captured("TypedAttribute"):
+                if m.captured("Attribute"):
+                    attrName = m.captured("AttributeName")
+                else:
+                    attrName = m.captured("TypedAttributeName")
                 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:
-                        attrName = m.group("AttributeName")
                         attr = Attribute(self.name, attrName, self.file, lineno)
                         self.__py_setVisibility(attr)
                         classstack[index][0].addAttribute(attrName, attr)
@@ -833,19 +856,28 @@
                     else:
                         index -= 1
 
-            elif m.start("Main") >= 0:
+            elif m.captured("Main"):
                 # 'main' part of the script, reset class stack
                 lineno += src.count("\n", last_lineno_pos, start)
                 last_lineno_pos = start
                 classstack = []
 
-            elif m.start("Variable") >= 0:
-                thisindent = _indent(m.group("VariableIndent"))
-                variable_name = m.group("VariableName")
-                isSignal = m.group("VariableSignal") != ""
+            elif m.captured("Variable") or m.captured("TypedVariable"):
+                if m.captured("Variable"):
+                    thisindent = _indent(m.captured("VariableIndent"))
+                    variable_name = m.captured("VariableName")
+                    isSignal = m.captured("VariableSignal") != ""
+                else:
+                    thisindent = _indent(m.captured("TypedVariableIndent"))
+                    variable_name = m.captured("TypedVariableName")
+                    isSignal = False
+                    if keyword.iskeyword(variable_name):
+                        # only if the determined name is not a keyword
+                        # (e.g. else, except)
+                        continue
                 lineno += src.count("\n", last_lineno_pos, start)
                 last_lineno_pos = start
-                if thisindent == 0:
+                if thisindent == 0 or not classstack:
                     # global variable
                     attr = Attribute(
                         self.name, variable_name, self.file, lineno, isSignal=isSignal
@@ -872,39 +904,11 @@
                                 classstack[index][0].addGlobal(variable_name, attr)
                             break
 
-            elif m.start("TypedVariable") >= 0:
-                thisindent = _indent(m.group("TypedVariableIndent"))
-                variable_name = m.group("TypedVariableName")
-                if not keyword.iskeyword(variable_name):
-                    # only if the determined name is not a keyword (e.g. else, except)
-                    lineno += src.count("\n", last_lineno_pos, start)
-                    last_lineno_pos = start
-                    if thisindent == 0:
-                        # global variable
-                        attr = Attribute(self.name, variable_name, self.file, lineno)
-                        self.__py_setVisibility(attr)
-                        self.addGlobal(variable_name, attr)
-                    else:
-                        index = -1
-                        while index >= -len(classstack):
-                            if classstack[index][1] >= thisindent:
-                                index -= 1
-                            else:
-                                if classstack[index][0] is not None and isinstance(
-                                    classstack[index][0], Class
-                                ):
-                                    attr = Attribute(
-                                        self.name, variable_name, self.file, lineno
-                                    )
-                                    self.__py_setVisibility(attr)
-                                    classstack[index][0].addGlobal(variable_name, attr)
-                                break
-
-            elif m.start("Import") >= 0:
+            elif m.captured("Import"):
                 # - import module
                 names = [
                     n.strip()
-                    for n in "".join(m.group("ImportList").splitlines())
+                    for n in "".join(m.captured("ImportList").splitlines())
                     .replace("\\", "")
                     .split(",")
                 ]
@@ -912,11 +916,11 @@
                     [name for name in names if name not in self.imports]
                 )
 
-            elif m.start("ImportFrom") >= 0:
+            elif m.captured("ImportFrom"):
                 # - from module import stuff
-                mod = m.group("ImportFromPath")
+                mod = m.captured("ImportFromPath")
                 namesLines = (
-                    m.group("ImportFromList")
+                    m.captured("ImportFromList")
                     .replace("(", "")
                     .replace(")", "")
                     .replace("\\", "")
@@ -931,9 +935,9 @@
                     [name for name in names if name not in self.from_imports[mod]]
                 )
 
-            elif m.start("ConditionalDefine") >= 0:
+            elif m.captured("ConditionalDefine"):
                 # a conditional function/method definition
-                thisindent = _indent(m.group("ConditionalDefineIndent"))
+                thisindent = _indent(m.captured("ConditionalDefineIndent"))
                 while conditionalsstack and conditionalsstack[-1] >= thisindent:
                     del conditionalsstack[-1]
                     if deltastack:
@@ -941,7 +945,7 @@
                 conditionalsstack.append(thisindent)
                 deltaindentcalculated = 0
 
-            elif m.start("Comment") >= 0 and modulelevel:
+            elif m.captured("Comment") and modulelevel:
                 continue
 
             modulelevel = False
@@ -967,16 +971,16 @@
                 break
             start, i = m.span()
 
-            if m.start("Method") >= 0:
+            if m.captured("Method"):
                 # found a method definition or function
                 thisindent = indent
                 indent += 1
                 meth_name = (
-                    m.group("MethodName")
-                    or m.group("MethodName2")
-                    or m.group("MethodName3")
+                    m.captured("MethodName")
+                    or m.captured("MethodName2")
+                    or m.captured("MethodName3")
                 )
-                meth_sig = m.group("MethodSignature")
+                meth_sig = m.captured("MethodSignature")
                 meth_sig = meth_sig and meth_sig.replace("\\\n", "") or ""
                 lineno += src.count("\n", last_lineno_pos, start)
                 last_lineno_pos = start
@@ -1034,14 +1038,14 @@
                 cur_obj = f
                 classstack.append((None, thisindent))  # Marker for nested fns
 
-            elif m.start("Docstring") >= 0:
-                contents = m.group("DocstringContents")
-                if contents is not None:
+            elif m.captured("Docstring"):
+                contents = m.captured("DocstringContents")
+                if contents:
                     contents = _hashsub(r"\1", contents)
                 if cur_obj:
                     cur_obj.addDescription(contents)
 
-            elif m.start("Class") >= 0:
+            elif m.captured("Class"):
                 # we found a class definition
                 thisindent = indent
                 indent += 1
@@ -1055,8 +1059,8 @@
                         # record the end line of this class, function or module
                         classstack[-1][0].setEndLine(lineno - 1)
                     del classstack[-1]
-                class_name = m.group("ClassName") or m.group("ClassName2")
-                inherit = m.group("ClassSupers")
+                class_name = m.captured("ClassName") or m.captured("ClassName2")
+                inherit = m.captured("ClassSupers")
                 if inherit:
                     # the class inherits from other classes
                     inherit = inherit[1:].strip()
@@ -1089,7 +1093,7 @@
                 acstack.append(["public", thisindent])
                 # default access control is 'public'
 
-            elif m.start("Module") >= 0:
+            elif m.captured("Module"):
                 # we found a module definition
                 thisindent = indent
                 indent += 1
@@ -1103,7 +1107,7 @@
                         # record the end line of this class, function or module
                         classstack[-1][0].setEndLine(lineno - 1)
                     del classstack[-1]
-                module_name = m.group("ModuleName")
+                module_name = m.captured("ModuleName")
                 # remember this class
                 cur_class = RbModule(self.name, module_name, self.file, lineno)
                 # add nested Ruby modules to the file
@@ -1122,21 +1126,9 @@
                 acstack.append(["public", thisindent])
                 # default access control is 'public'
 
-            elif m.start("AccessControl") >= 0:
-                aclist = m.group("AccessControlList")
-                if aclist is None:
-                    index = -1
-                    while index >= -len(acstack):
-                        if acstack[index][1] < indent:
-                            actype = (
-                                m.group("AccessControlType")
-                                or m.group("AccessControlType2").split("_")[0]
-                            )
-                            acstack[index][0] = actype.lower()
-                            break
-                        else:
-                            index -= 1
-                else:
+            elif m.captured("AccessControl"):
+                aclist = m.captured("AccessControlList")
+                if aclist:
                     index = -1
                     while index >= -len(classstack):
                         if (
@@ -1146,8 +1138,8 @@
                         ):
                             parent = classstack[index][0]
                             actype = (
-                                m.group("AccessControlType")
-                                or m.group("AccessControlType2").split("_")[0]
+                                m.captured("AccessControlType")
+                                or m.captured("AccessControlType2").split("_")[0]
                             )
                             actype = actype.lower()
                             for name in aclist.split(","):
@@ -1165,8 +1157,20 @@
                             break
                         else:
                             index -= 1
+                else:
+                    index = -1
+                    while index >= -len(acstack):
+                        if acstack[index][1] < indent:
+                            actype = (
+                                m.captured("AccessControlType")
+                                or m.captured("AccessControlType2").split("_")[0]
+                            )
+                            acstack[index][0] = actype.lower()
+                            break
+                        else:
+                            index -= 1
 
-            elif m.start("Attribute") >= 0:
+            elif m.captured("Attribute"):
                 lineno += src.count("\n", last_lineno_pos, start)
                 last_lineno_pos = start
                 index = -1
@@ -1176,7 +1180,7 @@
                         and not isinstance(classstack[index][0], Function)
                         and classstack[index][1] < indent
                     ):
-                        attrName = m.group("AttributeName")
+                        attrName = m.captured("AttributeName")
                         attr = Attribute(self.name, attrName, self.file, lineno)
                         if attrName.startswith("@@") or attrName[0].isupper():
                             classstack[index][0].addGlobal(attrName, attr)
@@ -1186,7 +1190,7 @@
                     else:
                         index -= 1
                 else:
-                    attrName = m.group("AttributeName")
+                    attrName = m.captured("AttributeName")
                     if attrName[0] != "@":
                         attr = Attribute(self.name, attrName, self.file, lineno)
                         self.addGlobal(attrName, attr)
@@ -1194,7 +1198,7 @@
                         lastGlobalEntry.setEndLine(lineno - 1)
                     lastGlobalEntry = None
 
-            elif m.start("Attr") >= 0:
+            elif m.captured("Attr"):
                 lineno += src.count("\n", last_lineno_pos, start)
                 last_lineno_pos = start
                 index = -1
@@ -1205,25 +1209,9 @@
                         and classstack[index][1] < indent
                     ):
                         parent = classstack[index][0]
-                        if m.group("AttrType") is None:
-                            nv = m.group("AttrList").split(",")
-                            if not nv:
-                                break
-                            # get rid of leading ':'
-                            name = nv[0].strip()[1:]
-                            attr = (
-                                parent.getAttribute("@" + name)
-                                or parent.getAttribute("@@" + name)
-                                or Attribute(self.name, "@" + name, self.file, lineno)
-                            )
-                            if len(nv) == 1 or nv[1].strip() == "false":
-                                attr.setProtected()
-                            elif nv[1].strip() == "true":
-                                attr.setPublic()
-                            parent.addAttribute(attr.name, attr)
-                        else:
-                            access = m.group("AttrType")
-                            for name in m.group("AttrList").split(","):
+                        if m.captured("AttrType"):
+                            access = m.captured("AttrType")
+                            for name in m.captured("AttrList").split(","):
                                 # get rid of leading ':'
                                 name = name.strip()[1:]
                                 attr = (
@@ -1241,15 +1229,31 @@
                                     elif attr.isProtected():
                                         attr.setPublic()
                                 parent.addAttribute(attr.name, attr)
+                        else:
+                            nv = m.captured("AttrList").split(",")
+                            if not nv:
+                                break
+                            # get rid of leading ':'
+                            name = nv[0].strip()[1:]
+                            attr = (
+                                parent.getAttribute("@" + name)
+                                or parent.getAttribute("@@" + name)
+                                or Attribute(self.name, "@" + name, self.file, lineno)
+                            )
+                            if len(nv) == 1 or nv[1].strip() == "false":
+                                attr.setProtected()
+                            elif nv[1].strip() == "true":
+                                attr.setPublic()
+                            parent.addAttribute(attr.name, attr)
                         break
                     else:
                         index -= 1
 
-            elif m.start("Begin") >= 0:
+            elif m.captured("Begin"):
                 # a begin of a block we are not interested in
                 indent += 1
 
-            elif m.start("End") >= 0:
+            elif m.captured("End"):
                 # an end of a block
                 indent -= 1
                 if indent < 0:
@@ -1261,10 +1265,10 @@
                         indent = 0
 
             elif (
-                m.start("String") >= 0
-                or m.start("Comment") >= 0
-                or m.start("ClassIgnored") >= 0
-                or m.start("BeginEnd") >= 0
+                m.captured("String")
+                or m.captured("Comment")
+                or m.captured("ClassIgnored")
+                or m.captured("BeginEnd")
             ):
                 pass
 

eric ide

mercurial