308 lines
14 KiB
Zig
308 lines
14 KiB
Zig
const std = @import("std");
|
|
const Allocator = std.mem.Allocator;
|
|
const io = @import("io");
|
|
|
|
var fmtBuffer: [256]u8 = undefined;
|
|
/// Convert C type to Zig type
|
|
/// Simple table-based conversion for SDL3 types
|
|
pub fn convertType(c_type: []const u8, allocator: Allocator) ![]const u8 {
|
|
const trimmed = std.mem.trim(u8, c_type, " \t");
|
|
|
|
// Handle opaque struct pointers: "struct X *" -> "*anyopaque"
|
|
if (std.mem.startsWith(u8, trimmed, "struct ") and std.mem.endsWith(u8, trimmed, " *")) {
|
|
return try allocator.dupe(u8, "*anyopaque");
|
|
}
|
|
|
|
// Handle function pointers: For now, just return as placeholder until we implement full conversion
|
|
if (std.mem.indexOf(u8, trimmed, "(SDLCALL *") != null or std.mem.indexOf(u8, trimmed, "(*") != null) {
|
|
// TODO: Implement full function pointer conversion
|
|
// For now, return a placeholder type
|
|
return try std.fmt.allocPrint(allocator, "?*const anyopaque", .{});
|
|
}
|
|
|
|
// Handle array types: "Uint8[2]" -> "[2]u8"
|
|
if (std.mem.indexOf(u8, trimmed, "[")) |bracket_pos| {
|
|
const base_type = std.mem.trim(u8, trimmed[0..bracket_pos], " \t");
|
|
var array_part = trimmed[bracket_pos..]; // "[2]"
|
|
|
|
// Recursively convert the base type
|
|
const zig_base = try convertType(base_type, allocator);
|
|
defer allocator.free(zig_base);
|
|
|
|
const inner = array_part[1 .. array_part.len - 1];
|
|
if (inner.len > 0) {
|
|
var alias_to_c: bool = false;
|
|
|
|
_ = std.fmt.parseInt(u32, inner, 0) catch {
|
|
// not a real error
|
|
alias_to_c = true;
|
|
};
|
|
|
|
if (alias_to_c) {
|
|
var b = std.io.fixedBufferStream(&fmtBuffer);
|
|
_ = try b.write("[");
|
|
_ = try b.write("c.");
|
|
_ = try b.write(inner);
|
|
_ = try b.write("]");
|
|
array_part = b.getWritten();
|
|
std.debug.print("arrya_part = {s}\n", .{array_part});
|
|
}
|
|
}
|
|
|
|
// Return Zig array notation: [size]Type
|
|
return try std.fmt.allocPrint(allocator, "{s}{s}", .{ array_part, zig_base });
|
|
}
|
|
|
|
// Primitives
|
|
if (std.mem.eql(u8, trimmed, "void")) return try allocator.dupe(u8, "void");
|
|
if (std.mem.eql(u8, trimmed, "bool")) return try allocator.dupe(u8, "bool");
|
|
if (std.mem.eql(u8, trimmed, "SDL_bool")) return try allocator.dupe(u8, "bool");
|
|
if (std.mem.eql(u8, trimmed, "float")) return try allocator.dupe(u8, "f32");
|
|
if (std.mem.eql(u8, trimmed, "double")) return try allocator.dupe(u8, "f64");
|
|
if (std.mem.eql(u8, trimmed, "char")) return try allocator.dupe(u8, "u8");
|
|
if (std.mem.eql(u8, trimmed, "int")) return try allocator.dupe(u8, "c_int");
|
|
if (std.mem.eql(u8, trimmed, "va_list")) return try allocator.dupe(u8, "std.builtin.VaList");
|
|
|
|
// SDL integer types
|
|
if (std.mem.eql(u8, trimmed, "Uint8")) return try allocator.dupe(u8, "u8");
|
|
if (std.mem.eql(u8, trimmed, "Uint16")) return try allocator.dupe(u8, "u16");
|
|
if (std.mem.eql(u8, trimmed, "Uint32")) return try allocator.dupe(u8, "u32");
|
|
if (std.mem.eql(u8, trimmed, "Uint64")) return try allocator.dupe(u8, "u64");
|
|
if (std.mem.eql(u8, trimmed, "Sint8")) return try allocator.dupe(u8, "i8");
|
|
if (std.mem.eql(u8, trimmed, "Sint16")) return try allocator.dupe(u8, "i16");
|
|
if (std.mem.eql(u8, trimmed, "Sint32")) return try allocator.dupe(u8, "i32");
|
|
if (std.mem.eql(u8, trimmed, "Sint64")) return try allocator.dupe(u8, "i64");
|
|
if (std.mem.eql(u8, trimmed, "size_t")) return try allocator.dupe(u8, "usize");
|
|
if (std.mem.eql(u8, trimmed, "intptr_t")) return try allocator.dupe(u8, "isize");
|
|
|
|
// Common pointer types
|
|
if (std.mem.eql(u8, trimmed, "const char *")) return try allocator.dupe(u8, "[*c]const u8");
|
|
if (std.mem.eql(u8, trimmed, "const char **")) return try allocator.dupe(u8, "[*c][*c]const u8");
|
|
if (std.mem.eql(u8, trimmed, "const char * const *")) return try allocator.dupe(u8, "[*c]const [*c]const u8");
|
|
if (std.mem.eql(u8, trimmed, "char *")) return try allocator.dupe(u8, "[*c]u8");
|
|
if (std.mem.eql(u8, trimmed, "char **")) return try allocator.dupe(u8, "[*c][*c]u8");
|
|
if (std.mem.eql(u8, trimmed, "char**")) return try allocator.dupe(u8, "[*c][*c]u8");
|
|
if (std.mem.eql(u8, trimmed, "void *")) return try allocator.dupe(u8, "?*anyopaque");
|
|
if (std.mem.eql(u8, trimmed, "const void *")) return try allocator.dupe(u8, "?*const anyopaque");
|
|
if (std.mem.eql(u8, trimmed, "void **")) return try allocator.dupe(u8, "[*c]?*anyopaque");
|
|
if (std.mem.eql(u8, trimmed, "const Uint8 *")) return try allocator.dupe(u8, "[*c]const u8");
|
|
if (std.mem.eql(u8, trimmed, "Uint8 *")) return try allocator.dupe(u8, "[*c]u8");
|
|
if (std.mem.eql(u8, trimmed, "Uint8 **")) return try allocator.dupe(u8, "[*c][*c]u8");
|
|
if (std.mem.eql(u8, trimmed, "const int *")) return try allocator.dupe(u8, "[*c]const c_int");
|
|
|
|
// Handle SDL types with pointers
|
|
// Check for double pointers like "SDL_Type **" or "SDL_Type *const *" or "SDL_Type * const *"
|
|
if (std.mem.startsWith(u8, trimmed, "SDL_")) {
|
|
// Match "SDL_Type *const *" (no space before const)
|
|
if (std.mem.indexOf(u8, trimmed, " *const *")) |pos| {
|
|
const base_type = trimmed[4..pos]; // Remove SDL_ prefix and get type
|
|
return std.fmt.allocPrint(allocator, "[*c]*const {s}", .{base_type});
|
|
}
|
|
// Match "SDL_Type * const *" (space before const)
|
|
if (std.mem.indexOf(u8, trimmed, " * const *")) |pos| {
|
|
const base_type = trimmed[4..pos]; // Remove SDL_ prefix and get type
|
|
return std.fmt.allocPrint(allocator, "[*c]*const {s}", .{base_type});
|
|
}
|
|
if (std.mem.indexOf(u8, trimmed, " **")) |pos| {
|
|
const base_type = trimmed[4..pos]; // Remove SDL_ prefix and get type
|
|
return std.fmt.allocPrint(allocator, "[*c][*c]{s}", .{base_type});
|
|
}
|
|
}
|
|
|
|
// Handle primitive pointer types
|
|
if (std.mem.eql(u8, trimmed, "int *")) return try allocator.dupe(u8, "*c_int");
|
|
if (std.mem.eql(u8, trimmed, "bool *")) return try allocator.dupe(u8, "*bool");
|
|
if (std.mem.eql(u8, trimmed, "size_t *")) return try allocator.dupe(u8, "*usize");
|
|
if (std.mem.eql(u8, trimmed, "float *")) return try allocator.dupe(u8, "*f32");
|
|
if (std.mem.eql(u8, trimmed, "const float *")) return try allocator.dupe(u8, "*const f32");
|
|
if (std.mem.eql(u8, trimmed, "double *")) return try allocator.dupe(u8, "*f64");
|
|
if (std.mem.eql(u8, trimmed, "Uint8 *")) return try allocator.dupe(u8, "*u8");
|
|
if (std.mem.eql(u8, trimmed, "Uint16 *")) return try allocator.dupe(u8, "*u16");
|
|
if (std.mem.eql(u8, trimmed, "Uint32 *")) return try allocator.dupe(u8, "*u32");
|
|
if (std.mem.eql(u8, trimmed, "Uint64 *")) return try allocator.dupe(u8, "*u64");
|
|
if (std.mem.eql(u8, trimmed, "Sint8 *")) return try allocator.dupe(u8, "*i8");
|
|
if (std.mem.eql(u8, trimmed, "Sint16 *")) return try allocator.dupe(u8, "*i16");
|
|
if (std.mem.eql(u8, trimmed, "Sint32 *")) return try allocator.dupe(u8, "*i32");
|
|
if (std.mem.eql(u8, trimmed, "const bool *")) return try allocator.dupe(u8, "*const bool");
|
|
|
|
if (std.mem.startsWith(u8, trimmed, "const ")) {
|
|
const rest = trimmed[6..];
|
|
if (std.mem.endsWith(u8, rest, " *") or std.mem.endsWith(u8, rest, "*")) {
|
|
const base_type = if (std.mem.endsWith(u8, rest, " *"))
|
|
rest[0 .. rest.len - 2]
|
|
else
|
|
rest[0 .. rest.len - 1];
|
|
if (std.mem.startsWith(u8, base_type, "SDL_")) {
|
|
// const SDL_Foo * -> *const Foo
|
|
const zig_type = base_type[4..]; // Remove SDL_
|
|
return std.fmt.allocPrint(allocator, "*const {s}", .{zig_type});
|
|
}
|
|
}
|
|
}
|
|
|
|
if (std.mem.endsWith(u8, trimmed, " *") or std.mem.endsWith(u8, trimmed, "*")) {
|
|
const base_type = if (std.mem.endsWith(u8, trimmed, " *"))
|
|
trimmed[0 .. trimmed.len - 2]
|
|
else
|
|
trimmed[0 .. trimmed.len - 1];
|
|
if (std.mem.startsWith(u8, base_type, "SDL_")) {
|
|
// SDL_Foo * or SDL_Foo* -> ?*Foo (nullable for opaque types from C)
|
|
const zig_type = base_type[4..]; // Remove SDL_
|
|
return std.fmt.allocPrint(allocator, "?*{s}", .{zig_type});
|
|
}
|
|
}
|
|
|
|
// Handle SDL types without pointers
|
|
if (std.mem.startsWith(u8, trimmed, "SDL_")) {
|
|
// SDL_Foo -> Foo
|
|
return try allocator.dupe(u8, trimmed[4..]);
|
|
}
|
|
|
|
// Generic pointer handling for any remaining pointer types
|
|
// Handle "const struct Foo *" -> "*const Foo"
|
|
if (std.mem.startsWith(u8, trimmed, "const struct ")) {
|
|
if (std.mem.endsWith(u8, trimmed, " *")) {
|
|
const struct_name = trimmed[13 .. trimmed.len - 2]; // Remove "const struct " and " *"
|
|
return std.fmt.allocPrint(allocator, "*const {s}", .{struct_name});
|
|
}
|
|
}
|
|
|
|
// Handle "Foo *" for any remaining types (fallback to C pointer)
|
|
if (std.mem.endsWith(u8, trimmed, " *")) {
|
|
const base_type = trimmed[0 .. trimmed.len - 2];
|
|
return std.fmt.allocPrint(allocator, "[*c]{s}", .{base_type});
|
|
}
|
|
if (std.mem.endsWith(u8, trimmed, "*")) {
|
|
const base_type = trimmed[0 .. trimmed.len - 1];
|
|
return std.fmt.allocPrint(allocator, "[*c]{s}", .{base_type});
|
|
}
|
|
|
|
// Fallback: return as-is
|
|
return try allocator.dupe(u8, trimmed);
|
|
}
|
|
|
|
/// Determine the appropriate cast for a given type when calling C functions
|
|
pub fn getCastType(zig_type: []const u8) CastType {
|
|
// Bool needs @bitCast
|
|
if (std.mem.eql(u8, zig_type, "bool")) {
|
|
return .bit_cast;
|
|
}
|
|
|
|
// Opaque pointers need @ptrCast (both ?*Type and *Type)
|
|
// Skip [*c] (C pointers) and *anyopaque
|
|
if (std.mem.startsWith(u8, zig_type, "?*") or std.mem.startsWith(u8, zig_type, "*")) {
|
|
// Exclude anyopaque and C pointers
|
|
if (std.mem.indexOf(u8, zig_type, "anyopaque") != null or
|
|
std.mem.startsWith(u8, zig_type, "[*c]") or
|
|
std.mem.startsWith(u8, zig_type, "?[*c]")) {
|
|
return .none;
|
|
}
|
|
return .ptr_cast;
|
|
}
|
|
|
|
// Enums need @intFromEnum
|
|
// We'll detect these by naming convention or explicit marking
|
|
// For now, assume types ending in certain patterns are enums
|
|
if (std.mem.indexOf(u8, zig_type, "Type") != null or
|
|
std.mem.indexOf(u8, zig_type, "Mode") != null or
|
|
std.mem.indexOf(u8, zig_type, "Op") != null)
|
|
{
|
|
return .int_from_enum;
|
|
}
|
|
|
|
// Flags (packed structs) need @bitCast
|
|
if (std.mem.endsWith(u8, zig_type, "Flags") or
|
|
std.mem.endsWith(u8, zig_type, "Format"))
|
|
{
|
|
return .bit_cast;
|
|
}
|
|
|
|
return .none;
|
|
}
|
|
|
|
/// Convert C function pointer type to Zig function pointer syntax
|
|
/// Example: Sint64 (SDLCALL *size)(void *userdata) -> *const fn (?*anyopaque) callconv(.C) i64
|
|
fn convertFunctionPointerType(c_type: []const u8, allocator: Allocator) ![]const u8 {
|
|
// Pattern: ReturnType (SDLCALL *name)(params) or ReturnType (*name)(params)
|
|
|
|
// Find the return type (everything before the opening paren)
|
|
const open_paren = std.mem.indexOf(u8, c_type, "(") orelse return try allocator.dupe(u8, c_type);
|
|
const return_type_str = std.mem.trim(u8, c_type[0..open_paren], " \t");
|
|
|
|
// Find the parameters (between the last ( and the last ))
|
|
const last_open_paren = std.mem.lastIndexOf(u8, c_type, "(") orelse return try allocator.dupe(u8, c_type);
|
|
const last_close_paren = std.mem.lastIndexOf(u8, c_type, ")") orelse return try allocator.dupe(u8, c_type);
|
|
|
|
if (last_close_paren <= last_open_paren) return try allocator.dupe(u8, c_type);
|
|
|
|
const params_str = std.mem.trim(u8, c_type[last_open_paren + 1 .. last_close_paren], " \t");
|
|
|
|
// Convert return type (but don't recursively convert function pointers)
|
|
const zig_return = if (std.mem.indexOf(u8, return_type_str, "(") != null)
|
|
try allocator.dupe(u8, return_type_str)
|
|
else
|
|
try convertType(return_type_str, allocator);
|
|
defer allocator.free(zig_return);
|
|
|
|
// Convert parameters
|
|
var params_list = std.ArrayList([]const u8).init(allocator);
|
|
defer {
|
|
for (params_list.items) |param| {
|
|
allocator.free(param);
|
|
}
|
|
params_list.deinit();
|
|
}
|
|
|
|
// Parse comma-separated parameters
|
|
var param_iter = std.mem.splitScalar(u8, params_str, ',');
|
|
while (param_iter.next()) |param| {
|
|
const trimmed_param = std.mem.trim(u8, param, " \t");
|
|
if (trimmed_param.len == 0) continue;
|
|
|
|
// Extract just the type (remove parameter name if present)
|
|
// Pattern: "void *userdata" -> "void *"
|
|
// Pattern: "Sint64 offset" -> "Sint64"
|
|
var param_type: []const u8 = trimmed_param;
|
|
|
|
// Find the last space that separates type from name
|
|
if (std.mem.lastIndexOf(u8, trimmed_param, " ")) |space_pos| {
|
|
// Check if what comes after is an identifier (not a *)
|
|
const after_space = trimmed_param[space_pos + 1 ..];
|
|
if (after_space.len > 0 and after_space[0] != '*') {
|
|
param_type = std.mem.trimRight(u8, trimmed_param[0..space_pos], " \t");
|
|
}
|
|
}
|
|
|
|
// Don't recursively convert function pointers in params
|
|
const zig_param = if (std.mem.indexOf(u8, param_type, "(") != null)
|
|
try allocator.dupe(u8, param_type)
|
|
else
|
|
try convertType(param_type, allocator);
|
|
try params_list.append(zig_param);
|
|
}
|
|
|
|
// Build Zig function pointer type
|
|
var result = std.ArrayList(u8).init(allocator);
|
|
defer result.deinit();
|
|
|
|
try result.appendSlice("?*const fn (");
|
|
|
|
for (params_list.items, 0..) |param, i| {
|
|
if (i > 0) try result.appendSlice(", ");
|
|
try result.appendSlice(param);
|
|
}
|
|
|
|
try result.appendSlice(") callconv(.C) ");
|
|
try result.appendSlice(zig_return);
|
|
|
|
return try result.toOwnedSlice();
|
|
}
|
|
|
|
pub const CastType = enum {
|
|
none,
|
|
ptr_cast,
|
|
bit_cast,
|
|
int_from_enum,
|
|
enum_from_int,
|
|
};
|