# 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 ## 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", .{}); ``` ### 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 ### 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) ### Main APIs (Priority) Focus areas: gpu, video, gamepad, joystick, input, event Anything beyond these is not actively maintained.