From 8a2caee9d82c00b819bdd2bb572911f3f7b9a8d9 Mon Sep 17 00:00:00 2001 From: LE GARREC Vincent Date: Wed, 22 May 2024 23:12:24 +0200 Subject: [PATCH] Add support to variable with [] inside type (#85) * Add support to variable with [] inside type std::unique_ptr variable; * Type of "array_size" should always be the same * Add test for std::unique_ptr * Add support for initialization with {} int double {0}; int double {(1.2+3.2)}; * Fix format --- CppHeaderParser/CppHeaderParser.py | 88 +++++++++++++++++++++--------- test/test_CppHeaderParser.py | 85 ++++++++++++++++++++++++++++- 2 files changed, 145 insertions(+), 28 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index a7c2ce5..c6061e6 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -141,6 +141,7 @@ def trace_print(*args): _BRACE_REASON_OTHER = 0 _BRACE_REASON_NS = 1 _BRACE_REASON_EXTERN = 2 +_BRACE_REASON_VARIABLE = 3 # Track what was added in what order and at what depth parseHistory = [] @@ -239,10 +240,13 @@ def is_property_namestack(nameStack): r = False if "(" not in nameStack and ")" not in nameStack: r = True - elif ( - "(" in nameStack - and "=" in nameStack - and nameStack.index("=") < nameStack.index("(") + elif "(" in nameStack and ( + ( # = initialization + "=" in nameStack and nameStack.index("=") < nameStack.index("(") + ) + or ( # {} initialization + "{" in nameStack and nameStack.index("{") < nameStack.index("(") + ) ): r = True # See if we are a function pointer @@ -1217,29 +1221,48 @@ def __init__(self, nameStack, doxygen, location, is_var=True, **kwargs): else: self["extern"] = False + if "=" in nameStack: + self["type"] = " ".join(nameStack[: nameStack.index("=") - 1]) + self["name"] = nameStack[nameStack.index("=") - 1] + default = " ".join(nameStack[nameStack.index("=") + 1 :]) + nameStack = nameStack[: nameStack.index("=")] + default = self._filter_name(default) + self["default"] = default + # backwards compat; deprecate camelCase in dicts + self["defaultValue"] = default + elif "{" in nameStack and "}" in nameStack: + posBracket = nameStack.index("{") + self["type"] = " ".join(nameStack[: posBracket - 1]) + self["name"] = nameStack[posBracket - 1] + default = " ".join(nameStack[posBracket + 1 : -1]) + nameStack = nameStack[:posBracket] + default = self._filter_name(default) + self["default"] = default + # backwards compat; deprecate camelCase in dicts + self["defaultValue"] = default + _stack_ = nameStack - if "[" in nameStack: # strip off array informatin - arrayStack = nameStack[nameStack.index("[") :] - if nameStack.count("[") > 1: + self["array"] = 0 + while "]" in nameStack[-1]: # strip off array information + arrayPos = len(nameStack) - 1 - nameStack[::-1].index("[") + arrayStack = nameStack[arrayPos:] + if self["array"] == 1: debug_print("Multi dimensional array") debug_print("arrayStack=%s", arrayStack) - nums = [x for x in arrayStack if x.isdigit()] - # Calculate size by multiplying all dimensions - p = 1 - for n in nums: - p *= int(n) + if len(arrayStack) == 3: + n = arrayStack[1] # Multi dimensional array - self["array_size"] = p + if not "multi_dimensional_array_size" in self: + self["multi_dimensional_array_size"] = self["array_size"] + self["multi_dimensional_array_size"] += "x" + n + self["array_size"] = str(int(self["array_size"]) * int(n)) self["multi_dimensional_array"] = 1 - self["multi_dimensional_array_size"] = "x".join(nums) else: debug_print("Array") if len(arrayStack) == 3: self["array_size"] = arrayStack[1] - nameStack = nameStack[: nameStack.index("[")] + nameStack = nameStack[:arrayPos] self["array"] = 1 - else: - self["array"] = 0 nameStack = self._name_stack_helper(nameStack) if doxygen: @@ -1268,15 +1291,6 @@ def __init__(self, nameStack, doxygen, location, is_var=True, **kwargs): ) self["function_pointer"] = 1 - elif "=" in nameStack: - self["type"] = " ".join(nameStack[: nameStack.index("=") - 1]) - self["name"] = nameStack[nameStack.index("=") - 1] - default = " ".join(nameStack[nameStack.index("=") + 1 :]) - default = self._filter_name(default) - self["default"] = default - # backwards compat; deprecate camelCase in dicts - self["defaultValue"] = default - elif ( is_fundamental(nameStack[-1]) or nameStack[-1] in [">", "<", ":", "."] @@ -2931,7 +2945,23 @@ def __init__( continue if parenDepth == 0 and tok.type == "{": - self.lastBraceReason = _BRACE_REASON_OTHER + if self.nameStack[0] in ( + "class", + "struct", + "union", + "namespace", + "enum", + "extern", + "typedef", + ) or (is_method_namestack(self.stack) or (not self.curClass)): + self.lastBraceReason = _BRACE_REASON_OTHER + else: + # Case : type variable {init}; + self.lastBraceReason = _BRACE_REASON_VARIABLE + self.braceDepth += 1 + self.braceReason.append(self.lastBraceReason) + self.nameStack.append(tok.value) + continue if len(self.nameStack) >= 2 and is_namespace( self.nameStack ): # namespace {} with no name used in boost, this sets default? @@ -2991,6 +3021,10 @@ def __init__( self.linkage_stack.pop() self.stack = [] # clear stack when linkage ends? self.stmtTokens = [] + # Case : type variable {init}; + elif reason == _BRACE_REASON_VARIABLE: + self.nameStack.append(tok.value) + continue else: self._evaluate_stack() self.braceDepth -= 1 diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index 5714e6c..4f949f5 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -2359,7 +2359,7 @@ def setUp(self): def test_array_size(self): self.assertEqual( self.cppHeader.classes["Picture"]["properties"]["public"][1]["array_size"], - 16384, + "16384", ) def test_multi_dimensional_array_size(self): @@ -4222,5 +4222,88 @@ def test_fn(self): self.assertEqual(fn["linkage"], "") +# Github PR 85 +class ContainerOfArray_TestCase(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +class ContainerOfArray { +public: + std::unique_ptr variable; + std::unique_ptr function(std::unique_ptr param1); +}; +""", + "string", + ) + + def test_rtntype(self): + self.assertEqual( + self.cppHeader.classes["ContainerOfArray"]["methods"]["public"][0][ + "rtnType" + ], + "std::unique_ptr", + ) + + def test_parameters(self): + self.assertEqual( + filter_pameters( + self.cppHeader.classes["ContainerOfArray"]["methods"]["public"][0][ + "parameters" + ] + ), + [{"name": "param1", "desc": None, "type": "std::unique_ptr"}], + ) + + def test_member(self): + self.assertEqual( + self.cppHeader.classes["ContainerOfArray"]["properties"]["public"][0][ + "name" + ], + "variable", + ) + self.assertEqual( + self.cppHeader.classes["ContainerOfArray"]["properties"]["public"][0][ + "type" + ], + "std::unique_ptr", + ) + + +class InitBracket_TestCase(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +class InitBracket { +public: + int variable{10}; + std::shared_ptr variable2 {std::make_shared(150)}; +}; +""", + "string", + ) + + def test_member(self): + self.assertEqual( + self.cppHeader.classes["InitBracket"]["properties"]["public"][0]["name"], + "variable", + ) + self.assertEqual( + self.cppHeader.classes["InitBracket"]["properties"]["public"][0][ + "defaultValue" + ], + "10", + ) + self.assertEqual( + self.cppHeader.classes["InitBracket"]["properties"]["public"][1]["name"], + "variable2", + ) + self.assertEqual( + self.cppHeader.classes["InitBracket"]["properties"]["public"][1][ + "defaultValue" + ], + "std::make_shared ( 150 )", + ) + + if __name__ == "__main__": unittest.main()