raylib-zig/lib/generate_functions.py
2022-01-08 20:58:22 +01:00

141 lines
4.2 KiB
Python

import re
"""
Automatic utility for generating raylib function headers. Simply put
raylib.h in the working directory of this script and execute.
Tested with raylib version 3.7.0
"""
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:
c = c.replace("const ", "")
z = C_TO_ZIG.get(c)
if z is not None:
return z
return c
def fix_pointer(name: str, t: str):
t = t.replace("const ", "")
pre = ""
while name.startswith("*"):
name = name[1:]
pre += "[*c]"
if len(pre) != 0:
t = pre + "const " + t
if t == "[*c]const void":
t = "*const anyopaque"
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":
if arg_name == "key":
arg_type = "KeyboardKey"
elif arg_name == "button":
arg_type = "MouseButton"
elif arg_name == "mode" and func_name == "SetCameraMode":
arg_type = "CameraMode"
elif arg_name == "gesture":
arg_type = "Gestures"
return arg_type
def parse_header(header_name: str, output_file: str, prefix: 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)
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
arg_type = " ".join(arg.split(" ")[0:-1]) # everything but the last element (for stuff like "const Vector3")
arg_type = arg_type.replace("const ", "") # we'll add that later if needed
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)
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 + ";")
zigheader = open(output_file, mode="w")
print("""const rl = @import("raylib-zig.zig");\n""", file=zigheader)
print("\n".join(sorted(f"const {t} = rl.{t};" for t in zig_types if ('*' not in t) and (t not in C_TO_ZIG.values()))), file=zigheader)
print("", file=zigheader)
print("\n".join(zig_heads), file=zigheader)
print("", file=zigheader)
print("\n".join(zig_functions), file=zigheader)
parse_header("raylib.h", "raylib-wa.zig", "RLAPI ")
parse_header("raymath.h", "raylib-zig-math.zig", "RMDEF ")