diff --git a/lib/sdl3/parser/src/codegen.zig b/lib/sdl3/parser/src/codegen.zig index 5a43d1c..ca758d8 100644 --- a/lib/sdl3/parser/src/codegen.zig +++ b/lib/sdl3/parser/src/codegen.zig @@ -95,6 +95,7 @@ pub const CodeGen = struct { switch (decl) { .opaque_type => |opaque_decl| try self.writeOpaqueWithMethods(opaque_decl), .typedef_decl => |typedef_decl| try self.writeTypedef(typedef_decl), + .function_pointer_decl => |func_ptr_decl| try self.writeFunctionPointer(func_ptr_decl), .enum_decl => |enum_decl| try self.writeEnum(enum_decl), .struct_decl => |struct_decl| try self.writeStruct(struct_decl), .flag_decl => |flag_decl| try self.writeFlags(flag_decl), @@ -176,6 +177,33 @@ pub const CodeGen = struct { try self.output.appendSlice(self.allocator, ";\n\n"); } + fn writeFunctionPointer(self: *CodeGen, func_ptr_decl: patterns.FunctionPointerDecl) !void { + // Write doc comment if present + if (func_ptr_decl.doc_comment) |doc| { + try self.writeDocComment(doc); + } + + const zig_name = naming.typeNameToZig(func_ptr_decl.name); + const return_type = try types.convertType(func_ptr_decl.return_type, self.allocator); + defer self.allocator.free(return_type); + + // Generate: pub const TimerCallback = *const fn(param1: Type1, ...) callconv(.C) RetType; + try self.output.writer(self.allocator).print("pub const {s} = *const fn(", .{zig_name}); + + // Write parameters + for (func_ptr_decl.params, 0..) |param, i| { + if (i > 0) try self.output.appendSlice(self.allocator, ", "); + + const param_type = try types.convertType(param.type_name, self.allocator); + defer self.allocator.free(param_type); + + try self.output.writer(self.allocator).print("{s}: {s}", .{param.name, param_type}); + } + + // Close with calling convention and return type + try self.output.writer(self.allocator).print(") callconv(.C) {s};\n\n", .{return_type}); + } + fn writeEnum(self: *CodeGen, enum_decl: EnumDecl) !void { const zig_name = naming.typeNameToZig(enum_decl.name); diff --git a/lib/sdl3/parser/src/dependency_resolver.zig b/lib/sdl3/parser/src/dependency_resolver.zig index 3e2b860..b023509 100644 --- a/lib/sdl3/parser/src/dependency_resolver.zig +++ b/lib/sdl3/parser/src/dependency_resolver.zig @@ -54,6 +54,7 @@ pub const DependencyResolver = struct { const type_name = switch (decl) { .opaque_type => |o| o.name, .typedef_decl => |t| t.name, + .function_pointer_decl => |fp| fp.name, .enum_decl => |e| e.name, .struct_decl => |s| s.name, .flag_decl => |f| f.name, @@ -72,6 +73,12 @@ pub const DependencyResolver = struct { try self.scanType(param.type_name); } }, + .function_pointer_decl => |func_ptr| { + try self.scanType(func_ptr.return_type); + for (func_ptr.params) |param| { + try self.scanType(param.type_name); + } + }, .struct_decl => |struct_decl| { for (struct_decl.fields) |field| { try self.scanType(field.type_name); @@ -271,6 +278,14 @@ fn cloneDeclaration(allocator: Allocator, decl: Declaration) !Declaration { .doc_comment = if (t.doc_comment) |doc| try allocator.dupe(u8, doc) else null, }, }, + .function_pointer_decl => |fp| .{ + .function_pointer_decl = .{ + .name = try allocator.dupe(u8, fp.name), + .return_type = try allocator.dupe(u8, fp.return_type), + .doc_comment = if (fp.doc_comment) |doc| try allocator.dupe(u8, doc) else null, + .params = try cloneParams(allocator, fp.params), + }, + }, .enum_decl => |e| .{ .enum_decl = .{ .name = try allocator.dupe(u8, e.name), @@ -362,6 +377,16 @@ fn freeDeclaration(allocator: Allocator, decl: Declaration) void { allocator.free(t.underlying_type); if (t.doc_comment) |doc| allocator.free(doc); }, + .function_pointer_decl => |fp| { + allocator.free(fp.name); + allocator.free(fp.return_type); + if (fp.doc_comment) |doc| allocator.free(doc); + for (fp.params) |param| { + allocator.free(param.name); + allocator.free(param.type_name); + } + allocator.free(fp.params); + }, .enum_decl => |e| { allocator.free(e.name); if (e.doc_comment) |doc| allocator.free(doc); diff --git a/lib/sdl3/parser/src/parser.zig b/lib/sdl3/parser/src/parser.zig index e931e6a..f0afb48 100644 --- a/lib/sdl3/parser/src/parser.zig +++ b/lib/sdl3/parser/src/parser.zig @@ -67,6 +67,16 @@ pub fn main() !void { allocator.free(typedef_decl.underlying_type); if (typedef_decl.doc_comment) |doc| allocator.free(doc); }, + .function_pointer_decl => |func_ptr_decl| { + allocator.free(func_ptr_decl.name); + allocator.free(func_ptr_decl.return_type); + if (func_ptr_decl.doc_comment) |doc| allocator.free(doc); + for (func_ptr_decl.params) |param| { + allocator.free(param.name); + allocator.free(param.type_name); + } + allocator.free(func_ptr_decl.params); + }, .enum_decl => |enum_decl| { allocator.free(enum_decl.name); if (enum_decl.doc_comment) |doc| allocator.free(doc); @@ -118,6 +128,7 @@ pub fn main() !void { // Count each type var opaque_count: usize = 0; var typedef_count: usize = 0; + var func_ptr_count: usize = 0; var enum_count: usize = 0; var struct_count: usize = 0; var flag_count: usize = 0; @@ -127,6 +138,7 @@ pub fn main() !void { switch (decl) { .opaque_type => opaque_count += 1, .typedef_decl => typedef_count += 1, + .function_pointer_decl => func_ptr_count += 1, .enum_decl => enum_count += 1, .struct_decl => struct_count += 1, .flag_decl => flag_count += 1, @@ -136,6 +148,7 @@ pub fn main() !void { std.debug.print(" - Opaque types: {d}\n", .{opaque_count}); std.debug.print(" - Typedefs: {d}\n", .{typedef_count}); + std.debug.print(" - Function pointers: {d}\n", .{func_ptr_count}); std.debug.print(" - Enums: {d}\n", .{enum_count}); std.debug.print(" - Structs: {d}\n", .{struct_count}); std.debug.print(" - Flags: {d}\n", .{flag_count}); @@ -349,6 +362,16 @@ fn freeDeclDeep(allocator: std.mem.Allocator, decl: patterns.Declaration) void { allocator.free(t.underlying_type); if (t.doc_comment) |doc| allocator.free(doc); }, + .function_pointer_decl => |fp| { + allocator.free(fp.name); + allocator.free(fp.return_type); + if (fp.doc_comment) |doc| allocator.free(doc); + for (fp.params) |param| { + allocator.free(param.name); + allocator.free(param.type_name); + } + allocator.free(fp.params); + }, .enum_decl => |e| { allocator.free(e.name); if (e.doc_comment) |doc| allocator.free(doc); diff --git a/lib/sdl3/parser/src/patterns.zig b/lib/sdl3/parser/src/patterns.zig index 7c929a1..f03b792 100644 --- a/lib/sdl3/parser/src/patterns.zig +++ b/lib/sdl3/parser/src/patterns.zig @@ -9,6 +9,7 @@ pub const Declaration = union(enum) { flag_decl: FlagDecl, function_decl: FunctionDecl, typedef_decl: TypedefDecl, + function_pointer_decl: FunctionPointerDecl, }; pub const OpaqueType = struct { @@ -59,6 +60,13 @@ pub const TypedefDecl = struct { doc_comment: ?[]const u8, }; +pub const FunctionPointerDecl = struct { + name: []const u8, // SDL_TimerCallback + return_type: []const u8, // Uint32 + params: []ParamDecl, + doc_comment: ?[]const u8, +}; + pub const FunctionDecl = struct { name: []const u8, // SDL_CreateGPUDevice return_type: []const u8, // SDL_GPUDevice * @@ -106,6 +114,9 @@ pub const Scanner = struct { } else if (try self.scanFlagTypedef()) |flag_decl| { // Flag typedef must come before simple typedef try decls.append(self.allocator, .{ .flag_decl = flag_decl }); + } else if (try self.scanFunctionPointer()) |func_ptr_decl| { + // Function pointer typedef must come before simple typedef + try decls.append(self.allocator, .{ .function_pointer_decl = func_ptr_decl }); } else if (try self.scanTypedef()) |typedef_decl| { // Simple typedef comes after flag typedef try decls.append(self.allocator, .{ .typedef_decl = typedef_decl }); @@ -174,6 +185,86 @@ pub const Scanner = struct { }; } + // Pattern: typedef RetType (SDLCALL *FuncName)(Param1Type param1, ...); + fn scanFunctionPointer(self: *Scanner) !?FunctionPointerDecl { + const start = self.pos; + + const line = try self.readLine(); + defer self.allocator.free(line); + + // Must start with typedef + if (!std.mem.startsWith(u8, line, "typedef ")) { + self.pos = start; + return null; + } + + // Must contain * pattern with SDL prefix (function pointer typedef) + // Pattern: typedef RetType (SDLCALL *SDL_Name)(Params); + const has_sdl_ptr = std.mem.indexOf(u8, line, " *SDL_") != null or + std.mem.indexOf(u8, line, "(*SDL_") != null; + if (!has_sdl_ptr) { + self.pos = start; + return null; + } + + // Parse: typedef RetType (SDLCALL *FuncName)(Params); + const trimmed = std.mem.trim(u8, line, " \t\r\n"); + const no_semi = std.mem.trimRight(u8, trimmed, ";"); + + // Skip "typedef " + const after_typedef = std.mem.trimLeft(u8, no_semi["typedef ".len..], " \t"); + + // Find the *SDL_ marker (function pointer name) + const ptr_marker = std.mem.indexOf(u8, after_typedef, " *SDL_") orelse + std.mem.indexOf(u8, after_typedef, "(*SDL_") orelse { + self.pos = start; + return null; + }; + + // Return type is everything before the pointer marker + // It may include (SDLCALL or just be the plain type + const return_type_section = std.mem.trim(u8, after_typedef[0..ptr_marker], " \t"); + + // Extract return type (remove SDLCALL if present) + const return_type = if (std.mem.indexOf(u8, return_type_section, "(SDLCALL")) |sdlcall_pos| + std.mem.trim(u8, return_type_section[0..sdlcall_pos], " \t") + else if (std.mem.indexOf(u8, return_type_section, "SDLCALL")) |sdlcall_pos| + std.mem.trim(u8, return_type_section[0..sdlcall_pos], " \t") + else + return_type_section; + + // Find function name: starts after *SDL_ and ends at ) + const after_star = std.mem.trimLeft(u8, after_typedef[ptr_marker..], " *("); + const name_end = std.mem.indexOfScalar(u8, after_star, ')') orelse { + self.pos = start; + return null; + }; + const func_name = std.mem.trim(u8, after_star[0..name_end], " \t"); + + // Find parameters (between the closing ) of name and final ) + const after_name = after_star[name_end + 1..]; // Skip ) + const params_start = std.mem.indexOfScalar(u8, after_name, '(') orelse { + self.pos = start; + return null; + }; + const params_end = std.mem.lastIndexOfScalar(u8, after_name, ')') orelse { + self.pos = start; + return null; + }; + const params_str = std.mem.trim(u8, after_name[params_start + 1..params_end], " \t"); + + // Parse parameters + const params = try self.parseParams(params_str); + const doc = self.consumePendingDocComment(); + + return FunctionPointerDecl{ + .name = try self.allocator.dupe(u8, func_name), + .return_type = try self.allocator.dupe(u8, return_type), + .params = params, + .doc_comment = doc, + }; + } + // Pattern: typedef Type SDL_Name; fn scanTypedef(self: *Scanner) !?TypedefDecl { const start = self.pos;