# SDL3 Header Parser - Agent Documentation ## Project Overview This is an SDL3 header parser that converts C header files into Zig interfaces, JSON objects, and C mocks. Built specifically for SDL3 headers (developed against SDL 3.2.10), it focuses on generating Zig APIs. **Key Points:** - Not a real C parser - best-effort text transformation specifically for SDL3 headers - Does not do proper AST elaboration or tokenization - Simple to modify but brittle - Requires Zig 0.15.2 and git ## Architecture The codebase consists of several modules: - `parser.zig` - Main entry point and orchestration - `patterns.zig` - SDL3 pattern matching and declaration scanning - `codegen.zig` - Zig code generation - `mock_codegen.zig` - C mock generation - `json_serializer.zig` - JSON output generation - `dependency_resolver.zig` - Type dependency resolution - `header_cache.zig` - Header caching for cross-file type resolution - `io.zig` - Error handling utilities - `types.zig` - Type mapping and conversions - `naming.zig` - Naming conventions ## Usage ### Building and Running **Generate all headers:** ```bash zig build generate ``` This processes all SDL3 headers and generates corresponding Zig files in the `api/` directory. **Generate a single header:** ```bash zig build run -- --output= ``` Example: ```bash zig build run -- sdl3\include\SDL3\SDL_gpu.h --output=api\gpu.zig ``` The parser converts C headers to Zig files: - `SDL_gpu.h` → `gpu.zig` - `SDL_video.h` → `video.zig` - `SDL_events.h` → `events.zig` **Additional options:** - `--mocks=` - Generate C mock implementations - `--generate-json=` - Generate JSON representation - `--timestamp=` - Set custom timestamp - `--basedir=` - Set base directory for dependency resolution ### Output Directories - `api/` - Generated Zig API files (final output) - `tmp/` - Temporary files during generation (before validation) - `debug/` - Debug output when generation fails (with `_fmterror.zig` suffix) - `archive/` - Archived previous debug outputs ## Developing ### Build System Patterns **Custom Build Steps:** The project uses custom Zig build steps for operations that need to happen during the build process: ```zig const MakeDirStep = struct { step: std.Build.Step, dir_path: []const u8, pub fn create(b: *std.Build, dir_path: []const u8) *std.Build.Step { const self = b.allocator.create(MakeDirStep) catch @panic("OOM"); self.* = .{ .step = std.Build.Step.init(.{ .id = .custom, .name = b.fmt("mkdir {s}", .{dir_path}), .owner = b, .makeFn = make, }), .dir_path = dir_path, }; return &self.step; } fn make(step: *std.Build.Step, options: std.Build.Step.MakeOptions) !void { _ = options; const self: *MakeDirStep = @fieldParentPtr("step", step); try std.fs.cwd().makePath(self.dir_path); } }; ``` This pattern allows cross-platform directory creation and other filesystem operations during build without relying on shell commands. ### Error Handling **CRITICAL:** Whenever actual errors are encountered (parsing failures, invalid declarations, missing types, etc.), you MUST call `io.emitError` to report them properly. Do NOT use `std.debug.print` for actual errors. ```zig // Good - proper error reporting try io.emitError(allocator, "Failed to parse struct field: {s}", .{field_name}); // Bad - don't use for errors std.debug.print("Error: Failed to parse...\n", .{}); ``` ### Type Casting in Generated Code The code generator automatically adds appropriate casts when calling C functions: **Parameters:** - Opaque pointers (`*Window`, `?*GPUDevice`) → `@ptrCast` - Enums → `@intFromEnum` - Flags (packed structs) → `@bitCast` - Booleans → `@bitCast` **Return values:** - Opaque pointers (`?*Window`) → `@ptrCast` - Booleans → `@bitCast` - Flags (packed structs) → `@bitCast` Example: ```zig pub inline fn createWindow(title: [*c]const u8, w: c_int, h: c_int, flags: WindowFlags) ?*Window { return @ptrCast(c.SDL_CreateWindow(title, w, h, @bitCast(flags))); // ^^^^^^^^ return cast ^^^^^ param cast } ``` ### Validation Process After generating Zig code: 1. Output written to `tmp/` directory 2. `zig ast-check` runs on the temporary file 3. If passes: `zig fmt` runs, then copied to final destination 4. If fails: debug output written to `debug/` with `_fmterror.zig` suffix **Testing all outputs:** Use `zig build generate` to regenerate all SDL3 headers and verify no regressions. ### Memory Management - Two cleanup functions exist with identical logic: - `freeDecls()` - frees a slice of declarations - `freeDeclDeep()` - frees a single declaration - Both free all nested allocations (names, types, doc comments, params/fields/values/flags) ### Zig 0.15 Specifics **IMPORTANT:** In Zig 0.15, `std.ArrayList` is an alias to `std.ArrayListUnmanaged`. This means: - ArrayList methods require an explicit allocator parameter - Use `list.append(allocator, item)` instead of `list.append(item)` - Use `list.toOwnedSlice(allocator)` instead of `list.toOwnedSlice()` - Use `list.deinit(allocator)` instead of `list.deinit()` - Initialize with `std.ArrayList(T){}` (empty init) Example: ```zig var list = std.ArrayList(MyType){}; errdefer list.deinit(allocator); try list.append(allocator, item); const slice = try list.toOwnedSlice(allocator); ``` ### Main APIs (Priority) Focus areas: gpu, video, gamepad, joystick, input, event Anything beyond these is not actively maintained. ## Code Generation Features ### Enum-to-Flags Conversion The parser automatically detects enums that contain bitwise OR operations and converts them to packed structs with bool flags. **Example:** SDL_FlipMode in SDL_surface.h ```c // C enum with bitwise OR typedef enum SDL_FlipMode { SDL_FLIP_NONE, // 0 SDL_FLIP_HORIZONTAL, // 1 SDL_FLIP_VERTICAL, // 2 SDL_FLIP_HORIZONTAL_AND_VERTICAL = (SDL_FLIP_HORIZONTAL | SDL_FLIP_VERTICAL) // 3 } SDL_FlipMode; ``` Converts to: ```zig // Zig packed struct with bool flags pub const FlipMode = packed struct(u32) { flipHorizontal: bool = false, flipVertical: bool = false, pad0: u29 = 0, rsvd: bool = false, pub const None = FlipMode{}; }; ``` **Usage:** - `FlipMode.None` - no flip (matches C's SDL_FLIP_NONE) - `FlipMode{ .flipHorizontal = true }` - horizontal flip - `FlipMode{ .flipVertical = true }` - vertical flip - `FlipMode{ .flipHorizontal = true, .flipVertical = true }` - both (equivalent to SDL_FLIP_HORIZONTAL_AND_VERTICAL) This approach eliminates the need for explicit combined values and provides a type-safe way to combine flags.