Skip to content

Commit

Permalink
RSN Parser: Fixed header generation failure with clang-16.0.0 (close #…
Browse files Browse the repository at this point in the history
  • Loading branch information
MikhailGorobets authored Sep 12, 2023
1 parent 575a001 commit 77ed652
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 41 deletions.
2 changes: 1 addition & 1 deletion RenderStateNotation/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ file(MAKE_DIRECTORY "${RSN_PARSER_GENERATED_HEADERS_DIR}")

find_package(Python3 REQUIRED)

execute_process(COMMAND ${Python3_EXECUTABLE} -m pip install libclang==15.0.6.1 # Fails with 16.0.0
execute_process(COMMAND ${Python3_EXECUTABLE} -m pip install libclang==16.0.0
RESULT_VARIABLE PYTHON_PIP_LIBCLANG_RESULT)
if(NOT PYTHON_PIP_LIBCLANG_RESULT EQUAL "0")
message(FATAL_ERROR "${Python3_EXECUTABLE} -m pip install libclang failed with ${PYTHON_PIP_LIBCLANG_RESULT}")
Expand Down
2 changes: 2 additions & 0 deletions RenderStateNotation/scripts/cxx_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from cxx_parser import generate_file, generate_common, generate_filename
from cxx_config import *


def main():
parser = ArgumentParser("Generate sources files")
parser.add_argument("--dir",
Expand All @@ -53,5 +54,6 @@ def main():
generate_common(os.path.join(args.dir, generated_filename))
print(f"Generate CXX -> {generated_filename}")


if __name__ == "__main__":
main()
102 changes: 62 additions & 40 deletions RenderStateNotation/scripts/cxx_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,78 +42,91 @@
CXX_PRAGMA_ONCE = "#pragma once"
CXX_NAMESPACE = "namespace {}"

CXX_PATTERN_INTERFACE = r"(Diligent::I[A-Z].*[a-z])|(struct I[A-Z].*[a-z])"
CXX_PATTERN_STRING = r"const(\s)+(char|Diligent::Char)(\s)+\*$"
CXX_PATTERN_INTERFACE = r"(I[A-Z].*[a-z])|(struct I[A-Z].*[a-z])"
CXX_PATTERN_STRING = r"const(\s)+(char|Char)(\s)+\*$"


def filter_by_file(nodes: typing.Iterable[Cursor], file_name: str) -> typing.Iterable[Cursor]:
return [node for node in nodes if node.location.file.name == file_name]
return [node for node in nodes if node.location.file.name == file_name]


def filter_by_node_kind(nodes: typing.Iterable[Cursor], kinds: list) -> typing.Iterable[Cursor]:
return [node for node in nodes if node.kind in kinds]


def filter_namespace(nodes: typing.Iterable[Cursor], namespace_name: str) -> typing.Iterable[Cursor]:
return [node for node in nodes if node.kind in [CursorKind.NAMESPACE] and node.spelling == namespace_name ]
return [node for node in nodes if node.kind in [CursorKind.NAMESPACE] and node.spelling == namespace_name]


def filter_bitwise_enum(translation_unit):
#TODO Ugly Code
# TODO Ugly Code
bitwise_enum = set()
for namespace in filter_namespace(translation_unit.cursor.get_children(), 'Diligent'):
for unexposed in filter_by_node_kind(namespace.get_children(), [CursorKind.UNEXPOSED_DECL]):
for unexposed in filter_by_node_kind(namespace.get_children(), [CursorKind.UNEXPOSED_DECL]):
for function in filter_by_node_kind(unexposed.get_children(), [CursorKind.FUNCTION_DECL]):
for param in filter_by_node_kind(function.get_children(), [CursorKind.PARM_DECL]):
name_type = param.type.spelling.split("::")[1]
if function.spelling == "operator&":
arguments = filter_by_node_kind(function.get_children(), [CursorKind.PARM_DECL])
name_type = arguments[0].type.spelling
if name_type in CXX_REGISTERED_ENUM:
bitwise_enum.add(name_type)
return list(bitwise_enum)


def find_all_fields(cursor: Cursor, bitwise_enum, base_structs) -> typing.Iterable[typing.Tuple[str, Type]]:
#TODO Ugly Code
# TODO Ugly Code
result = []
field_declarations = filter_by_node_kind(cursor.get_children(), [CursorKind.FIELD_DECL, CursorKind.UNION_DECL])
reference_types = [TypeKind.POINTER]

for struct_name in base_structs:
if struct_name in CXX_REGISTERD_BASE_STRUCT:
result.append(CXX_REGISTERD_BASE_STRUCT[struct_name])
result.append(CXX_REGISTERD_BASE_STRUCT[struct_name])
else:
raise Exception(f'Unexpected base struct: {struct_name}')

for node in field_declarations:
expression = replace_raw_type(node.type.spelling)
if (re.match(CXX_PATTERN_STRING, node.type.spelling)) is not None:
result.append({ 'name': node.displayname, 'type': node.type.spelling, 'meta': 'string' })
elif (node.type.kind == TypeKind.POINTER) and (re.match(CXX_PATTERN_INTERFACE, node.type.spelling) is not None):
cxx_type = node.type.get_named_type() if node.kind == TypeKind.ELABORATED else node.type
print(expression)
if (re.match(CXX_PATTERN_STRING, cxx_type.spelling)) is not None:
result.append({'name': node.displayname, 'type': cxx_type.spelling, 'meta': 'string'})
elif (cxx_type.kind == TypeKind.POINTER) and (re.match(CXX_PATTERN_INTERFACE, cxx_type.spelling) is not None):
pass
elif (node.type.get_declaration().is_anonymous()):
result.extend([{ 'name': union.displayname, 'type': union.type.spelling, 'meta': 'union' } for union in node.get_children() ])
elif (node.type.kind == TypeKind.CONSTANTARRAY):
result.append({ 'name': node.displayname, 'type': node.type.spelling, 'meta': 'const_array' })
elif (node.type.kind == TypeKind.ENUM) and expression in bitwise_enum:
result.append({ 'name': node.displayname, 'type': node.type.spelling, 'meta': 'bitwise' })
elif (node.type.kind in reference_types):
result.append({ 'name': node.displayname, 'type': node.type.spelling, 'meta': 'pointer' })
elif cxx_type.get_declaration().is_anonymous():
result.extend([{'name': union.displayname, 'type': union.type.spelling, 'meta': 'union'} for union in node.get_children()])
elif cxx_type.kind == TypeKind.CONSTANTARRAY:
result.append({'name': node.displayname, 'type': cxx_type.spelling, 'meta': 'const_array'})
elif expression in bitwise_enum:
result.append({'name': node.displayname, 'type': cxx_type.spelling, 'meta': 'bitwise'})
elif cxx_type.kind in reference_types:
result.append({'name': node.displayname, 'type': cxx_type.spelling, 'meta': 'pointer'})
elif expression in CXX_REGISTERED_STRUCT:
result.append({ 'name': node.displayname, 'type': node.type.spelling, 'meta': 'struct' })
result.append({'name': node.displayname, 'type': cxx_type.spelling, 'meta': 'struct'})
else:
result.append({ 'name': node.displayname, 'type': node.type.spelling, 'meta': '' })

result.append({'name': node.displayname, 'type': cxx_type.spelling, 'meta': ''})
return result


def find_all_base_structs(cursor: Cursor):
return [node.referenced.spelling for node in cursor.get_children() if node.kind == CursorKind.CXX_BASE_SPECIFIER]


def find_all_xitems(cursor: Cursor) -> typing.Iterable[str]:
xitems = [node.displayname for node in filter_by_node_kind(cursor.get_children(), [CursorKind.ENUM_CONSTANT_DECL])]
return [{ 'value': x, 'name': y} for x, y in zip(xitems, replace_enum_string(xitems))]
return [{'value': x, 'name': y} for x, y in zip(xitems, replace_enum_string(xitems))]


def compute_all_enums(enums):
return {node.spelling: find_all_xitems(node) for node in enums if node.spelling in CXX_REGISTERED_ENUM }
return {node.spelling: find_all_xitems(node) for node in enums if node.spelling in CXX_REGISTERED_ENUM}


def compute_all_structs(structs, bitwise_enum):
return { struct.spelling : { 'fields': find_all_fields(struct, bitwise_enum, find_all_base_structs(struct)) } for struct in structs if struct.spelling in CXX_REGISTERED_STRUCT }
return {struct.spelling: {'fields': find_all_fields(struct, bitwise_enum, find_all_base_structs(struct))} for struct
in structs if struct.spelling in CXX_REGISTERED_STRUCT}


def compute_all_fields_size(struct_field_map):
#TODO Ugly Code
# TODO Ugly Code
size_fields_map = {}
size_fields_map_inv = {}
condition = lambda x: x['meta'] == 'pointer'
Expand All @@ -135,19 +148,22 @@ def compute_all_fields_size(struct_field_map):

return (size_fields_map, size_fields_map_inv)


def replace_enum_string(strings: typing.Iterable[str]) -> typing.Iterable[str]:
prefix = os.path.commonprefix(strings)
return [ string.replace(prefix, '') for string in strings ]
return [string.replace(prefix, '') for string in strings]


def replace_raw_type(string):
string = string.replace('Diligent', '')
string = string.replace('::', '')
string = string.replace('struct', '')
string = string.replace('enum', '')
string = string.replace('*', '')
string = string.replace(' ', '')
string = string.strip()
return string


def generate_file(input_filename, output_filename):
index = Index.create()
translation_unit = index.parse(input_filename, args=['-x', 'c++', '-std=c++14'])
Expand All @@ -170,27 +186,32 @@ def generate_file(input_filename, output_filename):
field_size_map = compute_all_fields_size(struct_field_map)

with cpp.block(CXX_NAMESPACE.format(namespace.spelling)):
cpp.write(CXX_ENUM_SERIALIZE_TEMPLATE.render(enums=emum_xitems_map.items()))
cpp.write(CXX_STRUCT_SERIALIZE_TEMPLATE.render(structs=struct_field_map.items(), field_size=field_size_map[0], field_size_inv=field_size_map[1]))
cpp.write(CXX_ENUM_SERIALIZE_TEMPLATE.render(enums=emum_xitems_map.items()))
cpp.write(
CXX_STRUCT_SERIALIZE_TEMPLATE.render(structs=struct_field_map.items(), field_size=field_size_map[0],
field_size_inv=field_size_map[1]))
cpp.write_line()

cpp.save(output_filename)


def generate_common(output_filename):
cpp = CXXBuilder()
cpp.write_line(CXX_LICENCE)
cpp.write_line(CXX_PRAGMA_ONCE)
cpp.write_line()
cpp = CXXBuilder()
cpp.write_line(CXX_LICENCE)
cpp.write_line(CXX_PRAGMA_ONCE)
cpp.write_line()

with cpp.block(CXX_NAMESPACE.format("Diligent")):
cpp.write(CXX_COMMON_SERIALIZE_TEMPLATE.render())
cpp.write_line()
cpp.save(output_filename)

with cpp.block(CXX_NAMESPACE.format("Diligent")):
cpp.write(CXX_COMMON_SERIALIZE_TEMPLATE.render())
cpp.write_line()
cpp.save(output_filename)

def generate_filename(input_filename):
base_name = os.path.splitext(os.path.basename(input_filename))[0]
return f"{base_name}{CXX_SUFFIX_FILE}.{CXX_EXTENSION_FILE}"


def main():
parser = ArgumentParser()
parser.add_argument("--dir",
Expand All @@ -205,5 +226,6 @@ def main():

generate_file(args.file, os.path.join(args.dir, generate_filename(args.file)))


if __name__ == "__main__":
main()

0 comments on commit 77ed652

Please sign in to comment.