From 22f18b248b17757b326d77a6e42061932651ae3a Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 15 Nov 2020 23:11:13 -0500 Subject: [PATCH 01/12] Reorder some conditions for performance --- CppHeaderParser/CppHeaderParser.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 2825efb..75e0fd2 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -3280,12 +3280,12 @@ def _evaluate_stack(self, token=None): not self.curClass and "typedef" in self.nameStack and ( - ( + self.stack[-1] == ";" + or ( "struct" not in self.nameStack and "union" not in self.nameStack and "enum" not in self.nameStack ) - or self.stack[-1] == ";" ) ): debug_print("trace") @@ -3350,7 +3350,7 @@ def _evaluate_stack(self, token=None): # lookup is done alias = self.current_namespace() + alias self.using[alias] = atype - elif is_method_namestack(self.stack) and "(" in self.nameStack: + elif "(" in self.nameStack and is_method_namestack(self.stack): debug_print("trace") self._evaluate_method_stack() elif is_enum_namestack(self.nameStack): @@ -3377,7 +3377,7 @@ def _evaluate_stack(self, token=None): self.classes[new_name] = type_to_rename if new_name != type_name_to_rename: del self.classes[type_name_to_rename] - elif is_property_namestack(self.nameStack) and self.stack[-1] == ";": + elif self.stack[-1] == ";" and is_property_namestack(self.nameStack): debug_print("trace") if self.nameStack[0] in ("class", "struct") and len(self.stack) == 3: self.evalute_forward_decl() From 286001c57161f107655213ce1434355df919fd1a Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Mon, 16 Nov 2020 00:35:50 -0500 Subject: [PATCH 02/12] Replace consume_parens with generic balanced consumer --- CppHeaderParser/CppHeaderParser.py | 66 +++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 14 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 75e0fd2..626028b 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -419,21 +419,59 @@ def _fix_classname(self): return s -def _consume_parens(stack): - i = 0 +_end_balanced_items = {">", "}", "]", ")", "]]"} +_start_balanced_items = { + "<": ">", + "{": "}", + "(": ")", + "[": "]", + "[[": "]]", +} + + +def _consume_balanced_items(stack, init_expected, i): + """ + identical to consume_balanced_tokens, but works on a stack instead + TODO: make them the same function + + :param stack: Stack of tokens to search + :param init_expected: expected token to balance + :param i: Position in stack of initial token + + :returns: position of next token after balanced token + """ + match_stack = deque((init_expected,)) sl = len(stack) - nested = 1 - while i < sl: - t = stack[i] + + while True: i += 1 - if t == ")": - nested -= 1 - if nested == 0: - return i - elif t == "(": - nested += 1 + if i >= sl: + errmsg = "Did not find matching '%s'" % init_expected + raise CppParseError(errmsg) + + tok = stack[i] + if tok in _end_balanced_items: + expected = match_stack.pop() + if tok != expected: + # hack: ambiguous right-shift issues here, really + # should be looking at the context + if tok == ">": + i += 1 + if i < sl and stack[i] == ">": + match_stack.append(expected) + continue + + errmsg = "Expected '%s', found '%s'" % (expected, tok) + raise CppParseError(errmsg) + + if len(match_stack) == 0: + return i + 1 + + continue - raise CppParseError("Unmatched (") + next_end = _start_balanced_items.get(tok) + if next_end: + match_stack.append(next_end) def _parse_template_decl(stack): @@ -472,7 +510,7 @@ def _parse_template_decl(stack): require_ending = True elif t == "(": s = stack[i:] - n = _consume_parens(s) + n = _consume_balanced_items(s, ")", -1) i += n param["param"] = param["param"] + "".join(s[:n]) else: @@ -583,7 +621,7 @@ def _parse_cpp_base(stack, default_access): if t == "(": s = stack[i:] - n = _consume_parens(s) + n = _consume_balanced_items(s, ")", -1) i += n base["decl_name"] = base["decl_name"] + "".join(s[:n]) elif t == "...": From 4ecb8a49f96fa459a3b48d428fb77df7cf3f252e Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Mon, 16 Nov 2020 02:49:38 -0500 Subject: [PATCH 03/12] Remove unnecessary operator() check --- CppHeaderParser/CppHeaderParser.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 626028b..a813bbb 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -3288,13 +3288,6 @@ def _evaluate_stack(self, token=None): debug_caller_lineno, ) - # Handle special case of overloading operator () - if "operator()(" in "".join(self.nameStack): - operator_index = self.nameStack.index("operator") - self.nameStack.pop(operator_index + 2) - self.nameStack.pop(operator_index + 1) - self.nameStack[operator_index] = "operator()" - if len(self.curClass): debug_print("%s (%s) ", self.curClass, self.curAccessSpecifier) else: From 694536e7127b176472d1267495805e7b0b70a1ea Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Mon, 16 Nov 2020 23:56:44 -0500 Subject: [PATCH 04/12] Fix =default constructor/destructor detection ... apparently the unit tests were totally broken, go figure --- CppHeaderParser/CppHeaderParser.py | 9 +++------ test/TestSampleClass.h | 4 ++-- test/test_CppHeaderParser.py | 20 +++++++++++++++++++- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index a813bbb..c6d8162 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2233,6 +2233,9 @@ def parse_method_type(self, stack): info["pure_virtual"] = True elif stack[-2] == "delete": info["deleted"] = True + elif stack[-2] == "default": + info["default"] = True + info["defined"] = True r = header.split() name = None @@ -2274,15 +2277,9 @@ def parse_method_type(self, stack): if name.startswith("~"): info["destructor"] = True - if "default;" in stack: - info["defined"] = True - info["default"] = True name = name[1:] elif not a or (name == self.curClass and len(self.curClass)): info["constructor"] = True - if "default;" in stack: - info["defined"] = True - info["default"] = True info["name"] = name diff --git a/test/TestSampleClass.h b/test/TestSampleClass.h index 439635c..53d505f 100644 --- a/test/TestSampleClass.h +++ b/test/TestSampleClass.h @@ -780,13 +780,13 @@ int non_vararg_func(int foo, const char* fmt); class DefaultConstDest { public: DefaultConstDest() =default ; // spacing check - DefaultConstDest() = default ; // spacing check + ~DefaultConstDest() = default ; // spacing check }; // default constructor on a class containing "default" as name (edge case check) class default_class_tricky { public: default_class_tricky(); - default_class_tricky(); + ~default_class_tricky(); void randomMethod1_default(); void defaultrandomMethod2(); diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index 1be0344..b50b9fb 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -2648,7 +2648,7 @@ def test_Grackle_const_noexcept_noexcept_operator(self): # Test enhancement 13 (default constructor / destructor) -class DefaultConstDest_TestCase: +class DefaultConstDest_TestCase(unittest.TestCase): def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") @@ -3807,6 +3807,24 @@ def test_fn(self): self.assertEqual(props[1]["name"], "y") +class Default_TestCase(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +class A { +public: + A() = default; +}; +""", + "string", + ) + + def test_fn(self): + m = self.cppHeader.classes["A"]["methods"]["public"][0] + self.assertEqual(m["constructor"], True) + self.assertEqual(m["default"], True) + + class Deleted_TestCase(unittest.TestCase): def setUp(self): self.cppHeader = CppHeaderParser.CppHeader( From 443f5e3e4da9279e63eda4a2e7e76cc6fe82d4d5 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Tue, 8 Dec 2020 02:23:39 -0500 Subject: [PATCH 05/12] Get rid of _classes_brace_level, change curClass to the class object instead of a string --- CppHeaderParser/CppHeaderParser.py | 88 +++++++++++++----------------- 1 file changed, 37 insertions(+), 51 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index c6d8162..23c2745 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -1510,7 +1510,6 @@ def initextra(self): self.stack = ( [] ) # full name stack, good idea to keep both stacks? (simple stack and full stack) - self._classes_brace_level = {} # class name : level self._forward_decls = [] self._template_typenames = [] # template @@ -2278,7 +2277,7 @@ def parse_method_type(self, stack): if name.startswith("~"): info["destructor"] = True name = name[1:] - elif not a or (name == self.curClass and len(self.curClass)): + elif not a or (self.curClass and name == self.curClass["name"]): info["constructor"] = True info["name"] = name @@ -2353,15 +2352,15 @@ def _evaluate_method_stack(self): newMethod["path"] = klass["name"] elif self.curClass: # normal case + klass = self.curClass newMethod = CppMethod( self.nameStack, - self.curClass, + klass["name"], info, self.curTemplate, self._get_stmt_doxygen(), self._get_location(self.nameStack), ) - klass = self.classes[self.curClass] klass["methods"][self.curAccessSpecifier].append(newMethod) newMethod["parent"] = klass if klass["namespace"]: @@ -2453,11 +2452,10 @@ def _evaluate_property_stack(self, clearStack=True, addToVar=None): if self.curClass: typedef = self._parse_typedef(self.stack) name = typedef["name"] - klass = self.classes[self.curClass] - klass["typedefs"][self.curAccessSpecifier].append(name) + self.curClass["typedefs"][self.curAccessSpecifier].append(name) if self.curAccessSpecifier == "public": - klass._public_typedefs[name] = typedef["type"] - Resolver.SubTypedefs[name] = self.curClass + self.curClass._public_typedefs[name] = typedef["type"] + Resolver.SubTypedefs[name] = self.curClass["name"] else: assert 0 elif self.curClass: @@ -2510,7 +2508,7 @@ def _evaluate_property_stack(self, clearStack=True, addToVar=None): ) newVar["namespace"] = self.current_namespace() if self.curClass: - klass = self.classes[self.curClass] + klass = self.curClass klass["properties"][self.curAccessSpecifier].append(newVar) newVar["property_of_class"] = klass["name"] newVar["parent"] = klass @@ -2589,27 +2587,26 @@ def _evaluate_class_stack(self): classKey = newClass["name"] if parent: - newClass["namespace"] = self.classes[parent]["namespace"] + "::" + parent - newClass["parent"] = self.classes[parent] + newClass["namespace"] = parent["namespace"] + "::" + parent["name"] + newClass["parent"] = parent newClass["access_in_parent"] = self.accessSpecifierStack[-1] - self.classes[parent]["nested_classes"].append(newClass) + parent["nested_classes"].append(newClass) ## supports nested classes with the same name ## - self.curClass = key = parent + "::" + classKey - self._classes_brace_level[key] = self.braceDepth + key = parent["name"] + "::" + classKey elif newClass["parent"]: # nested class defined outside of parent. A::B {...} pcls = newClass["parent"] - parent = pcls["name"] - newClass["namespace"] = pcls["namespace"] + "::" + parent + parentName = pcls["name"] + newClass["namespace"] = pcls["namespace"] + "::" + parentName pcls["nested_classes"].append(newClass) ## supports nested classes with the same name ## - self.curClass = key = parent + "::" + classKey - self._classes_brace_level[key] = self.braceDepth + key = parentName + "::" + classKey else: newClass["namespace"] = self.cur_namespace() - self.curClass = key = classKey - self._classes_brace_level[classKey] = self.braceDepth + key = classKey + + self.curClass = newClass if not key.endswith("::") and not key.endswith(" ") and len(key) != 0: if key in self.classes: @@ -2628,10 +2625,9 @@ def evalute_forward_decl(self): assert self.nameStack[0] in ("class", "struct") name = self.nameStack[-1] if self.curClass: - klass = self.classes[self.curClass] - klass["forward_declares"][self.curAccessSpecifier].append(name) + self.curClass["forward_declares"][self.curAccessSpecifier].append(name) if self.curAccessSpecifier == "public": - klass._public_forward_declares.append(name) + self.curClass._public_forward_declares.append(name) else: self._forward_decls.append(name) @@ -2705,7 +2701,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): headerFileStr = headerFileName else: raise Exception("Arg type must be either file or string") - self.curClass = "" + self.curClass = None # nested classes have parent::nested, but no extra namespace, # this keeps the API compatible, TODO proper namespace for everything. @@ -2977,24 +2973,14 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self._evaluate_stack() self.braceDepth -= 1 - # if self.curClass: - # debug_print( - # "CURBD %s", self._classes_brace_level[self.curClass] - # ) - - if (self.braceDepth == 0) or ( - self.curClass - and self._classes_brace_level[self.curClass] == self.braceDepth - ): + if self.braceDepth == 0 or self.curClass: trace_print("END OF CLASS DEF") if self.accessSpecifierStack: self.curAccessSpecifier = self.accessSpecifierStack[-1] self.accessSpecifierStack = self.accessSpecifierStack[:-1] - if self.curClass and self.classes[self.curClass]["parent"]: - thisClass = self.classes[self.curClass] - self.curClass = self.curClass[ - : -(len(thisClass["name"]) + 2) - ] + if self.curClass and self.curClass["parent"]: + thisClass = self.curClass + self.curClass = self.curClass["parent"] # Detect anonymous union members if ( @@ -3014,7 +3000,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.nameStack = [] self.stmtTokens = [] else: - self.curClass = "" + self.curClass = None self.stack = [] self.stmtTokens = [] elif tok.type in _namestack_append_tokens: @@ -3115,7 +3101,6 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): "anon_struct_counter", "anon_union_counter", "anon_class_counter", - "_classes_brace_level", "_forward_decls", "stack", "mainClass", @@ -3124,6 +3109,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): "stmtTokens", "typedefs_order", "curTemplate", + "curClass", ]: del self.__dict__[key] @@ -3285,10 +3271,10 @@ def _evaluate_stack(self, token=None): debug_caller_lineno, ) - if len(self.curClass): - debug_print("%s (%s) ", self.curClass, self.curAccessSpecifier) - else: - debug_print(" (%s) ", self.curAccessSpecifier) + # if len(self.curClass): + # debug_print("%s (%s) ", self.curClass, self.curAccessSpecifier) + # else: + # debug_print(" (%s) ", self.curAccessSpecifier) # Filter special case of array with casting in it try: @@ -3372,7 +3358,7 @@ def _evaluate_stack(self, token=None): atype["raw_type"] = ns + atype["type"] if self.curClass: - klass = self.classes[self.curClass] + klass = self.curClass klass["using"][alias] = atype else: # lookup is done @@ -3436,10 +3422,11 @@ def _evaluate_stack(self, token=None): else: debug_print("Discarded statement %s", self.nameStack) + className = self.curClass["name"] if self.curClass else "" try: - self.nameStackHistory[self.braceDepth] = (nameStackCopy, self.curClass) + self.nameStackHistory[self.braceDepth] = (nameStackCopy, className) except: - self.nameStackHistory.append((nameStackCopy, self.curClass)) + self.nameStackHistory.append((nameStackCopy, className)) # its a little confusing to have some if/else above return and others not, and then clearning the nameStack down here self.nameStack = [] @@ -3595,12 +3582,11 @@ def _parse_enum(self): self._install_enum(newEnum, instancesData) def _install_enum(self, newEnum, instancesData): - if len(self.curClass): + if self.curClass: newEnum["namespace"] = self.cur_namespace(False) - klass = self.classes[self.curClass] - klass["enums"][self.curAccessSpecifier].append(newEnum) + self.curClass["enums"][self.curAccessSpecifier].append(newEnum) if self.curAccessSpecifier == "public" and "name" in newEnum: - klass._public_enums[newEnum["name"]] = newEnum + self.curClass._public_enums[newEnum["name"]] = newEnum else: newEnum["namespace"] = self.cur_namespace(True) self.enums.append(newEnum) From f67e878c76f6d3eba7bf647b8da4a02c53824184 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Wed, 9 Dec 2020 02:18:38 -0500 Subject: [PATCH 06/12] Make typedef detection simpler --- CppHeaderParser/CppHeaderParser.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 23c2745..a36935a 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -3289,10 +3289,14 @@ def _evaluate_stack(self, token=None): except: pass + if len(self.nameStack) == 0: + debug_print("trace (Empty Stack)") + return + # if 'typedef' in self.nameStack: self._evaluate_typedef() # allows nested typedefs, probably a bad idea - if ( + elif ( not self.curClass - and "typedef" in self.nameStack + and self.nameStack[0] == "typedef" and ( self.stack[-1] == ";" or ( @@ -3307,9 +3311,6 @@ def _evaluate_stack(self, token=None): self._evaluate_typedef() return - elif len(self.nameStack) == 0: - debug_print("trace (Empty Stack)") - return elif self.nameStack[0] == "namespace": # Taken care of outside of here pass From 41dc4e8c617ef98b5095572c30aa6b27c63baa03 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Fri, 11 Dec 2020 00:29:47 -0500 Subject: [PATCH 07/12] Remove unused local variables --- CppHeaderParser/CppHeaderParser.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index a36935a..41efd28 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -3005,7 +3005,6 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.stmtTokens = [] elif tok.type in _namestack_append_tokens: self.nameStack.append(tok.value) - nameStackAppended = True elif tok.type in _namestack_pass_tokens: pass elif tok.type in _namestack_str_tokens: @@ -3045,10 +3044,8 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): elif tok.type == "(": parenDepth += 1 self.nameStack.append(tok.value) - nameStackAppended = True elif tok.type == ")": self.nameStack.append(tok.value) - nameStackAppended = True if parenDepth != 0: parenDepth -= 1 From 279337beb3f309b0deb2dae05b6ddefd92108664 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Fri, 11 Dec 2020 01:03:07 -0500 Subject: [PATCH 08/12] Use precomputed length --- CppHeaderParser/CppHeaderParser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 41efd28..291be6e 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2912,7 +2912,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): continue if parenDepth == 0 and tok.type == "{": - if len(self.nameStack) >= 2 and is_namespace( + if nslen >= 2 and is_namespace( self.nameStack ): # namespace {} with no name used in boost, this sets default? if ( From 7baa8b2210777bdc2a577dd9492f3845fa3c9825 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Thu, 10 Dec 2020 23:49:09 -0500 Subject: [PATCH 09/12] Additional consumption helpers --- CppHeaderParser/CppHeaderParser.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 291be6e..a939162 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -3146,6 +3146,16 @@ def _next_token_must_be(self, *tokenTypes): raise self._parse_error((tok,), "' or '".join(tokenTypes)) return tok + def _consume_up_to(self, rtoks, *token_types): + token = self.lex.token + while True: + tok = token() + rtoks.append(tok) + if tok.type in token_types: + break + + return rtoks + _end_balanced_tokens = {">", "}", "]", ")", "DBL_RBRACKET"} _balanced_token_map = { "<": ">", From d3b6f97ce7266c3884f1dbc11c5070c0389beda3 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Fri, 11 Dec 2020 00:06:06 -0500 Subject: [PATCH 10/12] Rework method for renaming types in typedefs - Removes nameStackHistory, which was only used for this --- CppHeaderParser/CppHeaderParser.py | 77 ++++++++++++++++-------------- 1 file changed, 41 insertions(+), 36 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index a939162..fc15f08 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2443,6 +2443,29 @@ def _evaluate_typedef(self): if name not in self.typedefs_order: self.typedefs_order.append(name) + def _finish_struct_typedef(self): + # Look for the name of a typedef struct: struct typedef {...] StructName; or unions to get renamed + debug_print("finish struct typedef") + self.typedef_encountered = False + + toks = self._consume_up_to([], ";") + + # grab the first name token, TODO: typedef struct{} X, *PX; + for tok in toks: + if tok.type == "NAME": + new_name = tok.value + break + else: + return + + type_name_to_rename = self.curClass["name"] + type_to_rename = self.classes[type_name_to_rename] + type_to_rename["name"] = new_name + # Now re install it in its new location + self.classes[new_name] = type_to_rename + if new_name != type_name_to_rename: + del self.classes[type_name_to_rename] + def _evaluate_property_stack(self, clearStack=True, addToVar=None): """Create a Property out of the name stack""" global parseHistory @@ -2756,12 +2779,13 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): "curAccessSpecifier changed/defaulted to %s", self.curAccessSpecifier ) self.initextra() - # Old namestacks for a given level - self.nameStackHistory = [] + self.anon_struct_counter = 0 self.anon_union_counter = 0 self.anon_class_counter = 0 + self.typedef_encountered = False + #: Using directives in this header outside of class scope: key is #: full name for lookup, value is :class:`.CppVariable` self.using = {} @@ -2978,6 +3002,10 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): if self.accessSpecifierStack: self.curAccessSpecifier = self.accessSpecifierStack[-1] self.accessSpecifierStack = self.accessSpecifierStack[:-1] + + if self.curClass and self.typedef_encountered: + self._finish_struct_typedef() + if self.curClass and self.curClass["parent"]: thisClass = self.curClass self.curClass = self.curClass["parent"] @@ -3094,10 +3122,10 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): "nameSpaces", "curAccessSpecifier", "accessSpecifierStack", - "nameStackHistory", "anon_struct_counter", "anon_union_counter", "anon_class_counter", + "typedef_encountered", "_forward_decls", "stack", "mainClass", @@ -3269,8 +3297,6 @@ def _discard_ctor_initializer(self): def _evaluate_stack(self, token=None): """Evaluates the current name stack""" - nameStackCopy = self.nameStack[:] - debug_print( "Evaluating stack %s\n BraceDepth: %s (called from %s)", self.nameStack, @@ -3380,25 +3406,7 @@ def _evaluate_stack(self, token=None): self._parse_enum() self.stack = [] self.stmtTokens = [] - elif ( - len(self.nameStack) == 1 - and len(self.nameStackHistory) > self.braceDepth - and ( - self.nameStackHistory[self.braceDepth][0][0:2] == ["typedef", "struct"] - or self.nameStackHistory[self.braceDepth][0][0:2] - == ["typedef", "union"] - ) - ): - # Look for the name of a typedef struct: struct typedef {...] StructName; or unions to get renamed - debug_print("found the naming of a union") - type_name_to_rename = self.nameStackHistory[self.braceDepth][1] - new_name = self.nameStack[0] - type_to_rename = self.classes[type_name_to_rename] - type_to_rename["name"] = self.nameStack[0] - # Now re install it in its new location - self.classes[new_name] = type_to_rename - if new_name != type_name_to_rename: - del self.classes[type_name_to_rename] + elif self.stack[-1] == ";" and is_property_namestack(self.nameStack): debug_print("trace") if self.nameStack[0] in ("class", "struct") and len(self.stack) == 3: @@ -3410,13 +3418,16 @@ def _evaluate_stack(self, token=None): else: self._evaluate_property_stack() # catches class props and structs in a namespace - elif ( - self.nameStack[0] in ("class", "struct", "union") - or self.nameStack[0] == "typedef" - and self.nameStack[1] in ("struct", "union") + elif self.nameStack[0] in ("class", "struct", "union"): + debug_print("trace") + self._evaluate_class_stack() + + elif self.nameStack[0] == "typedef" and self.nameStack[1] in ( + "struct", + "union", ): - # Parsing a union can reuse much of the class parsing debug_print("trace") + self.typedef_encountered = True self._evaluate_class_stack() elif not self.curClass: @@ -3430,12 +3441,6 @@ def _evaluate_stack(self, token=None): else: debug_print("Discarded statement %s", self.nameStack) - className = self.curClass["name"] if self.curClass else "" - try: - self.nameStackHistory[self.braceDepth] = (nameStackCopy, className) - except: - self.nameStackHistory.append((nameStackCopy, className)) - # its a little confusing to have some if/else above return and others not, and then clearning the nameStack down here self.nameStack = [] self.lex.doxygenCommentCache = "" @@ -3684,7 +3689,7 @@ def _strip_parent_keys(self): for k in obj.keys(): trace_print("-Try key", k) trace_print("-type", type(obj[k])) - if k in ["nameStackHistory", "parent", "_public_typedefs"]: + if k in ["parent", "_public_typedefs"]: continue if type(obj[k]) == list: for i in obj[k]: From 2b4c6285ed6b81c74bc4f5a84dbe1c784e5d3bce Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Fri, 11 Dec 2020 02:15:53 -0500 Subject: [PATCH 11/12] classes must end with semicolon --- test/TestSampleClass.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/TestSampleClass.h b/test/TestSampleClass.h index 53d505f..08ce500 100644 --- a/test/TestSampleClass.h +++ b/test/TestSampleClass.h @@ -307,7 +307,7 @@ extern "C" { class ExternClass { ExternClass(); - } + }; }; // Bug 3514671 @@ -502,14 +502,14 @@ class BlueJay : public Bird, public virtual Food class Pea : public Vegetable { int i; -} +}; // Bug 3567172 class Pear { enum Stem stem_property; -} +}; // Bug 3567854 and 3568241 struct Beans @@ -572,7 +572,7 @@ class ClassAfterMagicMacro { public: ClassAfterMagicMacro(); -} +}; // Bug BitBucket #4 typedef unsigned int uint; @@ -583,7 +583,7 @@ typedef std::map StrStrMap; class AfterTypedefClass { public: -} +}; // Bug BitBucket #5 class Herb @@ -633,7 +633,7 @@ class Plumb class Peach * Plumb::myMethod( class Peach * pInPtr ) { return pInPtr; -} +}; // Bug BitBucket #9 class Grape @@ -667,7 +667,7 @@ class Hen public: void add(int a=100, b=0xfd, float c=1.7e-3, float d=3.14); void join(string s1="", string s2="nothing"); -} +}; // Bug BitBucket #19 template Date: Fri, 11 Dec 2020 02:28:52 -0500 Subject: [PATCH 12/12] Remove parseHistory, improve class ending - Support anonymous structs/classes in addition to unions --- CppHeaderParser/CppHeaderParser.py | 121 ++++++++++++----------------- 1 file changed, 51 insertions(+), 70 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index fc15f08..c1e7af3 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -142,10 +142,6 @@ def trace_print(*args): ignoreSymbols = ["Q_OBJECT"] -# Track what was added in what order and at what depth -parseHistory = [] - - def is_namespace(nameStack): """Determines if a namespace is being specified""" if len(nameStack) == 0: @@ -2379,14 +2375,6 @@ def _evaluate_method_stack(self): ) newMethod["parent"] = None self.functions.append(newMethod) - global parseHistory - parseHistory.append( - { - "braceDepth": self.braceDepth, - "item_type": "method", - "item": newMethod, - } - ) else: trace_print("free function?", self.nameStack) @@ -2443,14 +2431,12 @@ def _evaluate_typedef(self): if name not in self.typedefs_order: self.typedefs_order.append(name) - def _finish_struct_typedef(self): + def _finish_struct_typedef(self, toks): # Look for the name of a typedef struct: struct typedef {...] StructName; or unions to get renamed debug_print("finish struct typedef") self.typedef_encountered = False - toks = self._consume_up_to([], ";") - - # grab the first name token, TODO: typedef struct{} X, *PX; + # grab the first name token for tok in toks: if tok.type == "NAME": new_name = tok.value @@ -2458,6 +2444,7 @@ def _finish_struct_typedef(self): else: return + # TODO: typedef struct{} X, *PX; type_name_to_rename = self.curClass["name"] type_to_rename = self.classes[type_name_to_rename] type_to_rename["name"] = new_name @@ -2466,10 +2453,52 @@ def _finish_struct_typedef(self): if new_name != type_name_to_rename: del self.classes[type_name_to_rename] + def _finish_class_def(self): + + # starting at the last } of a class/struct/union + debug_print("finish_class_def") + + # consume any names for parsing + toks = self._consume_up_to([], ";") + + is_typedef = self.typedef_encountered + if is_typedef: + self._finish_struct_typedef(toks) + + thisClass = self.curClass + self.curClass = thisClass["parent"] + + if not is_typedef: + if len(toks) > 1: + # Deal with "struct { } x;" style of things + expected_types = {",", "NAME", "*", ";"} + stack = [thisClass["name"]] + for tok in toks: + stack.append(tok.value) + if tok.type not in expected_types: + self._parse_error((tok,), ",".join(expected_types)) + + self.nameStack = stack[:-1] + self.stack = stack + self.stmtTokens = toks + + self._evaluate_property_stack(clearStack=False) + + elif self.curClass and thisClass["name"].startswith("<"): + # anonymous class struct/union + stack = [thisClass["name"], "", ";"] + self.nameStack = stack[:-1] + self.stack = stack + + self._evaluate_property_stack(clearStack=False) + + self.stack = [] + self.nameStack = [] + self.stmtTokens = [] + def _evaluate_property_stack(self, clearStack=True, addToVar=None): """Create a Property out of the name stack""" - global parseHistory - debug_print("trace") + debug_print("evaluate_property_stack") if self.nameStack[0] == "typedef": assert self.stack and self.stack[-1] == ";" if self.curClass: @@ -2482,21 +2511,6 @@ def _evaluate_property_stack(self, clearStack=True, addToVar=None): else: assert 0 elif self.curClass: - if len(self.nameStack) == 1: - # See if we can de anonymize the type - filteredParseHistory = [ - h for h in parseHistory if h["braceDepth"] == self.braceDepth - ] - if ( - len(filteredParseHistory) - and filteredParseHistory[-1]["item_type"] == "class" - ): - self.nameStack.insert(0, filteredParseHistory[-1]["item"]["name"]) - debug_print( - "DEANONYMOIZING %s to type '%s'", - self.nameStack[1], - self.nameStack[0], - ) if "," in self.nameStack: # Maybe we have a variable list # Figure out what part is the variable separator but remember templates of function pointer # First find left most comma outside of a > and ) @@ -2535,9 +2549,6 @@ def _evaluate_property_stack(self, clearStack=True, addToVar=None): klass["properties"][self.curAccessSpecifier].append(newVar) newVar["property_of_class"] = klass["name"] newVar["parent"] = klass - parseHistory.append( - {"braceDepth": self.braceDepth, "item_type": "variable", "item": newVar} - ) if addToVar: newVar.update(addToVar) else: @@ -2638,10 +2649,6 @@ def _evaluate_class_stack(self): newClass.show() assert key not in self.classes # namespace collision self.classes[key] = newClass - global parseHistory - parseHistory.append( - {"braceDepth": self.braceDepth, "item_type": "class", "item": newClass} - ) def evalute_forward_decl(self): trace_print("FORWARD DECL", self.nameStack) @@ -3003,34 +3010,9 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.curAccessSpecifier = self.accessSpecifierStack[-1] self.accessSpecifierStack = self.accessSpecifierStack[:-1] - if self.curClass and self.typedef_encountered: - self._finish_struct_typedef() + if self.curClass: + self._finish_class_def() - if self.curClass and self.curClass["parent"]: - thisClass = self.curClass - self.curClass = self.curClass["parent"] - - # Detect anonymous union members - if ( - self.curClass - and thisClass["declaration_method"] == "union" - and thisClass["name"].startswith("<") - and self.lex.token_if(";") - ): - debug_print("Creating anonymous union") - # Force the processing of an anonymous union - self.nameStack = [""] - self.stack = self.nameStack + [";"] - debug_print("pre eval anon stack") - self._evaluate_stack(";") - debug_print("post eval anon stack") - self.stack = [] - self.nameStack = [] - self.stmtTokens = [] - else: - self.curClass = None - self.stack = [] - self.stmtTokens = [] elif tok.type in _namestack_append_tokens: self.nameStack.append(tok.value) elif tok.type in _namestack_pass_tokens: @@ -3110,8 +3092,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): ) self.finalize() - global parseHistory - parseHistory = [] + # Delete some temporary variables for key in [ "_precomp_macro_buf", @@ -3181,7 +3162,7 @@ def _consume_up_to(self, rtoks, *token_types): rtoks.append(tok) if tok.type in token_types: break - + return rtoks _end_balanced_tokens = {">", "}", "]", ")", "DBL_RBRACKET"} pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy