diff --git a/bindings/generate.py b/bindings/generate.py new file mode 100644 index 0000000..29029ec --- /dev/null +++ b/bindings/generate.py @@ -0,0 +1,116 @@ +import json +import sys +import clang.cindex +from clang.cindex import CursorKind, TypeKind, Type + + +def map_type(clang_type: Type): + canonical = clang_type.get_canonical() + kind = canonical.kind + spelling = ( + canonical.spelling.replace("const ", "") + .replace("volatile ", "") + .replace("restrict ", "") + ) + + if kind == TypeKind.POINTER: + pointee = canonical.get_pointee() + if pointee.kind == TypeKind.CHAR_S or pointee.kind == TypeKind.CHAR_U: + return "cstring" + + return f"^{map_type(pointee)}" + + if kind == TypeKind.CONSTANTARRAY: + element_type = canonical.get_array_element_type() + size = canonical.get_array_size() + return f"[{size}]{map_type(element_type)}" + + if kind == TypeKind.FUNCTIONPROTO or kind == TypeKind.FUNCTIONNOPROTO: + arg_types = [] + + for arg in canonical.argument_types(): + arg_types.append(map_type(arg)) + + mapped_return = map_type(canonical.get_result()) + args_str = ", ".join(arg_types) + + return f"func({args_str}): {mapped_return}" + + if kind == TypeKind.VOID: + return "void" + + if kind == TypeKind.BOOL: + return "bool" + + if kind in [TypeKind.CHAR_S, TypeKind.SCHAR]: + return "i8" + if kind == TypeKind.CHAR_U or kind == TypeKind.UCHAR: + return "u8" + if kind == TypeKind.SHORT: + return "i16" + if kind == TypeKind.USHORT: + return "u16" + if kind == TypeKind.INT: + return "i32" + if kind == TypeKind.UINT: + return "u32" + if kind in [TypeKind.LONG, TypeKind.LONGLONG]: + return "i64" + if kind in [TypeKind.ULONG, TypeKind.ULONGLONG]: + return "u64" + + if kind == TypeKind.FLOAT: + return "f32" + if kind == TypeKind.DOUBLE or kind == TypeKind.LONGDOUBLE: + return "f64" + + if kind == TypeKind.RECORD: + if not spelling.startswith("struct "): + name = clang_type.get_canonical().spelling + raise Exception(f"Unknown custom type: {name}") + + return spelling.replace("struct ", "") + + name = clang_type.get_canonical().spelling + raise Exception(f"Unresolved type: {name}") + + +filename = "../examples/raylib/raylib-5.5_linux_amd64/include/raylib.h" + +index = clang.cindex.Index.create() + +args = ["-x", "c"] + +tu = index.parse(filename, args=args) + +if tu.diagnostics: + for diag in tu.diagnostics: + if diag.severity >= clang.cindex.Diagnostic.Error: + print(f"Error: {diag.spelling}", file=sys.stderr) + +for cursor in tu.cursor.walk_preorder(): + if cursor.kind == CursorKind.FUNCTION_DECL: + name = cursor.spelling + return_type = map_type(cursor.result_type) + + params = [] + for arg in cursor.get_arguments(): + param_name = arg.spelling + param_type = map_type(arg.type) + params.append(f"{param_name}: {param_type}") + + params_str = ", ".join(params) + + print(f'export extern "{name}" func {name}({params_str}): {return_type}') + elif cursor.kind == CursorKind.STRUCT_DECL: + name = cursor.spelling + print(f"export struct {name}") + print("{") + for field in cursor.get_children(): + if field.kind == CursorKind.FIELD_DECL: + field_name = field.spelling + field_type = map_type(field.type) + print(f" {field_name}: {field_type}") + else: + raise Exception(f"Unsupported child of struct: {field.spelling}") + print("}")