raylib-zig/lib/generate_functions.py

154 lines
4.5 KiB
Python

import re
"""
Automatic utility for generating raylib function headers.
"""
C_TO_ZIG = {
"bool": "bool",
"char": "u8",
"double": "f64",
"float": "f32",
"int": "c_int",
"long": "c_long",
"unsigned char": "u8",
"unsigned int": "c_uint",
}
# Some c types have a different sizes on different systems
# and zig knows that so we tell it to get the system specific size for us
def c_to_zig_type(c: str) -> str:
const = "const " if "const " in c else ""
c = c.replace("const ", "")
z = C_TO_ZIG.get(c)
if z is not None:
return const + z
return const + c
def fix_pointer(name: str, t: str):
pre = ""
while name.startswith("*"):
name = name[1:]
pre += "[*c]"
t = pre + t
if t == "[*c]const void":
t = "*const anyopaque"
elif t == "[*c]void":
t = "*anyopaque"
elif len(pre) == 0:
t = t.replace("const ", "")
return name, t
def fix_enums(arg_name, arg_type, func_name):
# Hacking specifc enums in here
# Raylib doesn't use the enums but rather the resulting ints
if arg_type == "int" or arg_type == "unsigned int":
if arg_name == "key":
arg_type = "KeyboardKey"
elif arg_name == "button":
if "Gamepad" in func_name:
arg_type = "GamepadButton"
else:
arg_type = "MouseButton"
elif arg_name == "mode" and func_name == "SetCameraMode":
arg_type = "CameraMode"
elif arg_name == "gesture":
arg_type = "Gestures"
elif arg_name == "flags" and func_name in ["SetWindowState", "ClearWindowState", "SetConfigFlags"]:
arg_type = "ConfigFlags"
return arg_type
def parse_header(header_name: str, output_file: str, prefix: str, *args: str):
header = open(header_name, mode="r")
zig_functions = []
zig_heads = []
zig_types = set()
leftover = ""
for line in header.readlines():
if line.startswith("typedef struct"):
zig_types.add(line.split(' ')[2])
elif line.startswith("typedef enum"):
# don't trip the general typedef case
pass
elif line.startswith("typedef "):
zig_types.add(line.split(' ')[2].replace(';', '').strip())
if not line.startswith(prefix):
continue
line = line.split(";", 1)[0]
if leftover:
line = leftover + line
leftover = ""
line = line.replace("* ", " *")
line = line.replace(",", ", ")
line = line.replace(" ", " ")
# each (.*) is some variable value
result = re.search(prefix + "(.*) (.*)start_arg(.*)end_arg(.*)", line.replace("(", "start_arg").replace(")", "end_arg"))
if result is None:
leftover += line
continue
# get whats in the (.*)'s
return_type = result.group(1)
func_name = result.group(2)
arguments = result.group(3)
if func_name == "SetTraceLogCallback":
continue
return_type = c_to_zig_type(return_type)
func_name, return_type = fix_pointer(func_name, return_type)
zig_arguments = []
for arg in arguments.split(", "):
if arg == "void":
break
if arg == "...":
zig_arguments.append("...")
continue
# everything but the last element (for stuff like "const Vector3")
arg_type = " ".join(arg.split(" ")[0:-1])
arg_name = arg.split(" ")[-1] # last element should be the name
arg_type = fix_enums(arg_name, arg_type, func_name)
arg_type = c_to_zig_type(arg_type)
arg_name, arg_type = fix_pointer(arg_name, arg_type)
zig_types.add(arg_type.replace("const ", ""))
zig_arguments.append(arg_name + ": " + arg_type) # put everything together
zig_arguments = ", ".join(zig_arguments)
zig_heads.append("pub extern fn " + func_name + "(" + zig_arguments + ") " + return_type + ";")
prelude = str()
for imp in args:
prelude += open(imp, mode="r").read()
zig_header = open(output_file, mode="w")
print(prelude, file=zig_header)
print("\n".join(zig_heads), file=zig_header)
print("", file=zig_header)
print("\n".join(zig_functions), file=zig_header)
if __name__ == "__main__":
parse_header("../raylib/src/raylib.h", "raylib-zig.zig", "RLAPI ", "raylib-zig-types.zig")
parse_header("../raylib/src/raymath.h", "raylib-zig-math.zig", "RMAPI ", "raylib-zig-math-prelude.zig")