vulkan-project/convert_to_cpp.py

241 lines
6.8 KiB
Python
Raw Normal View History

2022-10-27 00:56:29 +02:00
#!/bin/python3
""" A python script to transform a vulkan project to use vulkan.hpp instead of the c api
Copyright © 2022 Matthias Quintern.
This software comes with no warranty.
This software is licensed under the GPL3
"""
from os import makedirs, mkdir, path, listdir, chdir
from sys import argv, exit
import re
from bs4 import BeautifulSoup as bs
filetypes = [ ".hpp", ".cpp" ]
no_delete = False
def error(s):
print(s)
exit(1)
def case_to_camel(s: str) -> str:
"""
turn all letters following a uppercase letter to lowercase
turn letter following " _0-9" to uppercase
remove " " and "_"
"""
ret = ""
make_lower = False
make_upper = False
for c in s:
if c in "_ 0123456789":
make_lower = False
make_upper = True
if c in "_ ":
continue
else:
ret += c
continue
if make_lower:
ret += c.lower()
elif make_upper:
ret += c.upper()
else:
ret += c
make_upper = False
make_lower = True
return ret
def case_first_letter_low(s: str) -> str:
return s[0].lower() + s[1:]
def delete(s) -> str:
global no_delete
if no_delete:
return "// no-delete " + s
else:
return ""
enum_suffixes = [ "KHR", "EXT", "AMD", "NV"]
vk_soup = bs()
def init_enum_names():
global vk_soup
with open("vk.xml", "r") as vk:
spec = vk.read()
vk_soup = bs(spec, features="lxml")
new_token_begin_chars = r"[<>()\[\]{} ,;=&|\t\n]"
# functions that take the line and a token and return the transformed line
re_vk_function: str = new_token_begin_chars + r"(vk[A-Z][a-zA-Z]+)\("
re_vk_function_device:str = r"vk[a-zA-Z]+\(((?:device)|(?:vk.getDevice\(\))), "
def vk_function(line:str, function:str) -> str:
match = re.search(re_vk_function_device, line)
if match:
new_function:str = match.groups()[0] + "." + case_first_letter_low(function.removeprefix("vk"))
return line.replace(match.groups()[0] + ", ", "").replace(function, new_function)
else:
new_function = "vk::" + case_first_letter_low(function.removeprefix("vk"))
return line.replace(function, new_function)
re_vk_type: str = new_token_begin_chars + r"(Vk[A-Z][a-zA-Z]+)"
def vk_type(line:str, type_:str) -> str:
new_type = "vk::" + type_.removeprefix("Vk")
return line.replace(type_, new_type)
# 0 -> name, 1 -> suffixes, 2 -> iteration
re_vk_enum_name: str = "Vk([a-zA-Z]+?)(?:FlagBits)?((?:KHR)|(?:EXT)|(?:AMD)|(?:NV))*([2-9]?)$"
re_vk_enum_val: str = new_token_begin_chars + r"(VK_(?:[A-Z0-9]+_)+([A-Z0-9]+))"
def vk_enum_val(line: str, enum_val: str) -> str:
new_enum_val = "vk::"
# name is VkName[FlagBits][Suffix][2] -> VK_NAME[_BIT][_SUFFIX][2]
tag = vk_soup.find(attrs={"name":enum_val})
if not tag:
print("skipping", enum_val, "(not found)")
return line
enum_val_2 = case_to_camel(enum_val)
parent = tag.parent
if not parent.attrs.__contains__("name"):
print("skipping", enum_val, "(no parent name)")
return line
# print(enum_val_2)
# print(parent["name"], parent["type"])
match = re.search(re_vk_enum_name, parent["name"])
if match:
name = match.groups()[0]
suffixes = match.groups()[1]
iteration = match.groups()[2]
# enum name
new_enum_val += parent["name"].removeprefix("Vk") + "::"
# enum val name
print("\tenum_val_2", enum_val_2)
print("\tname", name)
print("\tparent['name']", parent['name'])
print("\tsuffixes", suffixes)
print("\titeration", iteration)
new_enum_val += "e" + enum_val_2.removeprefix("Vk" + name)
if suffixes:
new_enum_val = new_enum_val.removesuffix(case_to_camel(suffixes))
new_enum_val = new_enum_val.removesuffix("Bit")
else:
print("skipping", enum_val, "(no parent name match)")
return line
print(new_enum_val, "\n")
return line.replace(enum_val, new_enum_val)
def vk_str(line, m):
# same match with different group
match = re.search(r"STR_VK_[A-Z]+\((.+)\)", line)
if match:
return line.replace(m, match.groups()[0])
else:
return line
# f = f(line, match_group) -> line
re_and_f = {
re_vk_function: vk_function,
re_vk_type: vk_type,
re_vk_enum_val: vk_enum_val,
r"(STR_VK_[A-Z]+\(.+\))": vk_str,
}
re_vk_struct_init: str = r"vk::[a-zA-Z2]+ [a-zA-Z]+ ?\{\};"
def vk_struct_init(s):
return s.replace("};", "").replace("{", " {").replace(" {", " {")
re_vk_struct_member_init: str = r"([a-zA-Z]+(?:(?:CI)|(?:AI)|(?:I)|(?:MI)))\.[a-zA-Z0-9]+ = .+;"
def vk_struct_member_init(s):
match= re.search(re_vk_struct_member_init, s)
if match:
return s.replace(match.groups()[0], "\t").replace(";", ",")
else:
return s
# f = f(line) -> line
re_no_group_and_f = {
"#include <vulkan/vulkan_core.h>": lambda l : "#define VULKAN_HPP_NO_CONSTRUCTORS\n" + l.replace("vulkan_core.h>", "vulkan.hpp>"),
r"\.sType ?=": lambda l : delete(l),
"VK_SUCCESS": lambda l : l.replace("VK_SUCCESS", "vk::Result::eSuccess"),
re_vk_struct_init: vk_struct_init,
re_vk_struct_member_init: vk_struct_member_init,
}
def transform_file(filename:str, outfilename):
"""
"""
print("transform_file:", filename)
if not path.isfile(filename): error("File does not exist:" + filename)
with open(filename, "r") as file:
lines = file.readlines()
for i in range(len(lines)):
line = lines[i]
for reg, f in re_and_f.items():
for m in re.finditer(reg, line):
line = f(line, m.groups()[0])
line = line
for reg, f in re_no_group_and_f.items():
if re.search(reg, line):
line = f(line)
lines[i] = line
with open(outfilename, "w") as file:
file.writelines(lines)
# with open(filename, "w") as file:
# file.writelines(lines)
def process_path(path_):
# print("Processing path:", path_)
if path.isfile(path_) and path.splitext(path_)[-1] in filetypes:
makedirs("new/" + path.dirname(path_), exist_ok=True)
transform_file(path_, "new/" + path_)
else:
if path.isdir(path_):
for p in listdir(path_):
process_path(path_ + "/" + p)
def print_help():
print("""
-h --help help
--no-delete comment lines instead of deleting them"""
)
def missing_arg(arg):
print("Missing argument for", arg)
exit(1)
if __name__ == "__main__":
i = 1
while i in range(1, len(argv)):
if argv[i] == "--help" or argv[i] == "-h":
print_help()
exit(0)
elif argv[i] == "--no-delete":
no_delete = True
i += 1
init_enum_names()
process_path(path.curdir)