From 119ff93461126585da9bdc0990159c2d30dbafd0 Mon Sep 17 00:00:00 2001 From: MS Date: Sat, 11 May 2024 18:40:31 -0400 Subject: [PATCH] Parser bugfix: vtable namespace (#910) --- tools/isledecomp/isledecomp/parser/util.py | 37 +++++++++++++--------- tools/isledecomp/tests/test_parser.py | 15 +++++++++ tools/isledecomp/tests/test_parser_util.py | 1 + 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/tools/isledecomp/isledecomp/parser/util.py b/tools/isledecomp/isledecomp/parser/util.py index bfe3803c..fbc78e10 100644 --- a/tools/isledecomp/isledecomp/parser/util.py +++ b/tools/isledecomp/isledecomp/parser/util.py @@ -69,32 +69,39 @@ def is_blank_or_comment(line: str) -> bool: ) -template_class_decl_regex = re.compile( - r"\s*(?:\/\/)?\s*(?:class|struct) (\w+)<([\w]+)\s*(\*+)?\s*>" +template_regex = re.compile(r"<(?P[\w]+)\s*(?P\*+)?\s*>") + + +class_decl_regex = re.compile( + r"\s*(?:\/\/)?\s*(?:class|struct) ((?:\w+(?:<.+>)?(?:::)?)+)" ) -class_decl_regex = re.compile(r"\s*(?:\/\/)?\s*(?:class|struct) (\w+)") +def template_replace(match: re.Match) -> str: + (type_name, asterisks) = match.groups() + if asterisks is None: + return f"<{type_name}>" + + return f"<{type_name} {asterisks}>" + + +def fix_template_type(class_name: str) -> str: + """For template classes, we should reformat the class name so it matches + the output from cvdump: one space between the template type and any asterisks + if it is a pointer type.""" + if "<" not in class_name: + return class_name + + return template_regex.sub(template_replace, class_name) def get_class_name(line: str) -> Optional[str]: """For VTABLE markers, extract the class name from the code line or comment where it appears.""" - match = template_class_decl_regex.match(line) - if match is not None: - # For template classes, we should reformat the class name so it matches - # the output from cvdump: one space between the template type and any asterisks - # if it is a pointer type. - (class_name, template_type, asterisks) = match.groups() - if asterisks is not None: - return f"{class_name}<{template_type} {asterisks}>" - - return f"{class_name}<{template_type}>" - match = class_decl_regex.match(line) if match is not None: - return match.group(1) + return fix_template_type(match.group(1)) return None diff --git a/tools/isledecomp/tests/test_parser.py b/tools/isledecomp/tests/test_parser.py index e748741a..c7905627 100644 --- a/tools/isledecomp/tests/test_parser.py +++ b/tools/isledecomp/tests/test_parser.py @@ -756,3 +756,18 @@ def test_virtual_inheritance(parser): assert parser.vtables[1].base_class == "Greetings" assert parser.vtables[2].base_class == "Howdy" assert all(v.name == "HiThere" for v in parser.vtables) + + +def test_namespace_in_comment(parser): + parser.read_lines( + [ + "// VTABLE: HELLO 0x1234", + "// class Tgl::Object", + "// VTABLE: HELLO 0x5555", + "// class TglImpl::RendererImpl", + ] + ) + + assert len(parser.vtables) == 2 + assert parser.vtables[0].name == "Tgl::Object" + assert parser.vtables[1].name == "TglImpl::RendererImpl" diff --git a/tools/isledecomp/tests/test_parser_util.py b/tools/isledecomp/tests/test_parser_util.py index 9936c5bc..e60f505c 100644 --- a/tools/isledecomp/tests/test_parser_util.py +++ b/tools/isledecomp/tests/test_parser_util.py @@ -126,6 +126,7 @@ def test_marker_dict_type_replace(): ("// class MxList", "MxList"), # I don't know if this would ever come up, but sure, why not? ("// class MxList", "MxList"), + ("// class Many::Name::Spaces", "Many::Name::Spaces"), ]