docs: Reorganize and clean up documentation
Complete documentation overhaul with clear organization and clean structure. ## Changes ### Documentation Reorganization **New Structure**: - README.md - Project overview and entry point - PROJECT_STRUCTURE.md - Complete directory layout - docs/ - All documentation (organized by category) - docs/archive/ - Historical planning documents - test/integration/ - Integration tests **Removed Duplicates**: - Consolidated multiple status documents - Archived planning documents - Removed redundant guides - Cleaned up old test files ### New User Documentation Created clean, focused guides: 1. **README.md** - Project overview, quick start, feature list 2. **docs/GETTING_STARTED.md** - Step-by-step tutorial 3. **docs/API_REFERENCE.md** - Complete CLI reference 4. **docs/QUICKSTART.md** - Quick reference guide ### New Technical Documentation 5. **docs/ARCHITECTURE.md** - System design and components 6. **docs/DEPENDENCY_RESOLUTION.md** - How automatic deps work 7. **docs/KNOWN_ISSUES.md** - Current limitations and workarounds ### New Development Documentation 8. **docs/DEVELOPMENT.md** - Contributing, extending, Zig 0.15 guide 9. **docs/ROADMAP.md** - Future plans and priorities 10. **docs/INDEX.md** - Complete documentation index ### Organized Technical Details Kept detailed implementation docs in docs/: - DEPENDENCY_FLOW.md (845 lines) - Technical walkthrough - VISUAL_FLOW.md (365 lines) - Flow diagrams - MULTI_FIELD_IMPLEMENTATION.md - Feature implementation - TYPEDEF_IMPLEMENTATION.md - Feature implementation - MULTI_HEADER_TEST_RESULTS.md - Test results ### Archived Historical Documents Moved to docs/archive/: - Planning documents - Session summaries - Status reports - Implementation notes These remain available for reference but don't clutter main docs. ## Documentation Statistics **Before**: - 18 markdown files in root - Mix of planning, status, and user docs - No clear entry point - Difficult to navigate **After**: - 2 files in root (README, PROJECT_STRUCTURE) - 14 organized docs in docs/ - 9 archived docs in docs/archive/ - Clear hierarchy and index - Easy navigation **Lines of Documentation**: - User guides: ~1,500 lines - Technical docs: ~2,500 lines - Implementation details: ~1,500 lines - **Total: ~5,500 lines** (well-organized) ## Documentation Organization ### By Audience **New Users**: 1. README.md 2. docs/GETTING_STARTED.md 3. docs/QUICKSTART.md **Existing Users**: 1. docs/API_REFERENCE.md 2. docs/KNOWN_ISSUES.md **Developers**: 1. docs/ARCHITECTURE.md 2. docs/DEVELOPMENT.md 3. docs/DEPENDENCY_FLOW.md ### By Purpose **Learning**: Getting Started, Quickstart, Architecture **Reference**: API Reference, INDEX, Known Issues **Development**: DEVELOPMENT, Roadmap, Implementation docs **History**: archive/ directory ## Benefits ✅ Clear navigation path for all users ✅ Focused documentation (no duplication) ✅ Preserved historical context (archive) ✅ Professional structure ✅ Easy to maintain ✅ Organized test files ## Testing - All existing tests still in place (test/ and test/integration/) - Build system unchanged - No functional changes to parser - Pure documentation cleanup --- Impact: Documentation only (no code changes) Files changed: 50+ (reorganization) Lines: ~5,500 (well-organized) Status: Production-ready documentation ✅
This commit is contained in:
parent
0734de2332
commit
c23ae441c1
|
|
@ -1,385 +0,0 @@
|
||||||
# Agent Solutions Guide: Zig 0.15 Issues
|
|
||||||
|
|
||||||
This document catalogs common issues encountered when working with Zig 0.15 and their solutions. Written for AI coding assistants to avoid repeating mistakes.
|
|
||||||
|
|
||||||
## Critical: ArrayList API Changed in Zig 0.15
|
|
||||||
|
|
||||||
### Problem
|
|
||||||
`std.ArrayList` is now an alias to `std.ArrayListUnmanaged` in Zig 0.15. The managed version has been removed.
|
|
||||||
|
|
||||||
### Old (Pre-0.15) Code - DOES NOT WORK
|
|
||||||
```zig
|
|
||||||
var list = std.ArrayList(u8).init(allocator);
|
|
||||||
defer list.deinit();
|
|
||||||
try list.append(item);
|
|
||||||
```
|
|
||||||
|
|
||||||
### New (0.15+) Code - CORRECT
|
|
||||||
```zig
|
|
||||||
// Empty initialization
|
|
||||||
var list = std.ArrayList(u8){};
|
|
||||||
defer list.deinit(allocator);
|
|
||||||
try list.append(allocator, item);
|
|
||||||
|
|
||||||
// Or with capacity
|
|
||||||
var list = try std.ArrayList(u8).initCapacity(allocator, 100);
|
|
||||||
defer list.deinit(allocator);
|
|
||||||
try list.append(allocator, item);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Key Changes
|
|
||||||
1. **Initialization**: Use `{}` or `initCapacity()`, not `init()`
|
|
||||||
2. **All methods take allocator**: `append(allocator, item)` not `append(item)`
|
|
||||||
3. **Deinit takes allocator**: `deinit(allocator)` not `deinit()`
|
|
||||||
|
|
||||||
## AST Rendering API Changed
|
|
||||||
|
|
||||||
### Problem
|
|
||||||
The `ast.render()` function signature changed in Zig 0.15.
|
|
||||||
|
|
||||||
### Old Code - DOES NOT WORK
|
|
||||||
```zig
|
|
||||||
var ast = try std.zig.Ast.parse(allocator, source, .zig);
|
|
||||||
const output = try ast.render(allocator);
|
|
||||||
```
|
|
||||||
|
|
||||||
### New Code - CORRECT
|
|
||||||
```zig
|
|
||||||
var ast = try std.zig.Ast.parse(allocator, source, .zig);
|
|
||||||
const output = try ast.renderAlloc(allocator);
|
|
||||||
defer allocator.free(output);
|
|
||||||
```
|
|
||||||
|
|
||||||
### The API
|
|
||||||
- `renderAlloc(allocator)` - Returns allocated string
|
|
||||||
- `render(tree, gpa, writer, fixups)` - Low-level version for custom output
|
|
||||||
|
|
||||||
## Type Conversion: SDL Types to Zig
|
|
||||||
|
|
||||||
### Pointer Types
|
|
||||||
|
|
||||||
| C Type | Zig Type | Notes |
|
|
||||||
|--------|----------|-------|
|
|
||||||
| `const char *` | `[*c]const u8` | C string |
|
|
||||||
| `void *` | `?*anyopaque` | Nullable any pointer |
|
|
||||||
| `const void *` | `?*const anyopaque` | Const version |
|
|
||||||
| `SDL_Type *` | `?*Type` | Nullable pointer to opaque/struct |
|
|
||||||
| `const SDL_Type *` | `*const Type` | Non-null const pointer |
|
|
||||||
| `SDL_Type **` | `?*?*Type` | Output parameter (double pointer) |
|
|
||||||
| `SDL_Type *const *` | `[*c]*const Type` | Array of const pointers |
|
|
||||||
| `Uint32 *` | `*u32` | Output parameter (primitive) |
|
|
||||||
|
|
||||||
### Key Principles
|
|
||||||
1. **Non-nullable by default** for const pointers to structs
|
|
||||||
2. **Nullable (`?*`)** for pointers that can be NULL
|
|
||||||
3. **Use `*` not `[*c]`** when you know it's not a C-style array
|
|
||||||
4. **Double pointers**: `?*?*Type` for output parameters
|
|
||||||
|
|
||||||
## Function Signature Formatting
|
|
||||||
|
|
||||||
### Trailing Commas
|
|
||||||
Only use trailing commas for functions with **more than 3 parameters**. This triggers multi-line formatting.
|
|
||||||
|
|
||||||
```zig
|
|
||||||
// 1-3 parameters: single line, no trailing comma
|
|
||||||
pub fn foo(a: i32, b: i32, c: i32) void {}
|
|
||||||
|
|
||||||
// 4+ parameters: multi-line with trailing comma
|
|
||||||
pub fn bar(
|
|
||||||
a: i32,
|
|
||||||
b: i32,
|
|
||||||
c: i32,
|
|
||||||
d: i32,
|
|
||||||
) void {}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Why?
|
|
||||||
- Trailing comma with no parameters: `(,)` is **syntax error**
|
|
||||||
- Trailing comma with 1-3 params: unnecessary, wastes vertical space
|
|
||||||
- Trailing comma with 4+ params: makes diffs cleaner, easier to read
|
|
||||||
|
|
||||||
## Method Organization
|
|
||||||
|
|
||||||
### Place Methods Inside Opaque Types
|
|
||||||
Functions where the first parameter is a pointer to an opaque type should be methods:
|
|
||||||
|
|
||||||
```zig
|
|
||||||
// Good - method syntax
|
|
||||||
pub const GPUDevice = opaque {
|
|
||||||
pub fn destroy(device: *GPUDevice) void {
|
|
||||||
c.SDL_DestroyGPUDevice(device);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Usage: device.destroy()
|
|
||||||
|
|
||||||
// Bad - standalone function
|
|
||||||
pub fn destroyGPUDevice(device: ?*GPUDevice) void {
|
|
||||||
c.SDL_DestroyGPUDevice(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Usage: destroyGPUDevice(device)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Benefits
|
|
||||||
1. Cleaner API: `device.create()` vs `createGPUDevice(device)`
|
|
||||||
2. IDE autocomplete works better
|
|
||||||
3. Namespacing prevents naming conflicts
|
|
||||||
4. More idiomatic Zig
|
|
||||||
|
|
||||||
## Casting Guidelines
|
|
||||||
|
|
||||||
### When to Cast
|
|
||||||
|
|
||||||
| Scenario | Cast | Example |
|
|
||||||
|----------|------|---------|
|
|
||||||
| Opaque pointer | `@ptrCast` | `@ptrCast(device)` |
|
|
||||||
| Flags (packed struct) | `@bitCast` | `@bitCast(flags)` |
|
|
||||||
| Enum to int | `@intFromEnum` | `@intFromEnum(enum_val)` |
|
|
||||||
| Struct passed by value | None | Just pass it |
|
|
||||||
| Const pointer to struct | `@ptrCast` | `@ptrCast(info)` |
|
|
||||||
|
|
||||||
### Don't Over-Cast
|
|
||||||
```zig
|
|
||||||
// Bad - unnecessary cast for value type
|
|
||||||
fn setColor(color: FColor) void {
|
|
||||||
c.SDL_SetColor(@bitCast(color)); // Wrong!
|
|
||||||
}
|
|
||||||
|
|
||||||
// Good - no cast needed
|
|
||||||
fn setColor(color: FColor) void {
|
|
||||||
c.SDL_SetColor(color); // Correct
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## StringHashMap Usage
|
|
||||||
|
|
||||||
### Correct Pattern
|
|
||||||
```zig
|
|
||||||
var map = std.StringHashMap(ValueType).init(allocator);
|
|
||||||
defer map.deinit(); // No allocator needed for deinit
|
|
||||||
|
|
||||||
try map.put("key", value);
|
|
||||||
const val = map.get("key");
|
|
||||||
```
|
|
||||||
|
|
||||||
### Iteration
|
|
||||||
```zig
|
|
||||||
var it = map.keyIterator();
|
|
||||||
while (it.next()) |key| {
|
|
||||||
// Use key.*
|
|
||||||
}
|
|
||||||
|
|
||||||
var it = map.valueIterator();
|
|
||||||
while (it.next()) |value| {
|
|
||||||
// Use value.* if needed
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Common Pitfalls
|
|
||||||
|
|
||||||
### 1. Forgetting Allocator in Unmanaged Collections
|
|
||||||
```zig
|
|
||||||
// Wrong
|
|
||||||
list.append(item);
|
|
||||||
|
|
||||||
// Right
|
|
||||||
list.append(allocator, item);
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Using .init() on ArrayList
|
|
||||||
```zig
|
|
||||||
// Wrong
|
|
||||||
var list = std.ArrayList(u8).init(allocator);
|
|
||||||
|
|
||||||
// Right
|
|
||||||
var list = std.ArrayList(u8){};
|
|
||||||
// or
|
|
||||||
var list = try std.ArrayList(u8).initCapacity(allocator, size);
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Not Checking AST Errors Before Rendering
|
|
||||||
```zig
|
|
||||||
// Wrong - will panic if there are errors
|
|
||||||
const output = try ast.renderAlloc(allocator);
|
|
||||||
|
|
||||||
// Right - check first
|
|
||||||
if (ast.errors.len > 0) {
|
|
||||||
// Handle errors
|
|
||||||
return error.ParseError;
|
|
||||||
}
|
|
||||||
const output = try ast.renderAlloc(allocator);
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Incorrect Double Pointer Types
|
|
||||||
```zig
|
|
||||||
// Wrong - C-style for output params
|
|
||||||
texture: [*c]*GPUTexture
|
|
||||||
|
|
||||||
// Right - Zig optional pointers
|
|
||||||
texture: ?*?*GPUTexture
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing Patterns
|
|
||||||
|
|
||||||
### Simple Test
|
|
||||||
```zig
|
|
||||||
test "description" {
|
|
||||||
const result = try someFunction();
|
|
||||||
try std.testing.expectEqual(expected, result);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Test with Allocator
|
|
||||||
```zig
|
|
||||||
test "with allocator" {
|
|
||||||
const allocator = std.testing.allocator;
|
|
||||||
const result = try allocateAndDoSomething(allocator);
|
|
||||||
defer allocator.free(result);
|
|
||||||
|
|
||||||
try std.testing.expectEqualStrings("expected", result);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Build System Integration
|
|
||||||
|
|
||||||
### Adding Parser to Dependencies
|
|
||||||
```zig
|
|
||||||
// build.zig.zon
|
|
||||||
.dependencies = .{
|
|
||||||
.sdl3_parser = .{ .path = "parser/" },
|
|
||||||
},
|
|
||||||
|
|
||||||
// build.zig
|
|
||||||
const parser_dep = b.dependency("sdl3_parser", .{
|
|
||||||
.target = target,
|
|
||||||
.optimize = optimize,
|
|
||||||
});
|
|
||||||
const parser_exe = parser_dep.artifact("sdl-parser");
|
|
||||||
```
|
|
||||||
|
|
||||||
### Run Step
|
|
||||||
```zig
|
|
||||||
const run_parser = b.addRunArtifact(parser_exe);
|
|
||||||
run_parser.addFileArg(b.path("input.h"));
|
|
||||||
run_parser.addArg("--output=output.zig");
|
|
||||||
|
|
||||||
const step = b.step("generate", "Generate bindings");
|
|
||||||
step.dependOn(&run_parser.step);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quick Reference Card
|
|
||||||
|
|
||||||
```zig
|
|
||||||
// Collections
|
|
||||||
var list = std.ArrayList(T){};
|
|
||||||
defer list.deinit(allocator);
|
|
||||||
try list.append(allocator, item);
|
|
||||||
|
|
||||||
var map = std.StringHashMap(V).init(allocator);
|
|
||||||
defer map.deinit();
|
|
||||||
try map.put("key", value);
|
|
||||||
|
|
||||||
// AST
|
|
||||||
var ast = try std.zig.Ast.parse(allocator, source, .zig);
|
|
||||||
defer ast.deinit(allocator);
|
|
||||||
const formatted = try ast.renderAlloc(allocator);
|
|
||||||
defer allocator.free(formatted);
|
|
||||||
|
|
||||||
// Type Patterns
|
|
||||||
?*Type // Nullable pointer
|
|
||||||
*const Type // Non-null const pointer
|
|
||||||
?*?*Type // Output parameter
|
|
||||||
[*c]*const Type // C array of const pointers
|
|
||||||
|
|
||||||
// Casts
|
|
||||||
@ptrCast(ptr) // Pointers
|
|
||||||
@bitCast(value) // Packed structs, flags
|
|
||||||
@intFromEnum(e) // Enum to int
|
|
||||||
// No cast for value types!
|
|
||||||
```
|
|
||||||
|
|
||||||
## Version Info
|
|
||||||
|
|
||||||
- **Zig Version**: 0.15.2
|
|
||||||
- **Date**: 2025-01-22
|
|
||||||
- **SDL Version**: 3.2.0
|
|
||||||
|
|
||||||
## Issues Encountered During Mock Testing Implementation
|
|
||||||
|
|
||||||
### Issue 1: Build.addStaticLibrary Removed
|
|
||||||
|
|
||||||
**Problem**: Zig 0.15 removed `b.addStaticLibrary()` method.
|
|
||||||
|
|
||||||
**Error**:
|
|
||||||
```
|
|
||||||
error: no field or member function named 'addStaticLibrary' in 'Build'
|
|
||||||
```
|
|
||||||
|
|
||||||
**Solution**: Use `b.addLibrary()` with `.linkage = .static`:
|
|
||||||
```zig
|
|
||||||
// OLD - Does not work
|
|
||||||
const lib = b.addStaticLibrary(.{
|
|
||||||
.name = "mylib",
|
|
||||||
.target = target,
|
|
||||||
.optimize = optimize,
|
|
||||||
});
|
|
||||||
|
|
||||||
// NEW - Correct for Zig 0.15
|
|
||||||
const lib = b.addLibrary(.{
|
|
||||||
.name = "mylib",
|
|
||||||
.linkage = .static,
|
|
||||||
.root_module = b.createModule(.{
|
|
||||||
.target = target,
|
|
||||||
.optimize = optimize,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
**Key change**: Must create `root_module` explicitly with target/optimize.
|
|
||||||
|
|
||||||
### Issue 2: C Mock Type Definitions
|
|
||||||
|
|
||||||
**Problem**: Generated C mocks referenced SDL types like `Uint32`, `SDL_Window`, `FColor` that weren't defined when using only stdint.h/stdbool.h.
|
|
||||||
|
|
||||||
**Error**:
|
|
||||||
```
|
|
||||||
error: unknown type name 'Uint32'
|
|
||||||
error: unknown type name 'SDL_GPUColorTargetInfo'
|
|
||||||
```
|
|
||||||
|
|
||||||
**Solution**: Include actual SDL headers in generated mocks:
|
|
||||||
```c
|
|
||||||
// OLD - Missing types
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
// NEW - Proper type definitions
|
|
||||||
#include <SDL3/SDL_stdinc.h>
|
|
||||||
#include <SDL3/SDL_gpu.h>
|
|
||||||
```
|
|
||||||
|
|
||||||
Then add SDL include path to C compilation:
|
|
||||||
```zig
|
|
||||||
mock_lib.addIncludePath(b.path("SDL/include"));
|
|
||||||
```
|
|
||||||
|
|
||||||
**Key insight**: Mocks should compile like real SDL implementation files, with full access to SDL type definitions.
|
|
||||||
|
|
||||||
### Issue 3: Testing Strategy
|
|
||||||
|
|
||||||
**Problem**: Initial testing with tiny `test_small.h` (3 declarations) didn't reveal real-world issues.
|
|
||||||
|
|
||||||
**Solution**: Test with full production header (SDL_gpu.h with 169 declarations) to:
|
|
||||||
- Verify parser handles large inputs
|
|
||||||
- Catch type definition issues
|
|
||||||
- Validate all declaration types work together
|
|
||||||
- Ensure build system scales
|
|
||||||
|
|
||||||
**Lesson**: Always test with realistic, production-sized inputs, not toy examples.
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
- Zig 0.15 Release Notes: https://ziglang.org/download/0.15.0/release-notes.html
|
|
||||||
- Zig Standard Library Docs: https://ziglang.org/documentation/master/std/
|
|
||||||
|
|
@ -1,111 +0,0 @@
|
||||||
# SDL3 Parser - Overview
|
|
||||||
|
|
||||||
## What It Does
|
|
||||||
|
|
||||||
Automatically generates type-safe Zig bindings and C mock implementations from SDL3 C headers.
|
|
||||||
|
|
||||||
## How It Works
|
|
||||||
|
|
||||||
### 1. Lexical Analysis (patterns.zig)
|
|
||||||
- Scans C header files for SDL API patterns
|
|
||||||
- Extracts 5 declaration types:
|
|
||||||
- **Opaque types**: `typedef struct SDL_Type SDL_Type;`
|
|
||||||
- **Enums**: `typedef enum { ... } SDL_Type;`
|
|
||||||
- **Structs**: `typedef struct { ... } SDL_Type;`
|
|
||||||
- **Flags**: Packed bitfields from enums
|
|
||||||
- **Functions**: `extern SDL_DECLSPEC RetType SDLCALL SDL_Func(...);`
|
|
||||||
|
|
||||||
### 2. Type Conversion (types.zig)
|
|
||||||
- Maps C types to Zig equivalents:
|
|
||||||
- `bool` → `bool`
|
|
||||||
- `Uint32` → `u32`
|
|
||||||
- `SDL_Type*` → `?*Type` (nullable) or `*Type` (non-null)
|
|
||||||
- `void*` → `?*anyopaque`
|
|
||||||
- `const char*` → `[*c]const u8`
|
|
||||||
|
|
||||||
### 3. Naming Convention (naming.zig)
|
|
||||||
- Strips `SDL_` prefix
|
|
||||||
- Removes first underscore for grouping: `SDL_GPU_Device` → `GPUDevice`
|
|
||||||
- Converts to camelCase: `SDL_CreateGPUDevice` → `createGPUDevice`
|
|
||||||
|
|
||||||
### 4. Code Generation (codegen.zig)
|
|
||||||
- **Groups methods**: Functions with matching first parameter go inside opaque type
|
|
||||||
- **Generates inline wrappers**: Handle casting between Zig and C types
|
|
||||||
- **Formats output**: Uses Zig AST for proper formatting
|
|
||||||
|
|
||||||
### 5. Mock Generation (mock_codegen.zig)
|
|
||||||
- Creates C stub implementations for testing
|
|
||||||
- Includes actual SDL headers for type definitions
|
|
||||||
- Returns null/0/false for all functions
|
|
||||||
|
|
||||||
## Example
|
|
||||||
|
|
||||||
**Input** (SDL_gpu.h):
|
|
||||||
```c
|
|
||||||
typedef struct SDL_GPUDevice SDL_GPUDevice;
|
|
||||||
extern SDL_DECLSPEC SDL_GPUDevice* SDLCALL SDL_CreateGPUDevice(bool debug);
|
|
||||||
```
|
|
||||||
|
|
||||||
**Output Zig** (gpu.zig):
|
|
||||||
```zig
|
|
||||||
pub const GPUDevice = opaque {};
|
|
||||||
|
|
||||||
pub inline fn createGPUDevice(debug: bool) ?*GPUDevice {
|
|
||||||
return c.SDL_CreateGPUDevice(debug);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Output Mock** (gpu_mock.c):
|
|
||||||
```c
|
|
||||||
#include <SDL3/SDL_gpu.h>
|
|
||||||
|
|
||||||
SDL_GPUDevice* SDL_CreateGPUDevice(bool debug) {
|
|
||||||
(void)debug;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Generate bindings only
|
|
||||||
zig build run -- SDL_gpu.h --output=gpu.zig
|
|
||||||
|
|
||||||
# Generate bindings + mocks
|
|
||||||
zig build run -- SDL_gpu.h --output=gpu.zig --mocks=gpu_mock.c
|
|
||||||
|
|
||||||
# Test with SDL_gpu.h
|
|
||||||
zig build test-mocks
|
|
||||||
```
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
```
|
|
||||||
C Header → Scanner → AST → Type Mapper → Code Generator → Zig Bindings
|
|
||||||
↓
|
|
||||||
Mock Generator → C Mocks
|
|
||||||
```
|
|
||||||
|
|
||||||
## Statistics (SDL_gpu.h)
|
|
||||||
|
|
||||||
- **Input**: 169 declarations
|
|
||||||
- **Output**: 1,229 lines of Zig, 577 lines of C mocks
|
|
||||||
- **Compilation**: 71KB static library, 94 exported functions
|
|
||||||
- **Tests**: 7/7 passing
|
|
||||||
|
|
||||||
## Key Features
|
|
||||||
|
|
||||||
✅ Type-safe pointer handling (nullable vs non-null)
|
|
||||||
✅ Automatic method grouping in opaque types
|
|
||||||
✅ Minimal casting (only where needed)
|
|
||||||
✅ AST-based formatting
|
|
||||||
✅ C mocks with real SDL headers
|
|
||||||
✅ Handles large headers (169+ declarations)
|
|
||||||
|
|
||||||
## Limitations
|
|
||||||
|
|
||||||
- No dependency resolution (types from other headers)
|
|
||||||
- No `#define` parsing (except simple enums)
|
|
||||||
- No function pointer types
|
|
||||||
- No union types
|
|
||||||
- Requires manual `c.zig` for imports
|
|
||||||
|
|
@ -0,0 +1,213 @@
|
||||||
|
# SDL3 Parser - Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
parser/
|
||||||
|
├── README.md # Project overview and quick start
|
||||||
|
├── build.zig # Build configuration
|
||||||
|
├── build.zig.zon # Dependencies
|
||||||
|
│
|
||||||
|
├── src/ # Source code (900 lines)
|
||||||
|
│ ├── parser.zig # Main entry point, CLI
|
||||||
|
│ ├── patterns.zig # Pattern matching & scanning
|
||||||
|
│ ├── types.zig # C to Zig type conversion
|
||||||
|
│ ├── naming.zig # Naming convention handling
|
||||||
|
│ ├── codegen.zig # Zig code generation
|
||||||
|
│ ├── mock_codegen.zig # C mock generation
|
||||||
|
│ └── dependency_resolver.zig # Dependency analysis (NEW)
|
||||||
|
│
|
||||||
|
├── test/ # Test files
|
||||||
|
│ ├── integration/ # Integration tests
|
||||||
|
│ │ ├── test_multifield_*.zig
|
||||||
|
│ │ ├── test_typedef_*.zig
|
||||||
|
│ │ └── test_flow_*.zig
|
||||||
|
│ └── (pattern test files)
|
||||||
|
│
|
||||||
|
├── docs/ # Documentation (5,500+ lines)
|
||||||
|
│ ├── INDEX.md # Documentation index
|
||||||
|
│ │
|
||||||
|
│ ├── GETTING_STARTED.md # Installation and first use
|
||||||
|
│ ├── QUICKSTART.md # Quick reference
|
||||||
|
│ ├── API_REFERENCE.md # Command-line options
|
||||||
|
│ │
|
||||||
|
│ ├── ARCHITECTURE.md # System design
|
||||||
|
│ ├── DEPENDENCY_RESOLUTION.md # How deps work
|
||||||
|
│ ├── KNOWN_ISSUES.md # Limitations
|
||||||
|
│ │
|
||||||
|
│ ├── DEVELOPMENT.md # Contributing guide
|
||||||
|
│ ├── ROADMAP.md # Future plans
|
||||||
|
│ │
|
||||||
|
│ ├── DEPENDENCY_FLOW.md # Technical deep dive
|
||||||
|
│ ├── VISUAL_FLOW.md # Flow diagrams
|
||||||
|
│ ├── MULTI_FIELD_IMPLEMENTATION.md
|
||||||
|
│ ├── TYPEDEF_IMPLEMENTATION.md
|
||||||
|
│ ├── MULTI_HEADER_TEST_RESULTS.md
|
||||||
|
│ │
|
||||||
|
│ └── archive/ # Historical documents
|
||||||
|
│ └── (planning and status docs)
|
||||||
|
│
|
||||||
|
└── zig-out/ # Build artifacts
|
||||||
|
└── bin/sdl-parser # Executable
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation Organization
|
||||||
|
|
||||||
|
### User Documentation (Start Here)
|
||||||
|
1. README.md - Project overview
|
||||||
|
2. GETTING_STARTED.md - Tutorial
|
||||||
|
3. QUICKSTART.md - Quick reference
|
||||||
|
4. API_REFERENCE.md - Complete reference
|
||||||
|
|
||||||
|
### Technical Documentation
|
||||||
|
5. ARCHITECTURE.md - System design
|
||||||
|
6. DEPENDENCY_RESOLUTION.md - Feature details
|
||||||
|
7. DEPENDENCY_FLOW.md - Implementation walkthrough
|
||||||
|
8. VISUAL_FLOW.md - Diagrams
|
||||||
|
|
||||||
|
### Development Documentation
|
||||||
|
9. DEVELOPMENT.md - Contributing guide
|
||||||
|
10. KNOWN_ISSUES.md - Current limitations
|
||||||
|
11. ROADMAP.md - Future plans
|
||||||
|
|
||||||
|
### Implementation Documentation
|
||||||
|
12. MULTI_FIELD_IMPLEMENTATION.md - Struct parsing
|
||||||
|
13. TYPEDEF_IMPLEMENTATION.md - Typedef support
|
||||||
|
14. MULTI_HEADER_TEST_RESULTS.md - Test results
|
||||||
|
|
||||||
|
## Source Code Organization
|
||||||
|
|
||||||
|
### Core Pipeline
|
||||||
|
|
||||||
|
```
|
||||||
|
parser.zig (main)
|
||||||
|
↓
|
||||||
|
patterns.zig (scan)
|
||||||
|
↓
|
||||||
|
dependency_resolver.zig (resolve)
|
||||||
|
↓
|
||||||
|
codegen.zig (generate)
|
||||||
|
↓
|
||||||
|
Output (Zig/C)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Supporting Modules
|
||||||
|
|
||||||
|
- `types.zig` - Type conversion utilities
|
||||||
|
- `naming.zig` - Naming convention utilities
|
||||||
|
- `mock_codegen.zig` - C mock generation
|
||||||
|
|
||||||
|
## Build Outputs
|
||||||
|
|
||||||
|
### Local Build
|
||||||
|
|
||||||
|
```
|
||||||
|
zig-out/
|
||||||
|
├── bin/
|
||||||
|
│ └── sdl-parser # Executable
|
||||||
|
└── (test outputs)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Integration with lib/sdl3
|
||||||
|
|
||||||
|
```
|
||||||
|
lib/sdl3/
|
||||||
|
├── v2/ # Generated bindings
|
||||||
|
│ ├── gpu.zig # SDL_gpu.h bindings
|
||||||
|
│ ├── video.zig # SDL_video.h (if working)
|
||||||
|
│ └── ...
|
||||||
|
└── zig-out/
|
||||||
|
├── gpu_test.zig # Test bindings
|
||||||
|
└── gpu_test_mock.c # Test mocks
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test Organization
|
||||||
|
|
||||||
|
### Unit Tests (in source files)
|
||||||
|
|
||||||
|
Each src/*.zig file contains tests at the bottom:
|
||||||
|
- Pattern matching tests
|
||||||
|
- Type conversion tests
|
||||||
|
- Naming convention tests
|
||||||
|
|
||||||
|
### Integration Tests (test/integration/)
|
||||||
|
|
||||||
|
- `test_multifield_*.zig` - Multi-field struct parsing
|
||||||
|
- `test_typedef_*.zig` - Typedef scanning
|
||||||
|
- `test_flow_*.zig` - Dependency resolution
|
||||||
|
- `test_*.c` - Test input files
|
||||||
|
|
||||||
|
### Running Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# All tests
|
||||||
|
zig build test
|
||||||
|
|
||||||
|
# Specific test file
|
||||||
|
zig test test/integration/test_typedef_simple.zig
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation Categories
|
||||||
|
|
||||||
|
### For Users
|
||||||
|
- Getting started, quickstart, API reference
|
||||||
|
- Focus: How to use the tool
|
||||||
|
|
||||||
|
### For Understanding
|
||||||
|
- Architecture, dependency resolution
|
||||||
|
- Focus: How it works internally
|
||||||
|
|
||||||
|
### For Developers
|
||||||
|
- Development guide, implementation docs
|
||||||
|
- Focus: How to extend and contribute
|
||||||
|
|
||||||
|
### For Reference
|
||||||
|
- Technical deep dives, flow diagrams
|
||||||
|
- Focus: Complete implementation details
|
||||||
|
|
||||||
|
## File Size Reference
|
||||||
|
|
||||||
|
### Source Code
|
||||||
|
- Total: ~900 lines production code
|
||||||
|
- Average: ~150 lines per module
|
||||||
|
- Largest: dependency_resolver.zig (454 lines)
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
- Total: ~5,500 lines
|
||||||
|
- User guides: ~1,500 lines
|
||||||
|
- Technical docs: ~2,500 lines
|
||||||
|
- Implementation details: ~1,500 lines
|
||||||
|
|
||||||
|
### Tests
|
||||||
|
- Unit tests: ~400 lines (in source files)
|
||||||
|
- Integration tests: ~500 lines (separate files)
|
||||||
|
- Total: ~900 lines
|
||||||
|
|
||||||
|
## Quick Navigation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Main documentation entry point
|
||||||
|
cat README.md
|
||||||
|
|
||||||
|
# Start tutorial
|
||||||
|
cat docs/GETTING_STARTED.md
|
||||||
|
|
||||||
|
# Command reference
|
||||||
|
cat docs/API_REFERENCE.md
|
||||||
|
|
||||||
|
# Understand internals
|
||||||
|
cat docs/ARCHITECTURE.md
|
||||||
|
|
||||||
|
# Fix issues
|
||||||
|
cat docs/KNOWN_ISSUES.md
|
||||||
|
|
||||||
|
# Contribute
|
||||||
|
cat docs/DEVELOPMENT.md
|
||||||
|
|
||||||
|
# All docs
|
||||||
|
ls docs/
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Last Updated**: 2026-01-22
|
||||||
|
**Documentation Version**: 2.1
|
||||||
|
**Status**: Clean and organized ✅
|
||||||
|
|
@ -0,0 +1,189 @@
|
||||||
|
# SDL3 Header Parser
|
||||||
|
|
||||||
|
A Zig tool that automatically generates idiomatic Zig bindings from SDL3 C headers with automatic dependency resolution.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
✅ **Automatic Dependency Resolution** - Detects and extracts missing types from included headers
|
||||||
|
✅ **Multi-Field Struct Parsing** - Handles compact C syntax like `int x, y;`
|
||||||
|
✅ **Type Conversion** - Converts C types to idiomatic Zig types
|
||||||
|
✅ **Method Organization** - Groups functions as methods on opaque types
|
||||||
|
✅ **Mock Generation** - Creates C stub implementations for testing
|
||||||
|
✅ **Production Ready** - 100% dependency resolution for SDL_gpu.h
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd parser/
|
||||||
|
zig build # Build the parser
|
||||||
|
zig build test # Run tests (26+ tests)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Basic Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate Zig bindings
|
||||||
|
zig build run -- ../SDL/include/SDL3/SDL_gpu.h --output=gpu.zig
|
||||||
|
|
||||||
|
# Generate with C mocks for testing
|
||||||
|
zig build run -- ../SDL/include/SDL3/SDL_gpu.h --output=gpu.zig --mocks=gpu_mock.c
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example Output
|
||||||
|
|
||||||
|
**Input** (SDL_gpu.h):
|
||||||
|
```c
|
||||||
|
typedef struct SDL_GPUDevice SDL_GPUDevice;
|
||||||
|
extern SDL_DECLSPEC void SDLCALL SDL_DestroyGPUDevice(SDL_GPUDevice *device);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Output** (gpu.zig):
|
||||||
|
```zig
|
||||||
|
pub const GPUDevice = opaque {
|
||||||
|
pub inline fn destroyGPUDevice(gpudevice: *GPUDevice) void {
|
||||||
|
return c.SDL_DestroyGPUDevice(gpudevice);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Supported C Patterns
|
||||||
|
|
||||||
|
### Type Declarations
|
||||||
|
- **Opaque types**: `typedef struct SDL_Type SDL_Type;`
|
||||||
|
- **Structs**: `typedef struct { int x, y; } SDL_Rect;` (multi-field support!)
|
||||||
|
- **Enums**: `typedef enum { VALUE1, VALUE2 } SDL_Enum;`
|
||||||
|
- **Flags**: Bitfield enums with `#define` values
|
||||||
|
- **Typedefs**: `typedef Uint32 SDL_PropertiesID;`
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
- **Extern functions**: `extern SDL_DECLSPEC RetType SDLCALL SDL_Func(...);`
|
||||||
|
- **Method grouping**: Functions with opaque first parameter become methods
|
||||||
|
|
||||||
|
### Automatic Type Conversion
|
||||||
|
|
||||||
|
| C Type | Zig Type |
|
||||||
|
|--------|----------|
|
||||||
|
| `bool` | `bool` |
|
||||||
|
| `Uint32` | `u32` |
|
||||||
|
| `int` | `c_int` |
|
||||||
|
| `SDL_Type*` | `?*Type` |
|
||||||
|
| `const SDL_Type*` | `*const Type` |
|
||||||
|
| `void*` | `?*anyopaque` |
|
||||||
|
|
||||||
|
## Dependency Resolution
|
||||||
|
|
||||||
|
The parser automatically:
|
||||||
|
1. Detects types referenced but not defined
|
||||||
|
2. Searches included headers for definitions
|
||||||
|
3. Extracts required types
|
||||||
|
4. Generates unified output with all dependencies
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```
|
||||||
|
SDL_gpu.h references SDL_Window
|
||||||
|
→ Parser finds #include <SDL3/SDL_video.h>
|
||||||
|
→ Extracts SDL_Window definition
|
||||||
|
→ Includes in output automatically
|
||||||
|
```
|
||||||
|
|
||||||
|
**Success Rate**: 100% for SDL_gpu.h (5/5 dependencies)
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
**Start Here**: [Getting Started Guide](docs/GETTING_STARTED.md)
|
||||||
|
|
||||||
|
### User Guides
|
||||||
|
- **[Getting Started](docs/GETTING_STARTED.md)** - Installation and first steps
|
||||||
|
- **[Quickstart](docs/QUICKSTART.md)** - Quick reference
|
||||||
|
- **[API Reference](docs/API_REFERENCE.md)** - All command-line options
|
||||||
|
|
||||||
|
### Technical Docs
|
||||||
|
- **[Architecture](docs/ARCHITECTURE.md)** - How the parser works
|
||||||
|
- **[Dependency Resolution](docs/DEPENDENCY_RESOLUTION.md)** - Automatic type extraction
|
||||||
|
- **[Known Issues](docs/KNOWN_ISSUES.md)** - Current limitations
|
||||||
|
|
||||||
|
### Development
|
||||||
|
- **[Development Guide](docs/DEVELOPMENT.md)** - Contributing and extending
|
||||||
|
- **[Roadmap](docs/ROADMAP.md)** - Future plans
|
||||||
|
|
||||||
|
### Complete Index
|
||||||
|
- **[Documentation Index](docs/INDEX.md)** - All documentation
|
||||||
|
|
||||||
|
## Project Status
|
||||||
|
|
||||||
|
### Production Ready ✅
|
||||||
|
- SDL_gpu.h: 100% working
|
||||||
|
- 26+ tests passing
|
||||||
|
- Comprehensive documentation
|
||||||
|
- Zero manual intervention needed
|
||||||
|
|
||||||
|
### Tested Headers
|
||||||
|
|
||||||
|
| Header | Status | Dependencies | Notes |
|
||||||
|
|--------|--------|--------------|-------|
|
||||||
|
| SDL_gpu.h | ✅ Complete | 5/5 (100%) | Production ready |
|
||||||
|
| SDL_keyboard.h | ⚠️ Partial | 6/6 resolved | Enum syntax issues |
|
||||||
|
| SDL_video.h | ⚠️ Partial | 5/14 resolved | Needs fixes |
|
||||||
|
| SDL_events.h | ⚠️ Partial | Unknown | Needs fixes |
|
||||||
|
|
||||||
|
See [Known Issues](docs/KNOWN_ISSUES.md) for details.
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
- Small headers (<100 decls): ~100ms
|
||||||
|
- Large headers (SDL_gpu.h, 169 decls): ~520ms
|
||||||
|
- Memory usage: ~2-5MB peak
|
||||||
|
- Output: ~1KB per declaration
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Zig 0.15+
|
||||||
|
- SDL3 headers (included in parent directory)
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Parse a Header
|
||||||
|
```bash
|
||||||
|
zig build run -- ../SDL/include/SDL3/SDL_gpu.h --output=gpu.zig
|
||||||
|
```
|
||||||
|
|
||||||
|
### Use Generated Bindings
|
||||||
|
```zig
|
||||||
|
const gpu = @import("gpu.zig");
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
const device = gpu.createGPUDevice(true);
|
||||||
|
defer if (device) |d| d.destroyGPUDevice();
|
||||||
|
|
||||||
|
// All dependency types available automatically
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run Tests
|
||||||
|
```bash
|
||||||
|
zig build test
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
See [DEVELOPMENT.md](docs/DEVELOPMENT.md) for:
|
||||||
|
- Architecture overview
|
||||||
|
- Adding new patterns
|
||||||
|
- Testing guidelines
|
||||||
|
- Code style
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Part of the Backlog game engine project.
|
||||||
|
|
||||||
|
## Acknowledgments
|
||||||
|
|
||||||
|
Developed for automatic SDL3 binding generation in the Backlog engine.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Version**: 2.1
|
||||||
|
**Status**: Production ready for SDL_gpu.h
|
||||||
|
**Last Updated**: 2026-01-22
|
||||||
|
|
@ -0,0 +1,402 @@
|
||||||
|
# API Reference
|
||||||
|
|
||||||
|
Complete reference for the SDL3 header parser command-line interface.
|
||||||
|
|
||||||
|
## Command Syntax
|
||||||
|
|
||||||
|
```bash
|
||||||
|
zig build run -- <header_file> [options]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Arguments
|
||||||
|
|
||||||
|
### Required
|
||||||
|
|
||||||
|
**`<header_file>`** - Path to SDL C header file
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
```bash
|
||||||
|
zig build run -- ../SDL/include/SDL3/SDL_gpu.h
|
||||||
|
zig build run -- /full/path/to/SDL_video.h
|
||||||
|
zig build run -- relative/path/to/header.h
|
||||||
|
```
|
||||||
|
|
||||||
|
### Optional
|
||||||
|
|
||||||
|
**`--output=<file>`** - Write output to file instead of stdout
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
```bash
|
||||||
|
--output=gpu.zig
|
||||||
|
--output=bindings/video.zig
|
||||||
|
--output=/tmp/test.zig
|
||||||
|
```
|
||||||
|
|
||||||
|
**`--mocks=<file>`** - Generate C mock implementations
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
```bash
|
||||||
|
--mocks=gpu_mock.c
|
||||||
|
--mocks=test/mocks.c
|
||||||
|
```
|
||||||
|
|
||||||
|
## Output Formats
|
||||||
|
|
||||||
|
### Zig Bindings (Default)
|
||||||
|
|
||||||
|
Generated when `--output` is specified (or to stdout if not):
|
||||||
|
|
||||||
|
```zig
|
||||||
|
pub const c = @import("c.zig").c;
|
||||||
|
|
||||||
|
pub const GPUDevice = opaque {
|
||||||
|
pub inline fn createGPUDevice(debug_mode: bool) ?*GPUDevice {
|
||||||
|
return c.SDL_CreateGPUDevice(debug_mode);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
**Features**:
|
||||||
|
- Type conversions (C → Zig)
|
||||||
|
- Method organization
|
||||||
|
- Dependency inclusion
|
||||||
|
- Doc comments preserved
|
||||||
|
|
||||||
|
### C Mocks (Optional)
|
||||||
|
|
||||||
|
Generated when `--mocks` is specified:
|
||||||
|
|
||||||
|
```c
|
||||||
|
// Auto-generated C mock implementations
|
||||||
|
#include <SDL3/SDL_gpu.h>
|
||||||
|
|
||||||
|
SDL_GPUDevice* SDL_CreateGPUDevice(bool debug_mode) {
|
||||||
|
return NULL; // Mock: always returns null
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDL_DestroyGPUDevice(SDL_GPUDevice *device) {
|
||||||
|
// Mock: no-op
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Use Case**: Testing without real SDL implementation
|
||||||
|
|
||||||
|
## Build System Integration
|
||||||
|
|
||||||
|
### In build.zig
|
||||||
|
|
||||||
|
```zig
|
||||||
|
const parser_dep = b.dependency("sdl3_parser", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
const parser_exe = parser_dep.artifact("sdl-parser");
|
||||||
|
|
||||||
|
const gen = b.addRunArtifact(parser_exe);
|
||||||
|
gen.addFileArg(b.path("SDL/include/SDL3/SDL_gpu.h"));
|
||||||
|
gen.addArg("--output=src/gpu.zig");
|
||||||
|
|
||||||
|
const gen_step = b.step("generate", "Generate SDL bindings");
|
||||||
|
gen_step.dependOn(&gen.step);
|
||||||
|
```
|
||||||
|
|
||||||
|
Then run:
|
||||||
|
```bash
|
||||||
|
zig build generate
|
||||||
|
```
|
||||||
|
|
||||||
|
## Parser Behavior
|
||||||
|
|
||||||
|
### Dependency Resolution
|
||||||
|
|
||||||
|
**Automatic** - No configuration needed
|
||||||
|
|
||||||
|
When the parser detects missing types, it:
|
||||||
|
1. Parses `#include` directives from the header
|
||||||
|
2. Searches each included header
|
||||||
|
3. Extracts matching type definitions
|
||||||
|
4. Includes them in the output
|
||||||
|
|
||||||
|
**Progress Reporting**:
|
||||||
|
```
|
||||||
|
Analyzing dependencies...
|
||||||
|
Found 5 missing types:
|
||||||
|
- SDL_Window
|
||||||
|
- SDL_Rect
|
||||||
|
...
|
||||||
|
|
||||||
|
Resolving dependencies...
|
||||||
|
✓ Found SDL_Window in SDL_video.h
|
||||||
|
✓ Found SDL_Rect in SDL_rect.h
|
||||||
|
```
|
||||||
|
|
||||||
|
### Type Filtering
|
||||||
|
|
||||||
|
Only SDL types are processed:
|
||||||
|
- Types starting with `SDL_`
|
||||||
|
- Known SDL types (Window, Rect, etc.)
|
||||||
|
|
||||||
|
Primitive types are ignored:
|
||||||
|
- `bool`, `int`, `float`, `void`, etc.
|
||||||
|
|
||||||
|
### Pattern Matching Order
|
||||||
|
|
||||||
|
Patterns are tried in this order:
|
||||||
|
1. Opaque types (`typedef struct X X;`)
|
||||||
|
2. Enums (`typedef enum {...} X;`)
|
||||||
|
3. Structs (`typedef struct {...} X;`)
|
||||||
|
4. Flags (`typedef Uint32 SDL_Flags;` + `#define` values)
|
||||||
|
5. Typedefs (`typedef Type SDL_Alias;`)
|
||||||
|
6. Functions (`extern SDL_DECLSPEC ...`)
|
||||||
|
|
||||||
|
**Note**: Order matters! Flags must be tried before simple typedefs.
|
||||||
|
|
||||||
|
### Naming Conventions
|
||||||
|
|
||||||
|
**Types**:
|
||||||
|
- `SDL_GPUDevice` → `GPUDevice` (strip `SDL_` prefix)
|
||||||
|
- `SDL_GPU_PRIMITIVE_TYPE` → `GPUPrimitiveType` (remove first underscore)
|
||||||
|
|
||||||
|
**Functions**:
|
||||||
|
- `SDL_CreateGPUDevice` → `createGPUDevice` (strip `SDL_`, camelCase)
|
||||||
|
|
||||||
|
**Enum Values**:
|
||||||
|
- `SDL_GPU_PRIMITIVETYPE_TRIANGLELIST` → `primitiveTypeTrianglelist`
|
||||||
|
|
||||||
|
**Parameters**:
|
||||||
|
- `SDL_GPUDevice *device` → `device: ?*GPUDevice`
|
||||||
|
|
||||||
|
## Exit Codes
|
||||||
|
|
||||||
|
- `0` - Success
|
||||||
|
- `1` - Error (file not found, out of memory, invalid arguments)
|
||||||
|
|
||||||
|
## Console Output
|
||||||
|
|
||||||
|
### Normal Operation
|
||||||
|
|
||||||
|
```
|
||||||
|
SDL3 Header Parser
|
||||||
|
==================
|
||||||
|
|
||||||
|
Parsing: header.h
|
||||||
|
|
||||||
|
Found N declarations
|
||||||
|
- Opaque types: X
|
||||||
|
- Typedefs: X
|
||||||
|
- Enums: X
|
||||||
|
- Structs: X
|
||||||
|
- Flags: X
|
||||||
|
- Functions: X
|
||||||
|
|
||||||
|
Analyzing dependencies...
|
||||||
|
[dependency information]
|
||||||
|
|
||||||
|
Generated: output.zig
|
||||||
|
```
|
||||||
|
|
||||||
|
### With Warnings
|
||||||
|
|
||||||
|
```
|
||||||
|
Resolving dependencies...
|
||||||
|
✓ Found SDL_Window in SDL_video.h
|
||||||
|
⚠ Warning: Could not find definition for type: SDL_Unknown
|
||||||
|
```
|
||||||
|
|
||||||
|
### With Errors
|
||||||
|
|
||||||
|
```
|
||||||
|
Error: 5 syntax errors detected in generated code
|
||||||
|
Line 10: expected_comma_after_field
|
||||||
|
Line 12: expected_type_expr
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
File is still written, but may need manual fixes.
|
||||||
|
|
||||||
|
## Type Conversion Reference
|
||||||
|
|
||||||
|
### Integer Types
|
||||||
|
|
||||||
|
| C Type | Zig Type |
|
||||||
|
|--------|----------|
|
||||||
|
| `Uint8` | `u8` |
|
||||||
|
| `Uint16` | `u16` |
|
||||||
|
| `Uint32` | `u32` |
|
||||||
|
| `Uint64` | `u64` |
|
||||||
|
| `Sint8` | `i8` |
|
||||||
|
| `Sint16` | `i16` |
|
||||||
|
| `Sint32` | `i32` |
|
||||||
|
| `Sint64` | `i64` |
|
||||||
|
| `int` | `c_int` |
|
||||||
|
| `unsigned int` | `c_uint` |
|
||||||
|
| `size_t` | `usize` |
|
||||||
|
|
||||||
|
### Pointer Types
|
||||||
|
|
||||||
|
| C Type | Zig Type |
|
||||||
|
|--------|----------|
|
||||||
|
| `SDL_Type*` | `?*Type` (nullable) |
|
||||||
|
| `const SDL_Type*` | `*const Type` |
|
||||||
|
| `SDL_Type**` | `?*?*Type` |
|
||||||
|
| `void*` | `?*anyopaque` |
|
||||||
|
| `const void*` | `*const anyopaque` |
|
||||||
|
| `const char*` | `[*c]const u8` |
|
||||||
|
|
||||||
|
### Special Types
|
||||||
|
|
||||||
|
| C Type | Zig Type |
|
||||||
|
|--------|----------|
|
||||||
|
| `bool` | `bool` |
|
||||||
|
| `float` | `f32` |
|
||||||
|
| `double` | `f64` |
|
||||||
|
| `size_t` | `usize` |
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Example 1: Simple Header
|
||||||
|
|
||||||
|
**Input** (simple.h):
|
||||||
|
```c
|
||||||
|
typedef struct SDL_Thing SDL_Thing;
|
||||||
|
typedef Uint32 SDL_ThingID;
|
||||||
|
|
||||||
|
extern SDL_DECLSPEC SDL_ThingID SDLCALL SDL_CreateThing(void);
|
||||||
|
extern SDL_DECLSPEC void SDLCALL SDL_DestroyThing(SDL_Thing *thing);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Command**:
|
||||||
|
```bash
|
||||||
|
zig build run -- simple.h --output=thing.zig
|
||||||
|
```
|
||||||
|
|
||||||
|
**Output** (thing.zig):
|
||||||
|
```zig
|
||||||
|
pub const c = @import("c.zig").c;
|
||||||
|
|
||||||
|
pub const ThingID = u32;
|
||||||
|
|
||||||
|
pub const Thing = opaque {
|
||||||
|
pub inline fn destroyThing(thing: *Thing) void {
|
||||||
|
return c.SDL_DestroyThing(thing);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub inline fn createThing() ThingID {
|
||||||
|
return c.SDL_CreateThing();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 2: With Dependencies
|
||||||
|
|
||||||
|
**Input** (depends.h):
|
||||||
|
```c
|
||||||
|
#include <SDL3/SDL_rect.h>
|
||||||
|
|
||||||
|
extern void SDL_UseRect(SDL_Rect *rect);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Command**:
|
||||||
|
```bash
|
||||||
|
zig build run -- depends.h --output=depends.zig
|
||||||
|
```
|
||||||
|
|
||||||
|
**Output**:
|
||||||
|
```zig
|
||||||
|
pub const c = @import("c.zig").c;
|
||||||
|
|
||||||
|
// Dependency automatically included
|
||||||
|
pub const Rect = extern struct {
|
||||||
|
x: c_int,
|
||||||
|
y: c_int,
|
||||||
|
w: c_int,
|
||||||
|
h: c_int,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub inline fn useRect(rect: *Rect) void {
|
||||||
|
return c.SDL_UseRect(rect);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 3: With Mocks
|
||||||
|
|
||||||
|
**Command**:
|
||||||
|
```bash
|
||||||
|
zig build run -- simple.h --output=thing.zig --mocks=thing_mock.c
|
||||||
|
```
|
||||||
|
|
||||||
|
**Output** (thing_mock.c):
|
||||||
|
```c
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
|
SDL_ThingID SDL_CreateThing(void) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDL_DestroyThing(SDL_Thing *thing) {
|
||||||
|
// No-op
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Characteristics
|
||||||
|
|
||||||
|
### Timing
|
||||||
|
|
||||||
|
| Operation | Time (SDL_gpu.h) |
|
||||||
|
|-----------|------------------|
|
||||||
|
| Parse primary header | ~50ms |
|
||||||
|
| Analyze dependencies | ~10ms |
|
||||||
|
| Extract dependencies | ~300ms |
|
||||||
|
| Generate code | ~150ms |
|
||||||
|
| **Total** | **~520ms** |
|
||||||
|
|
||||||
|
### Memory
|
||||||
|
|
||||||
|
| Component | Memory |
|
||||||
|
|-----------|--------|
|
||||||
|
| Source files | ~150KB |
|
||||||
|
| Declarations | ~2MB |
|
||||||
|
| Output | ~53KB |
|
||||||
|
| **Peak Total** | **~2.2MB** |
|
||||||
|
|
||||||
|
### Scaling
|
||||||
|
|
||||||
|
- **Time**: O(n + h×d) where n=lines, h=headers, d=declarations
|
||||||
|
- **Memory**: O(d) where d=total declarations
|
||||||
|
- **Linear scaling** with input size
|
||||||
|
|
||||||
|
## Advanced Usage
|
||||||
|
|
||||||
|
### Batch Processing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
for header in SDL/include/SDL3/SDL_*.h; do
|
||||||
|
name=$(basename "$header" .h)
|
||||||
|
zig build run -- "$header" --output="bindings/${name}.zig"
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
### CI/CD Integration
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Generate SDL bindings
|
||||||
|
run: |
|
||||||
|
cd lib/sdl3
|
||||||
|
zig build regenerate-zig
|
||||||
|
git diff --exit-code v2/*.zig || echo "Bindings updated"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Validation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate and validate
|
||||||
|
zig build run -- header.h --output=test.zig
|
||||||
|
zig ast-check test.zig
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**See Also**:
|
||||||
|
- [Getting Started](GETTING_STARTED.md) - Basic usage tutorial
|
||||||
|
- [Architecture](ARCHITECTURE.md) - How it works internally
|
||||||
|
- [Known Issues](KNOWN_ISSUES.md) - Current limitations
|
||||||
|
|
@ -0,0 +1,430 @@
|
||||||
|
# ## Documentation
|
||||||
|
|
||||||
|
- **[README](../README.md)** - Project overview and quick start
|
||||||
|
- **[Getting Started](GETTING_STARTED.md)** - Installation and first steps
|
||||||
|
- **[Architecture](ARCHITECTURE.md)** - How the parser works
|
||||||
|
- **[Dependency Resolution](DEPENDENCY_RESOLUTION.md)** - Automatic type extraction
|
||||||
|
- **[API Reference](API_REFERENCE.md)** - Command-line options and features
|
||||||
|
- **[Known Issues](KNOWN_ISSUES.md)** - Limitations and workarounds
|
||||||
|
- **[Quickstart Guide](QUICKSTART.md)** - Quick reference
|
||||||
|
- **[Roadmap](ROADMAP.md)** - Future plans and priorities
|
||||||
|
|
||||||
|
## Technical Deep Dives
|
||||||
|
|
||||||
|
For implementation details and visual guides:
|
||||||
|
- **[Dependency Flow](DEPENDENCY_FLOW.md)** - Complete technical walkthrough
|
||||||
|
- **[Visual Flow Diagrams](VISUAL_FLOW.md)** - Quick reference diagrams
|
||||||
|
- **[Multi-Field Structs](MULTI_FIELD_IMPLEMENTATION.md)** - Struct parsing details
|
||||||
|
- **[Typedef Support](TYPEDEF_IMPLEMENTATION.md)** - Typedef implementation
|
||||||
|
- **[Multi-Header Testing](MULTI_HEADER_TEST_RESULTS.md)** - Test results
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
- **[Development Guide](DEVELOPMENT.md)** - Contributing and extending the parser
|
||||||
|
|
||||||
|
## Archive
|
||||||
|
|
||||||
|
Historical planning documents are in `archive/` for reference.
|
||||||
|
|
||||||
|
## High-Level Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
Input (C Header) → Scanner → Declarations → Dependency Resolver → CodeGen → Output (Zig)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Core Components
|
||||||
|
|
||||||
|
### 1. Scanner (`src/patterns.zig`)
|
||||||
|
|
||||||
|
**Purpose**: Parse C header files into structured declarations
|
||||||
|
|
||||||
|
**Process**:
|
||||||
|
1. Reads header file line by line
|
||||||
|
2. Tries to match each line against known patterns
|
||||||
|
3. Extracts type information, comments, and structure
|
||||||
|
4. Returns array of `Declaration` structures
|
||||||
|
|
||||||
|
**Supported Patterns**:
|
||||||
|
- Opaque types: `typedef struct SDL_X SDL_X;`
|
||||||
|
- Typedefs: `typedef Uint32 SDL_PropertiesID;`
|
||||||
|
- Enums: `typedef enum { ... } SDL_Type;`
|
||||||
|
- Structs: `typedef struct { int x, y; } SDL_Rect;`
|
||||||
|
- Flags: `typedef Uint32 SDL_Flags;` + `#define` values
|
||||||
|
- Functions: `extern SDL_DECLSPEC void SDLCALL SDL_Func(...);`
|
||||||
|
|
||||||
|
### 2. Dependency Resolver (`src/dependency_resolver.zig`)
|
||||||
|
|
||||||
|
**Purpose**: Automatically find and extract missing type definitions
|
||||||
|
|
||||||
|
**Process**:
|
||||||
|
1. Scans all declarations to find referenced types
|
||||||
|
2. Compares referenced types against defined types
|
||||||
|
3. Identifies missing types
|
||||||
|
4. Parses `#include` directives from source
|
||||||
|
5. Searches included headers for missing types
|
||||||
|
6. Extracts and clones matching declarations
|
||||||
|
|
||||||
|
**Key Features**:
|
||||||
|
- Type string normalization (strips `*`, `const`, etc.)
|
||||||
|
- Deduplication using HashMaps
|
||||||
|
- Deep cloning for safe ownership
|
||||||
|
- Selective extraction (only types needed)
|
||||||
|
|
||||||
|
### 3. Code Generator (`src/codegen.zig`)
|
||||||
|
|
||||||
|
**Purpose**: Convert C declarations to idiomatic Zig code
|
||||||
|
|
||||||
|
**Process**:
|
||||||
|
1. Groups functions by first parameter type (method categorization)
|
||||||
|
2. Generates type declarations
|
||||||
|
3. Generates function wrappers
|
||||||
|
4. Applies naming conventions
|
||||||
|
5. Performs type conversion
|
||||||
|
|
||||||
|
**Features**:
|
||||||
|
- Method organization for opaque types
|
||||||
|
- Inline function wrappers
|
||||||
|
- Automatic type conversion
|
||||||
|
- Doc comment preservation
|
||||||
|
|
||||||
|
### 4. Type Converter (`src/types.zig`)
|
||||||
|
|
||||||
|
**Purpose**: Convert C types to Zig equivalents
|
||||||
|
|
||||||
|
**Conversions**:
|
||||||
|
```zig
|
||||||
|
"bool" → "bool"
|
||||||
|
"Uint32" → "u32"
|
||||||
|
"int" → "c_int"
|
||||||
|
"SDL_Type *" → "?*Type"
|
||||||
|
"const SDL_Type *" → "*const Type"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Naming Convention Handler (`src/naming.zig`)
|
||||||
|
|
||||||
|
**Purpose**: Convert C names to idiomatic Zig
|
||||||
|
|
||||||
|
**Rules**:
|
||||||
|
- Strip `SDL_` prefix: `SDL_GPUDevice` → `GPUDevice`
|
||||||
|
- Remove first underscore: `SDL_GPU_TYPE` → `GPUType`
|
||||||
|
- CamelCase functions: `SDL_CreateDevice` → `createDevice`
|
||||||
|
- Lowercase first letter for values
|
||||||
|
|
||||||
|
## Data Flow
|
||||||
|
|
||||||
|
### 1. Parsing Phase
|
||||||
|
|
||||||
|
```
|
||||||
|
C Header File
|
||||||
|
↓
|
||||||
|
Scanner.scan()
|
||||||
|
↓
|
||||||
|
[]Declaration {
|
||||||
|
.opaque_type,
|
||||||
|
.typedef_decl,
|
||||||
|
.enum_decl,
|
||||||
|
.struct_decl,
|
||||||
|
.flag_decl,
|
||||||
|
.function_decl,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Dependency Analysis Phase
|
||||||
|
|
||||||
|
```
|
||||||
|
[]Declaration
|
||||||
|
↓
|
||||||
|
DependencyResolver.analyze()
|
||||||
|
├─ collectDefinedTypes() → defined_types HashMap
|
||||||
|
└─ collectReferencedTypes() → referenced_types HashMap
|
||||||
|
↓
|
||||||
|
getMissingTypes()
|
||||||
|
↓
|
||||||
|
missing_types = referenced - defined
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Dependency Resolution Phase
|
||||||
|
|
||||||
|
```
|
||||||
|
For each missing_type:
|
||||||
|
Parse #include directives
|
||||||
|
↓
|
||||||
|
For each included header:
|
||||||
|
Read header file
|
||||||
|
↓
|
||||||
|
Scanner.scan()
|
||||||
|
↓
|
||||||
|
Search for matching type
|
||||||
|
↓
|
||||||
|
If found: cloneDeclaration()
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Code Generation Phase
|
||||||
|
|
||||||
|
```
|
||||||
|
[]Declaration (primary + dependencies)
|
||||||
|
↓
|
||||||
|
CodeGen.generate()
|
||||||
|
├─ categorizeDeclarations() (group methods)
|
||||||
|
├─ writeHeader()
|
||||||
|
└─ writeDeclarations()
|
||||||
|
├─ writeOpaqueWithMethods()
|
||||||
|
├─ writeTypedef()
|
||||||
|
├─ writeEnum()
|
||||||
|
├─ writeStruct()
|
||||||
|
├─ writeFlags()
|
||||||
|
└─ writeFunction()
|
||||||
|
↓
|
||||||
|
Zig source code (string)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Validation Phase
|
||||||
|
|
||||||
|
```
|
||||||
|
Generated Zig code
|
||||||
|
↓
|
||||||
|
std.zig.Ast.parse()
|
||||||
|
↓
|
||||||
|
Check for syntax errors
|
||||||
|
↓
|
||||||
|
ast.renderAlloc() (format)
|
||||||
|
↓
|
||||||
|
Write to file or stdout
|
||||||
|
```
|
||||||
|
|
||||||
|
## Key Algorithms
|
||||||
|
|
||||||
|
### Type Extraction
|
||||||
|
|
||||||
|
**Purpose**: Strip pointer/const decorators to get base type
|
||||||
|
|
||||||
|
```zig
|
||||||
|
"SDL_Window *" → "SDL_Window"
|
||||||
|
"?*const SDL_Rect" → "SDL_Rect"
|
||||||
|
"SDL_Buffer *const *" → "SDL_Buffer"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Algorithm**:
|
||||||
|
1. Trim whitespace
|
||||||
|
2. Remove leading qualifiers (`const`, `*`, `?`)
|
||||||
|
3. Remove trailing qualifiers (`*`, `*const`, ` const`)
|
||||||
|
4. Handle special patterns (`[*c]`)
|
||||||
|
5. Return base type string
|
||||||
|
|
||||||
|
### Multi-Field Parsing
|
||||||
|
|
||||||
|
**Purpose**: Handle C compact syntax like `int x, y;`
|
||||||
|
|
||||||
|
**Algorithm**:
|
||||||
|
1. Detect comma in field declaration
|
||||||
|
2. Extract common type (before first field name)
|
||||||
|
3. Split remaining part on commas
|
||||||
|
4. Create separate `FieldDecl` for each name
|
||||||
|
5. Return array of fields
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```c
|
||||||
|
int x, y; → [FieldDecl{.name="x", .type="int"},
|
||||||
|
FieldDecl{.name="y", .type="int"}]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Method Categorization
|
||||||
|
|
||||||
|
**Purpose**: Determine if function should be a method
|
||||||
|
|
||||||
|
**Algorithm**:
|
||||||
|
1. Check if function has parameters
|
||||||
|
2. Get type of first parameter
|
||||||
|
3. Check if type is an opaque type pointer
|
||||||
|
4. If yes, add to opaque type's methods
|
||||||
|
5. If no, write as standalone function
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```c
|
||||||
|
void SDL_Destroy(SDL_Device *d) → Method of GPUDevice
|
||||||
|
void SDL_Init(void) → Standalone function
|
||||||
|
```
|
||||||
|
|
||||||
|
## Memory Management
|
||||||
|
|
||||||
|
### Ownership Rules
|
||||||
|
|
||||||
|
1. **Scanner owns strings** during parsing (allocated from its allocator)
|
||||||
|
2. **Parser owns declarations** after scanning (freed at end of main)
|
||||||
|
3. **Resolver owns HashMap keys** (duped when inserted, freed in deinit)
|
||||||
|
4. **Cloned declarations own strings** (allocated explicitly, freed by caller)
|
||||||
|
|
||||||
|
### Allocation Strategy
|
||||||
|
|
||||||
|
```
|
||||||
|
GPA (General Purpose Allocator)
|
||||||
|
├─ Primary header source (freed at end)
|
||||||
|
├─ Primary declarations (freed with deep free)
|
||||||
|
├─ DependencyResolver
|
||||||
|
│ ├─ referenced_types HashMap (keys owned)
|
||||||
|
│ └─ defined_types HashMap (keys borrowed)
|
||||||
|
├─ Missing types array (freed explicitly)
|
||||||
|
├─ Includes array (freed explicitly)
|
||||||
|
├─ Dependency declarations (freed with deep free)
|
||||||
|
└─ Generated output (freed after writing)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cleanup Pattern
|
||||||
|
|
||||||
|
```zig
|
||||||
|
defer {
|
||||||
|
for (decls) |decl| {
|
||||||
|
freeDeclDeep(allocator, decl);
|
||||||
|
}
|
||||||
|
allocator.free(decls);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
### Fatal Errors (Exit Immediately)
|
||||||
|
|
||||||
|
- File not found (primary header)
|
||||||
|
- Out of memory
|
||||||
|
- Cannot write output file
|
||||||
|
|
||||||
|
### Non-Fatal Errors (Continue with Warnings)
|
||||||
|
|
||||||
|
- Dependency header not readable → Skip, try next
|
||||||
|
- Type not found in any header → Print warning, continue
|
||||||
|
- Struct parsing error → Generate partial, continue
|
||||||
|
- Syntax errors in output → Print errors, write anyway
|
||||||
|
|
||||||
|
### Error Recovery
|
||||||
|
|
||||||
|
The parser uses graceful degradation:
|
||||||
|
1. Try to extract as much as possible
|
||||||
|
2. Warn about issues
|
||||||
|
3. Continue processing
|
||||||
|
4. Generate best-effort output
|
||||||
|
|
||||||
|
This allows partial success even with problematic headers.
|
||||||
|
|
||||||
|
## Extension Points
|
||||||
|
|
||||||
|
### Adding New Pattern Support
|
||||||
|
|
||||||
|
1. Add new variant to `Declaration` union in `patterns.zig`
|
||||||
|
2. Implement `scan*()` function to match pattern
|
||||||
|
3. Add to pattern matching chain in `Scanner.scan()`
|
||||||
|
4. Update all switch statements:
|
||||||
|
- Cleanup code in `parser.zig`
|
||||||
|
- `cloneDeclaration()` in `dependency_resolver.zig`
|
||||||
|
- `freeDeclaration()` in `dependency_resolver.zig`
|
||||||
|
5. Implement `write*()` in `codegen.zig`
|
||||||
|
|
||||||
|
### Adding Type Conversions
|
||||||
|
|
||||||
|
Edit `src/types.zig`:
|
||||||
|
```zig
|
||||||
|
pub fn convertType(c_type: []const u8, allocator: Allocator) ![]const u8 {
|
||||||
|
// Add new conversion here
|
||||||
|
if (std.mem.eql(u8, c_type, "MyType")) {
|
||||||
|
return try allocator.dupe(u8, "MyZigType");
|
||||||
|
}
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Adding Naming Rules
|
||||||
|
|
||||||
|
Edit `src/naming.zig`:
|
||||||
|
```zig
|
||||||
|
pub fn typeNameToZig(c_name: []const u8) []const u8 {
|
||||||
|
// Add custom naming logic
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Characteristics
|
||||||
|
|
||||||
|
### Time Complexity
|
||||||
|
|
||||||
|
- **Primary parsing**: O(n) where n = source lines
|
||||||
|
- **Dependency analysis**: O(d) where d = declarations
|
||||||
|
- **Type extraction**: O(h × d) where h = headers, d = declarations per header
|
||||||
|
- **Code generation**: O(d) where d = total declarations
|
||||||
|
|
||||||
|
**Overall**: O(n + h×d) - Linear for typical use
|
||||||
|
|
||||||
|
### Space Complexity
|
||||||
|
|
||||||
|
- **Declarations**: O(d) where d = declaration count
|
||||||
|
- **HashMaps**: O(t) where t = unique type names
|
||||||
|
- **Output**: O(d) where d = declaration count
|
||||||
|
|
||||||
|
**Peak memory**: ~2-5MB for SDL_gpu.h (169 declarations)
|
||||||
|
|
||||||
|
### Optimization Points
|
||||||
|
|
||||||
|
Current optimizations:
|
||||||
|
- HashMap-based deduplication
|
||||||
|
- Early exit when type found
|
||||||
|
- Selective parsing (only missing types)
|
||||||
|
- String interning for type names
|
||||||
|
|
||||||
|
Potential improvements:
|
||||||
|
- Cache parsed headers (avoid re-parsing)
|
||||||
|
- Parallel header processing
|
||||||
|
- Lazy header loading
|
||||||
|
|
||||||
|
## Testing Strategy
|
||||||
|
|
||||||
|
### Unit Tests (`test/`)
|
||||||
|
|
||||||
|
- Pattern matching tests (each C pattern)
|
||||||
|
- Type conversion tests
|
||||||
|
- Naming convention tests
|
||||||
|
- Dependency resolution tests
|
||||||
|
- Multi-field parsing tests
|
||||||
|
|
||||||
|
### Integration Tests
|
||||||
|
|
||||||
|
- Real SDL headers (SDL_gpu.h)
|
||||||
|
- Dependency chain resolution
|
||||||
|
- End-to-end parsing and generation
|
||||||
|
|
||||||
|
### Validation
|
||||||
|
|
||||||
|
- AST parsing of generated code
|
||||||
|
- Memory leak detection (GPA)
|
||||||
|
- No regressions (all tests must pass)
|
||||||
|
|
||||||
|
## Code Organization
|
||||||
|
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
├── parser.zig # Main entry point, CLI handling
|
||||||
|
├── patterns.zig # Pattern matching and scanning
|
||||||
|
├── types.zig # C to Zig type conversion
|
||||||
|
├── naming.zig # Naming convention handling
|
||||||
|
├── codegen.zig # Zig code generation
|
||||||
|
├── mock_codegen.zig # C mock generation
|
||||||
|
└── dependency_resolver.zig # Dependency analysis and extraction
|
||||||
|
|
||||||
|
test/
|
||||||
|
└── (various test files)
|
||||||
|
|
||||||
|
docs/
|
||||||
|
├── GETTING_STARTED.md # This file
|
||||||
|
├── ARCHITECTURE.md # Architecture overview
|
||||||
|
├── DEPENDENCY_RESOLUTION.md # Dependency system details
|
||||||
|
└── ...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
- Read [Dependency Resolution](DEPENDENCY_RESOLUTION.md) for details on automatic type extraction
|
||||||
|
- See [API Reference](API_REFERENCE.md) for all command-line options
|
||||||
|
- Check [Known Issues](KNOWN_ISSUES.md) for current limitations
|
||||||
|
- Review [Development](DEVELOPMENT.md) to contribute
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Related Documents**:
|
||||||
|
- Technical deep dive: [docs/DEPENDENCY_FLOW.md](DEPENDENCY_FLOW.md)
|
||||||
|
- Visual diagrams: [docs/VISUAL_FLOW.md](VISUAL_FLOW.md)
|
||||||
|
|
@ -0,0 +1,283 @@
|
||||||
|
# Dependency Resolution System
|
||||||
|
|
||||||
|
The parser automatically detects and resolves type dependencies from SDL headers.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
When parsing a header like SDL_gpu.h, functions often reference types defined in other headers (SDL_Window, SDL_Rect, etc.). The dependency resolver automatically finds and includes these types.
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
### Step 1: Detect Missing Types
|
||||||
|
|
||||||
|
After parsing the primary header, the system:
|
||||||
|
1. Scans all function signatures and struct fields
|
||||||
|
2. Extracts all referenced type names
|
||||||
|
3. Compares against types defined in the header
|
||||||
|
4. Identifies missing types
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```c
|
||||||
|
// SDL_gpu.h
|
||||||
|
extern void SDL_ClaimWindow(SDL_GPUDevice *device, SDL_Window *window);
|
||||||
|
```
|
||||||
|
|
||||||
|
- `SDL_GPUDevice` is defined in SDL_gpu.h ✓
|
||||||
|
- `SDL_Window` is NOT defined in SDL_gpu.h ✗
|
||||||
|
|
||||||
|
**Result**: SDL_Window added to missing types list
|
||||||
|
|
||||||
|
### Step 2: Parse Include Directives
|
||||||
|
|
||||||
|
Extracts `#include` directives from the header:
|
||||||
|
```c
|
||||||
|
#include <SDL3/SDL_stdinc.h>
|
||||||
|
#include <SDL3/SDL_video.h>
|
||||||
|
#include <SDL3/SDL_rect.h>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Result**: List of headers to search: [`SDL_stdinc.h`, `SDL_video.h`, `SDL_rect.h`]
|
||||||
|
|
||||||
|
### Step 3: Search for Missing Types
|
||||||
|
|
||||||
|
For each missing type:
|
||||||
|
1. Try each included header in order
|
||||||
|
2. Parse the header completely
|
||||||
|
3. Search for matching type definition
|
||||||
|
4. If found, clone the declaration and stop searching
|
||||||
|
5. If not found, continue to next header
|
||||||
|
|
||||||
|
**Example Search for SDL_Window**:
|
||||||
|
```
|
||||||
|
Try SDL_stdinc.h → Not found
|
||||||
|
Try SDL_video.h → Found! ✓
|
||||||
|
└─ Extract SDL_Window definition
|
||||||
|
└─ Stop searching
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 4: Combine Declarations
|
||||||
|
|
||||||
|
```zig
|
||||||
|
final_declarations = [
|
||||||
|
// Dependencies FIRST (so types are defined before use)
|
||||||
|
SDL_Window,
|
||||||
|
SDL_Rect,
|
||||||
|
SDL_FColor,
|
||||||
|
|
||||||
|
// Primary declarations
|
||||||
|
SDL_GPUDevice,
|
||||||
|
SDL_GPUTexture,
|
||||||
|
...
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 5: Generate Unified Output
|
||||||
|
|
||||||
|
```zig
|
||||||
|
pub const c = @import("c.zig").c;
|
||||||
|
|
||||||
|
// Dependencies (automatically included)
|
||||||
|
pub const Window = opaque {};
|
||||||
|
pub const Rect = extern struct { x: c_int, y: c_int, w: c_int, h: c_int };
|
||||||
|
|
||||||
|
// Primary declarations
|
||||||
|
pub const GPUDevice = opaque {
|
||||||
|
pub fn claimWindow(device: *GPUDevice, window: ?*Window) bool {
|
||||||
|
return c.SDL_ClaimWindowForGPUDevice(device, window);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Type Extraction Details
|
||||||
|
|
||||||
|
### Type String Normalization
|
||||||
|
|
||||||
|
C type strings often have pointer and const decorators that need to be stripped:
|
||||||
|
|
||||||
|
```
|
||||||
|
"SDL_Window *" → "SDL_Window"
|
||||||
|
"?*SDL_GPUDevice" → "SDL_GPUDevice"
|
||||||
|
"*const SDL_Rect" → "SDL_Rect"
|
||||||
|
"SDL_Buffer *const *" → "SDL_Buffer"
|
||||||
|
"[*c]const u8" → "u8"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Algorithm**:
|
||||||
|
1. Remove leading: `const`, `struct`, `?`, `*`
|
||||||
|
2. Handle C arrays: `[*c]T` → `T`
|
||||||
|
3. Remove trailing: `*`, `*const`, ` const`
|
||||||
|
4. Repeat until no changes
|
||||||
|
|
||||||
|
### SDL Type Detection
|
||||||
|
|
||||||
|
A type is considered "SDL" if:
|
||||||
|
- Name starts with `SDL_` prefix, OR
|
||||||
|
- Name is in known SDL types list (Window, Rect, etc.)
|
||||||
|
|
||||||
|
Non-SDL types (primitives) are ignored:
|
||||||
|
- `bool`, `int`, `float`, `void`, etc.
|
||||||
|
|
||||||
|
### Declaration Cloning
|
||||||
|
|
||||||
|
When extracting types from dependency headers, we must clone them because:
|
||||||
|
1. The temporary scanner will be freed
|
||||||
|
2. Original strings will be deallocated
|
||||||
|
3. We need owned copies with stable lifetime
|
||||||
|
|
||||||
|
**Cloning Process**:
|
||||||
|
```zig
|
||||||
|
fn cloneDeclaration(allocator: Allocator, decl: Declaration) !Declaration {
|
||||||
|
return switch (decl) {
|
||||||
|
.struct_decl => |s| .{
|
||||||
|
.struct_decl = .{
|
||||||
|
.name = try allocator.dupe(u8, s.name),
|
||||||
|
.fields = try cloneFields(allocator, s.fields),
|
||||||
|
.doc_comment = if (s.doc_comment) |doc|
|
||||||
|
try allocator.dupe(u8, doc) else null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// ... similar for other types
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
All strings are duplicated so the cloned declaration owns them.
|
||||||
|
|
||||||
|
## Success Metrics
|
||||||
|
|
||||||
|
### SDL_gpu.h Results
|
||||||
|
|
||||||
|
**Missing Types Detected**: 5
|
||||||
|
1. SDL_FColor
|
||||||
|
2. SDL_PropertiesID
|
||||||
|
3. SDL_Rect
|
||||||
|
4. SDL_Window
|
||||||
|
5. SDL_FlipMode
|
||||||
|
|
||||||
|
**Resolution Results**: 5/5 (100%) ✅
|
||||||
|
|
||||||
|
**Where Found**:
|
||||||
|
- SDL_FColor → SDL_pixels.h (struct)
|
||||||
|
- SDL_PropertiesID → SDL_properties.h (typedef)
|
||||||
|
- SDL_Rect → SDL_rect.h (struct)
|
||||||
|
- SDL_Window → SDL_video.h (opaque)
|
||||||
|
- SDL_FlipMode → SDL_surface.h (enum)
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Behavior
|
||||||
|
|
||||||
|
The dependency resolver is **always enabled** - no configuration needed.
|
||||||
|
|
||||||
|
When missing types are detected, it automatically:
|
||||||
|
- ✅ Searches included headers
|
||||||
|
- ✅ Extracts matching types
|
||||||
|
- ✅ Combines into output
|
||||||
|
- ✅ Reports progress
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
|
||||||
|
**Warnings** (non-fatal):
|
||||||
|
- Type not found in any header
|
||||||
|
- Dependency header not readable
|
||||||
|
- Parsing errors in dependency
|
||||||
|
|
||||||
|
**Result**: Partial output with warnings
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
### Timing Breakdown (SDL_gpu.h)
|
||||||
|
|
||||||
|
| Phase | Time | Notes |
|
||||||
|
|-------|------|-------|
|
||||||
|
| Primary parsing | 50ms | Parse SDL_gpu.h |
|
||||||
|
| Dependency analysis | 10ms | Build HashMaps |
|
||||||
|
| Include parsing | 1ms | Extract #includes |
|
||||||
|
| Type extraction | 300ms | Parse 5 dependency headers |
|
||||||
|
| Code generation | 150ms | Generate + validate |
|
||||||
|
| **Total** | **~520ms** | Acceptable |
|
||||||
|
|
||||||
|
### Optimization
|
||||||
|
|
||||||
|
**Current**:
|
||||||
|
- Selective parsing (only types needed)
|
||||||
|
- Early exit (stop when found)
|
||||||
|
- HashMap deduplication
|
||||||
|
|
||||||
|
**Future**:
|
||||||
|
- Cache parsed headers
|
||||||
|
- Parallel header parsing
|
||||||
|
- Header dependency graph
|
||||||
|
|
||||||
|
## Limitations
|
||||||
|
|
||||||
|
### Not Resolved
|
||||||
|
|
||||||
|
1. **Function pointer typedefs** - Not yet supported
|
||||||
|
```c
|
||||||
|
typedef void (*SDL_Callback)(void *userdata);
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **#define-based types** - Requires preprocessor
|
||||||
|
```c
|
||||||
|
#define SDL_VALUE (1u << 0)
|
||||||
|
typedef Uint32 SDL_Type; // Not found by scanner
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **External library types** - Expected
|
||||||
|
```c
|
||||||
|
SDL_EGLConfig // From EGL, not SDL
|
||||||
|
```
|
||||||
|
|
||||||
|
### Workarounds
|
||||||
|
|
||||||
|
**Manual Definitions**: Add missing types to a separate file
|
||||||
|
```zig
|
||||||
|
// manual_types.zig
|
||||||
|
pub const Callback = *const fn(?*anyopaque) void;
|
||||||
|
```
|
||||||
|
|
||||||
|
**Preprocessor**: Use clang to preprocess before parsing
|
||||||
|
```bash
|
||||||
|
clang -E -I/path/to/SDL3 header.h | zig build run --
|
||||||
|
```
|
||||||
|
|
||||||
|
## Debugging
|
||||||
|
|
||||||
|
### Enable Verbose Output
|
||||||
|
|
||||||
|
The parser already prints detailed progress:
|
||||||
|
```
|
||||||
|
Analyzing dependencies...
|
||||||
|
Found 5 missing types:
|
||||||
|
- SDL_FColor
|
||||||
|
- SDL_Rect
|
||||||
|
...
|
||||||
|
|
||||||
|
Resolving dependencies from included headers...
|
||||||
|
✓ Found SDL_FColor in SDL_pixels.h
|
||||||
|
✓ Found SDL_Rect in SDL_rect.h
|
||||||
|
⚠ Warning: Could not find definition for type: SDL_Unknown
|
||||||
|
```
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
**"Could not find definition for type"**
|
||||||
|
- Type might be typedef (check if recently added)
|
||||||
|
- Type might be in different include
|
||||||
|
- Type might be external (EGL, GL, etc.)
|
||||||
|
|
||||||
|
**"Syntax errors in generated code"**
|
||||||
|
- Check generated file line numbers
|
||||||
|
- Usually struct/enum parsing issues
|
||||||
|
- See [Known Issues](KNOWN_ISSUES.md)
|
||||||
|
|
||||||
|
## Technical Details
|
||||||
|
|
||||||
|
For implementation details, see:
|
||||||
|
- [Technical Flow](DEPENDENCY_FLOW.md) - Step-by-step walkthrough
|
||||||
|
- [Visual Guide](VISUAL_FLOW.md) - Diagrams and quick reference
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Next**: See [API Reference](API_REFERENCE.md) for command-line options.
|
||||||
|
|
@ -0,0 +1,634 @@
|
||||||
|
# Development Guide
|
||||||
|
|
||||||
|
Guide for contributing to and extending the SDL3 header parser.
|
||||||
|
|
||||||
|
## Quick Start for Developers
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clone and build
|
||||||
|
cd lib/sdl3/parser
|
||||||
|
zig build
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
zig build test
|
||||||
|
|
||||||
|
# Make changes
|
||||||
|
# ... edit src/*.zig ...
|
||||||
|
|
||||||
|
# Test your changes
|
||||||
|
zig build test
|
||||||
|
zig build run -- ../SDL/include/SDL3/SDL_gpu.h --output=test.zig
|
||||||
|
```
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
parser/
|
||||||
|
├── src/
|
||||||
|
│ ├── parser.zig # Main entry point, CLI
|
||||||
|
│ ├── patterns.zig # Pattern matching & scanning
|
||||||
|
│ ├── types.zig # C to Zig type conversion
|
||||||
|
│ ├── naming.zig # Naming conventions
|
||||||
|
│ ├── codegen.zig # Zig code generation
|
||||||
|
│ ├── mock_codegen.zig # C mock generation
|
||||||
|
│ └── dependency_resolver.zig # Dependency analysis
|
||||||
|
├── test/
|
||||||
|
│ └── (test files)
|
||||||
|
├── docs/
|
||||||
|
│ └── (documentation)
|
||||||
|
└── build.zig
|
||||||
|
```
|
||||||
|
|
||||||
|
## Zig 0.15 API Changes - CRITICAL
|
||||||
|
|
||||||
|
**This project uses Zig 0.15**. Key API changes from 0.14:
|
||||||
|
|
||||||
|
### ArrayList Changes
|
||||||
|
|
||||||
|
**Old (0.14)**:
|
||||||
|
```zig
|
||||||
|
var list = std.ArrayList(T).init(allocator);
|
||||||
|
defer list.deinit();
|
||||||
|
try list.append(item);
|
||||||
|
```
|
||||||
|
|
||||||
|
**New (0.15)** - REQUIRED:
|
||||||
|
```zig
|
||||||
|
var list = std.ArrayList(T){};
|
||||||
|
defer list.deinit(allocator);
|
||||||
|
try list.append(allocator, item);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Points**:
|
||||||
|
- Initialize with `{}` or `initCapacity()`
|
||||||
|
- All methods take allocator: `append(allocator, item)`
|
||||||
|
- Deinit takes allocator: `deinit(allocator)`
|
||||||
|
|
||||||
|
### AST Rendering
|
||||||
|
|
||||||
|
**Old**: `ast.render(allocator)`
|
||||||
|
**New**: `ast.renderAlloc(allocator)`
|
||||||
|
|
||||||
|
## Adding New Pattern Support
|
||||||
|
|
||||||
|
### Example: Adding Union Support
|
||||||
|
|
||||||
|
1. **Add to Declaration union** (patterns.zig):
|
||||||
|
```zig
|
||||||
|
pub const Declaration = union(enum) {
|
||||||
|
// ... existing variants
|
||||||
|
union_decl: UnionDecl, // NEW
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const UnionDecl = struct {
|
||||||
|
name: []const u8,
|
||||||
|
fields: []FieldDecl,
|
||||||
|
doc_comment: ?[]const u8,
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Add scanner function** (patterns.zig):
|
||||||
|
```zig
|
||||||
|
fn scanUnion(self: *Scanner) !?UnionDecl {
|
||||||
|
// Pattern matching logic
|
||||||
|
// Return UnionDecl or null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Add to scan chain** (patterns.zig):
|
||||||
|
```zig
|
||||||
|
if (try self.scanOpaque()) |opaque_decl| {
|
||||||
|
// ...
|
||||||
|
} else if (try self.scanUnion()) |union_decl| {
|
||||||
|
try decls.append(self.allocator, .{ .union_decl = union_decl });
|
||||||
|
} else if (try self.scanEnum()) |enum_decl| {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Update cleanup code** (parser.zig):
|
||||||
|
```zig
|
||||||
|
defer {
|
||||||
|
for (decls) |decl| {
|
||||||
|
switch (decl) {
|
||||||
|
// ... existing cases
|
||||||
|
.union_decl => |u| {
|
||||||
|
allocator.free(u.name);
|
||||||
|
if (u.doc_comment) |doc| allocator.free(doc);
|
||||||
|
for (u.fields) |field| {
|
||||||
|
allocator.free(field.name);
|
||||||
|
allocator.free(field.type_name);
|
||||||
|
if (field.comment) |c| allocator.free(c);
|
||||||
|
}
|
||||||
|
allocator.free(u.fields);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Update dependency resolver** (dependency_resolver.zig):
|
||||||
|
```zig
|
||||||
|
// In collectDefinedTypes:
|
||||||
|
.union_decl => |u| u.name,
|
||||||
|
|
||||||
|
// In cloneDeclaration:
|
||||||
|
.union_decl => |u| .{
|
||||||
|
.union_decl = .{
|
||||||
|
.name = try allocator.dupe(u8, u.name),
|
||||||
|
.fields = try cloneFields(allocator, u.fields),
|
||||||
|
.doc_comment = if (u.doc_comment) |doc|
|
||||||
|
try allocator.dupe(u8, doc) else null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// In freeDeclaration:
|
||||||
|
.union_decl => |u| {
|
||||||
|
allocator.free(u.name);
|
||||||
|
if (u.doc_comment) |doc| allocator.free(doc);
|
||||||
|
for (u.fields) |field| {
|
||||||
|
allocator.free(field.name);
|
||||||
|
allocator.free(field.type_name);
|
||||||
|
if (field.comment) |c| allocator.free(c);
|
||||||
|
}
|
||||||
|
allocator.free(u.fields);
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
6. **Add code generator** (codegen.zig):
|
||||||
|
```zig
|
||||||
|
fn writeUnion(self: *CodeGen, union_decl: patterns.UnionDecl) !void {
|
||||||
|
const zig_name = naming.typeNameToZig(union_decl.name);
|
||||||
|
|
||||||
|
if (union_decl.doc_comment) |doc| {
|
||||||
|
try self.writeDocComment(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
try self.output.writer(self.allocator).print(
|
||||||
|
"pub const {s} = extern union {{\n",
|
||||||
|
.{zig_name}
|
||||||
|
);
|
||||||
|
|
||||||
|
for (union_decl.fields) |field| {
|
||||||
|
const zig_type = try types.convertType(field.type_name, self.allocator);
|
||||||
|
defer self.allocator.free(zig_type);
|
||||||
|
|
||||||
|
try self.output.writer(self.allocator).print(
|
||||||
|
" {s}: {s},\n",
|
||||||
|
.{field.name, zig_type}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try self.output.appendSlice(self.allocator, "};\n\n");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
7. **Update writeDeclarations** (codegen.zig):
|
||||||
|
```zig
|
||||||
|
switch (decl) {
|
||||||
|
// ... existing cases
|
||||||
|
.union_decl => |union_decl| try self.writeUnion(union_decl),
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
8. **Add tests**:
|
||||||
|
```zig
|
||||||
|
test "parse union" {
|
||||||
|
const source =
|
||||||
|
\\typedef union SDL_Color {
|
||||||
|
\\ Uint32 rgba;
|
||||||
|
\\ struct { Uint8 r, g, b, a; };
|
||||||
|
\\} SDL_Color;
|
||||||
|
;
|
||||||
|
|
||||||
|
var scanner = patterns.Scanner.init(allocator, source);
|
||||||
|
const decls = try scanner.scan();
|
||||||
|
// ... test expectations
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing Guidelines
|
||||||
|
|
||||||
|
### Unit Tests
|
||||||
|
|
||||||
|
Place tests in `test/` or at bottom of source files:
|
||||||
|
|
||||||
|
```zig
|
||||||
|
test "descriptive test name" {
|
||||||
|
const allocator = std.testing.allocator;
|
||||||
|
|
||||||
|
// Setup
|
||||||
|
const source = "...";
|
||||||
|
var scanner = patterns.Scanner.init(allocator, source);
|
||||||
|
|
||||||
|
// Execute
|
||||||
|
const result = try scanner.scan();
|
||||||
|
defer allocator.free(result);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
try std.testing.expectEqual(expected, actual);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Integration Tests
|
||||||
|
|
||||||
|
Test with real SDL headers:
|
||||||
|
```bash
|
||||||
|
zig build run -- ../SDL/include/SDL3/SDL_gpu.h --output=test.zig
|
||||||
|
zig ast-check test.zig
|
||||||
|
```
|
||||||
|
|
||||||
|
### Memory Testing
|
||||||
|
|
||||||
|
Always run tests with GPA to detect leaks:
|
||||||
|
```zig
|
||||||
|
test "my test" {
|
||||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
defer _ = gpa.deinit();
|
||||||
|
const allocator = gpa.allocator();
|
||||||
|
|
||||||
|
// Test code using allocator
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code Style
|
||||||
|
|
||||||
|
### Naming
|
||||||
|
|
||||||
|
- **Functions**: camelCase (`parseStructField`)
|
||||||
|
- **Types**: PascalCase (`FieldDecl`)
|
||||||
|
- **Constants**: PascalCase (`Declaration`)
|
||||||
|
- **Variables**: camelCase (`decl_name`)
|
||||||
|
|
||||||
|
### Comments
|
||||||
|
|
||||||
|
Only comment code that needs clarification:
|
||||||
|
```zig
|
||||||
|
// Good: Explains WHY
|
||||||
|
// Must check flags before typedefs - pattern order matters
|
||||||
|
if (try self.scanFlagTypedef()) |flag_decl| { ... }
|
||||||
|
|
||||||
|
// Bad: Explains WHAT (obvious from code)
|
||||||
|
// Append to list
|
||||||
|
try list.append(allocator, item);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
|
||||||
|
Use graceful degradation:
|
||||||
|
```zig
|
||||||
|
// Good: Continue on error
|
||||||
|
const decl = extractType(source, name) catch |err| {
|
||||||
|
std.debug.print("Warning: {}\n", .{err});
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Bad: Fail immediately (unless truly fatal)
|
||||||
|
const decl = try extractType(source, name);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Memory Management Rules
|
||||||
|
|
||||||
|
### Ownership
|
||||||
|
|
||||||
|
1. **Scanner owns strings** during parsing
|
||||||
|
2. **Caller owns result** of scan()
|
||||||
|
3. **HashMap owns keys** when they're duped
|
||||||
|
4. **Cloned declarations own strings** after cloning
|
||||||
|
|
||||||
|
### Cleanup Pattern
|
||||||
|
|
||||||
|
Always use defer for cleanup:
|
||||||
|
```zig
|
||||||
|
const decls = try scanner.scan();
|
||||||
|
defer {
|
||||||
|
for (decls) |decl| {
|
||||||
|
freeDeclDeep(allocator, decl);
|
||||||
|
}
|
||||||
|
allocator.free(decls);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### HashMap Keys
|
||||||
|
|
||||||
|
Must be owned (not slices into temporary data):
|
||||||
|
```zig
|
||||||
|
// Wrong:
|
||||||
|
try map.put(type_str, {}); // type_str might be freed!
|
||||||
|
|
||||||
|
// Right:
|
||||||
|
if (!map.contains(type_str)) {
|
||||||
|
const owned = try allocator.dupe(u8, type_str);
|
||||||
|
try map.put(owned, {});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Patterns
|
||||||
|
|
||||||
|
### Pattern Matching
|
||||||
|
|
||||||
|
```zig
|
||||||
|
fn scanSomething(self: *Scanner) !?SomeDecl {
|
||||||
|
const start = self.pos;
|
||||||
|
const line = try self.readLine();
|
||||||
|
defer self.allocator.free(line);
|
||||||
|
|
||||||
|
// Check pattern
|
||||||
|
if (!std.mem.startsWith(u8, line, "expected_start")) {
|
||||||
|
self.pos = start; // Reset position
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse and return
|
||||||
|
return SomeDecl{ ... };
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### String Building
|
||||||
|
|
||||||
|
```zig
|
||||||
|
var buf = std.ArrayList(u8){};
|
||||||
|
defer buf.deinit(allocator);
|
||||||
|
|
||||||
|
try buf.appendSlice(allocator, "pub const ");
|
||||||
|
try buf.appendSlice(allocator, name);
|
||||||
|
try buf.appendSlice(allocator, " = ");
|
||||||
|
|
||||||
|
return try buf.toOwnedSlice(allocator);
|
||||||
|
```
|
||||||
|
|
||||||
|
### HashMap Usage
|
||||||
|
|
||||||
|
```zig
|
||||||
|
var map = std.StringHashMap(void).init(allocator);
|
||||||
|
defer {
|
||||||
|
var it = map.keyIterator();
|
||||||
|
while (it.next()) |key| {
|
||||||
|
allocator.free(key.*); // Free owned keys
|
||||||
|
}
|
||||||
|
map.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add items
|
||||||
|
const owned_key = try allocator.dupe(u8, key);
|
||||||
|
try map.put(owned_key, {});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Debugging Tips
|
||||||
|
|
||||||
|
### Print Debugging
|
||||||
|
|
||||||
|
```zig
|
||||||
|
std.debug.print("Debug: value = {s}\n", .{value});
|
||||||
|
std.debug.print("Type: {}\n", .{@TypeOf(variable)});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Memory Leak Detection
|
||||||
|
|
||||||
|
Run with GPA and check output:
|
||||||
|
```bash
|
||||||
|
zig build run -- header.h 2>&1 | grep "memory address"
|
||||||
|
```
|
||||||
|
|
||||||
|
### AST Debugging
|
||||||
|
|
||||||
|
Check what Zig thinks is wrong:
|
||||||
|
```bash
|
||||||
|
zig ast-check generated.zig
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Optimization
|
||||||
|
|
||||||
|
### Guidelines
|
||||||
|
|
||||||
|
1. **Avoid allocations in hot paths** - Use stack when possible
|
||||||
|
2. **Reuse buffers** - Clear and reuse instead of allocating new
|
||||||
|
3. **Early exit** - Return as soon as answer is known
|
||||||
|
4. **HashMap for lookups** - O(1) instead of O(n) searches
|
||||||
|
|
||||||
|
### Profiling
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build with profiling
|
||||||
|
zig build -Drelease-safe
|
||||||
|
|
||||||
|
# Run with timing
|
||||||
|
time zig build run -- large_header.h --output=out.zig
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contributing Workflow
|
||||||
|
|
||||||
|
1. **Create branch** from `dev/sdl3-parser`
|
||||||
|
2. **Make changes** in focused commits
|
||||||
|
3. **Run tests** - All must pass
|
||||||
|
4. **Update docs** if behavior changes
|
||||||
|
5. **Commit** with descriptive message
|
||||||
|
6. **Push** and create PR
|
||||||
|
|
||||||
|
### Commit Message Format
|
||||||
|
|
||||||
|
```
|
||||||
|
feat: Add union type support
|
||||||
|
|
||||||
|
Implements parsing and code generation for C union types.
|
||||||
|
|
||||||
|
- Added UnionDecl to Declaration union
|
||||||
|
- Implemented scanUnion() pattern matcher
|
||||||
|
- Added writeUnion() code generator
|
||||||
|
- Created comprehensive test suite (5 tests)
|
||||||
|
|
||||||
|
Results:
|
||||||
|
- Successfully parses SDL union types
|
||||||
|
- Generates proper extern unions
|
||||||
|
- All tests passing
|
||||||
|
|
||||||
|
Closes: #123
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test-Driven Development
|
||||||
|
|
||||||
|
Recommended workflow:
|
||||||
|
|
||||||
|
1. **Write test first**:
|
||||||
|
```zig
|
||||||
|
test "parse union type" {
|
||||||
|
const source = "typedef union { int x; float y; } SDL_Union;";
|
||||||
|
var scanner = patterns.Scanner.init(allocator, source);
|
||||||
|
const decls = try scanner.scan();
|
||||||
|
|
||||||
|
try testing.expectEqual(@as(usize, 1), decls.len);
|
||||||
|
try testing.expect(decls[0] == .union_decl);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Run test** (it will fail)
|
||||||
|
3. **Implement feature** until test passes
|
||||||
|
4. **Add more tests** for edge cases
|
||||||
|
5. **Refactor** if needed
|
||||||
|
|
||||||
|
## Architecture Decisions
|
||||||
|
|
||||||
|
### Why Single-File Output?
|
||||||
|
|
||||||
|
**Alternative**: Generate separate file per type
|
||||||
|
|
||||||
|
**Decision**: Single file with dependencies first
|
||||||
|
|
||||||
|
**Reasons**:
|
||||||
|
- Simpler for users (one import)
|
||||||
|
- Zig's structural typing handles it
|
||||||
|
- Type ordering guaranteed
|
||||||
|
- Less build system complexity
|
||||||
|
|
||||||
|
### Why On-Demand Resolution?
|
||||||
|
|
||||||
|
**Alternative**: Always parse all includes
|
||||||
|
|
||||||
|
**Decision**: Only parse when missing types detected
|
||||||
|
|
||||||
|
**Reasons**:
|
||||||
|
- Better performance
|
||||||
|
- Minimal overhead for self-contained headers
|
||||||
|
- Users see only relevant dependencies
|
||||||
|
|
||||||
|
### Why Conservative Error Handling?
|
||||||
|
|
||||||
|
**Alternative**: Fail on any error
|
||||||
|
|
||||||
|
**Decision**: Warn and continue
|
||||||
|
|
||||||
|
**Reasons**:
|
||||||
|
- Partial success is better than no success
|
||||||
|
- Users can manually fix issues
|
||||||
|
- Allows incremental improvement
|
||||||
|
|
||||||
|
## Extending the Parser
|
||||||
|
|
||||||
|
### Adding Type Conversions
|
||||||
|
|
||||||
|
Edit `src/types.zig`:
|
||||||
|
```zig
|
||||||
|
pub fn convertType(c_type: []const u8, allocator: Allocator) ![]const u8 {
|
||||||
|
// Check for your pattern first
|
||||||
|
if (std.mem.eql(u8, c_type, "MyCustomType")) {
|
||||||
|
return try allocator.dupe(u8, "MyZigType");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall through to existing logic
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Adding Naming Rules
|
||||||
|
|
||||||
|
Edit `src/naming.zig`:
|
||||||
|
```zig
|
||||||
|
pub fn typeNameToZig(c_name: []const u8) []const u8 {
|
||||||
|
// Handle special cases
|
||||||
|
if (std.mem.eql(u8, c_name, "SDL_bool")) {
|
||||||
|
return "Bool"; // Custom mapping
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default logic
|
||||||
|
return stripSDLPrefix(c_name);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Adding Pattern Matchers
|
||||||
|
|
||||||
|
1. Implement `scan*()` function in `patterns.zig`
|
||||||
|
2. Add to scan chain with proper ordering
|
||||||
|
3. Update all switch statements
|
||||||
|
4. Add code generator
|
||||||
|
5. Write tests
|
||||||
|
|
||||||
|
See "Adding New Pattern Support" section above for full example.
|
||||||
|
|
||||||
|
## Common Issues When Developing
|
||||||
|
|
||||||
|
### Issue: ArrayList API Changed
|
||||||
|
|
||||||
|
**Error**: `error: no field named 'init' in struct 'ArrayList'`
|
||||||
|
|
||||||
|
**Solution**: Use Zig 0.15 API (see above)
|
||||||
|
|
||||||
|
### Issue: HashMap Key Lifetime
|
||||||
|
|
||||||
|
**Error**: Memory corruption or use-after-free
|
||||||
|
|
||||||
|
**Solution**: Always dupe keys before inserting:
|
||||||
|
```zig
|
||||||
|
const owned = try allocator.dupe(u8, key);
|
||||||
|
try map.put(owned, {});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Issue: Pattern Matching Order
|
||||||
|
|
||||||
|
**Error**: Wrong scanner function matches
|
||||||
|
|
||||||
|
**Solution**: Order matters! More specific patterns first:
|
||||||
|
```zig
|
||||||
|
// Correct order:
|
||||||
|
if (try self.scanFlagTypedef()) { ... } // Specific
|
||||||
|
else if (try self.scanTypedef()) { ... } // General
|
||||||
|
|
||||||
|
// Wrong order:
|
||||||
|
if (try self.scanTypedef()) { ... } // Too general - catches flags!
|
||||||
|
else if (try self.scanFlagTypedef()) { ... } // Never reached
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Considerations
|
||||||
|
|
||||||
|
### Current Performance
|
||||||
|
|
||||||
|
- Small headers: ~100ms
|
||||||
|
- Large headers (SDL_gpu.h): ~520ms
|
||||||
|
- Memory: ~2-5MB peak
|
||||||
|
|
||||||
|
### Bottlenecks
|
||||||
|
|
||||||
|
1. **Dependency extraction**: 300ms (58% of time)
|
||||||
|
- Parsing multiple headers
|
||||||
|
- Could cache parsed headers
|
||||||
|
|
||||||
|
2. **String allocations**: Many small allocations
|
||||||
|
- Could use arena allocator
|
||||||
|
- String interning would help
|
||||||
|
|
||||||
|
### Optimization Ideas
|
||||||
|
|
||||||
|
```zig
|
||||||
|
// Cache parsed headers
|
||||||
|
var header_cache = std.StringHashMap([]Declaration).init(allocator);
|
||||||
|
|
||||||
|
// Use arena for temporary allocations
|
||||||
|
var arena = std.heap.ArenaAllocator.init(allocator);
|
||||||
|
defer arena.deinit();
|
||||||
|
const temp_alloc = arena.allocator();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
### Zig Documentation
|
||||||
|
- [Zig Language Reference](https://ziglang.org/documentation/master/)
|
||||||
|
- [Zig Standard Library](https://ziglang.org/documentation/master/std/)
|
||||||
|
|
||||||
|
### SDL Documentation
|
||||||
|
- [SDL3 API](https://wiki.libsdl.org/SDL3/)
|
||||||
|
- [SDL3 Headers](https://github.com/libsdl-org/SDL)
|
||||||
|
|
||||||
|
### Project Documentation
|
||||||
|
- [Architecture](ARCHITECTURE.md) - How the parser works
|
||||||
|
- [Dependency Flow](DEPENDENCY_FLOW.md) - Detailed flow
|
||||||
|
- [Visual Flow](VISUAL_FLOW.md) - Diagrams
|
||||||
|
|
||||||
|
## Getting Help
|
||||||
|
|
||||||
|
- Check existing tests for examples
|
||||||
|
- Read [Architecture](ARCHITECTURE.md) for design
|
||||||
|
- See [Dependency Flow](DEPENDENCY_FLOW.md) for details
|
||||||
|
- Review git history for patterns
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Ready to contribute?** Start with the tests, understand the existing patterns, then extend!
|
||||||
|
|
@ -0,0 +1,278 @@
|
||||||
|
# Getting Started with SDL3 Parser
|
||||||
|
|
||||||
|
This guide will help you get up and running with the SDL3 header parser.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Zig 0.15 or later
|
||||||
|
- SDL3 headers (included in `../SDL/include/SDL3/`)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Navigate to the parser directory:
|
||||||
|
```bash
|
||||||
|
cd lib/sdl3/parser
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Build the parser:
|
||||||
|
```bash
|
||||||
|
zig build
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Run tests to verify installation:
|
||||||
|
```bash
|
||||||
|
zig build test
|
||||||
|
```
|
||||||
|
|
||||||
|
You should see: `All tests passed.`
|
||||||
|
|
||||||
|
## Your First Parse
|
||||||
|
|
||||||
|
### Step 1: Parse SDL_gpu.h
|
||||||
|
|
||||||
|
```bash
|
||||||
|
zig build run -- ../SDL/include/SDL3/SDL_gpu.h --output=my_gpu.zig
|
||||||
|
```
|
||||||
|
|
||||||
|
You'll see output like:
|
||||||
|
```
|
||||||
|
SDL3 Header Parser
|
||||||
|
==================
|
||||||
|
|
||||||
|
Parsing: ../SDL/include/SDL3/SDL_gpu.h
|
||||||
|
|
||||||
|
Found 169 declarations
|
||||||
|
- Opaque types: 13
|
||||||
|
- Typedefs: 6
|
||||||
|
- Enums: 24
|
||||||
|
- Structs: 35
|
||||||
|
- Flags: 3
|
||||||
|
- Functions: 94
|
||||||
|
|
||||||
|
Analyzing dependencies...
|
||||||
|
Found 5 missing types:
|
||||||
|
- SDL_FColor
|
||||||
|
- SDL_PropertiesID
|
||||||
|
- SDL_Rect
|
||||||
|
- SDL_Window
|
||||||
|
- SDL_FlipMode
|
||||||
|
|
||||||
|
Resolving dependencies from included headers...
|
||||||
|
✓ Found SDL_FColor in SDL_pixels.h
|
||||||
|
✓ Found SDL_PropertiesID in SDL_properties.h
|
||||||
|
✓ Found SDL_Rect in SDL_rect.h
|
||||||
|
✓ Found SDL_Window in SDL_video.h
|
||||||
|
✓ Found SDL_FlipMode in SDL_surface.h
|
||||||
|
|
||||||
|
Combining 5 dependency declarations with primary declarations...
|
||||||
|
Generated: my_gpu.zig
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Examine the Output
|
||||||
|
|
||||||
|
```bash
|
||||||
|
head -50 my_gpu.zig
|
||||||
|
```
|
||||||
|
|
||||||
|
You'll see clean Zig bindings:
|
||||||
|
```zig
|
||||||
|
pub const c = @import("c.zig").c;
|
||||||
|
|
||||||
|
// Dependencies (automatically included)
|
||||||
|
pub const FColor = extern struct {
|
||||||
|
r: f32,
|
||||||
|
g: f32,
|
||||||
|
b: f32,
|
||||||
|
a: f32,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const PropertiesID = u32;
|
||||||
|
|
||||||
|
pub const Rect = extern struct {
|
||||||
|
x: c_int,
|
||||||
|
y: c_int,
|
||||||
|
w: c_int,
|
||||||
|
h: c_int,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Window = opaque {};
|
||||||
|
|
||||||
|
// Primary declarations
|
||||||
|
pub const GPUDevice = opaque {
|
||||||
|
pub inline fn destroyGPUDevice(gpudevice: *GPUDevice) void {
|
||||||
|
return c.SDL_DestroyGPUDevice(gpudevice);
|
||||||
|
}
|
||||||
|
// ... 93 more methods
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Create c.zig Wrapper
|
||||||
|
|
||||||
|
Create a file `c.zig` that imports SDL:
|
||||||
|
```zig
|
||||||
|
pub const c = @cImport({
|
||||||
|
@cInclude("SDL3/SDL.h");
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 4: Use in Your Project
|
||||||
|
|
||||||
|
```zig
|
||||||
|
const std = @import("std");
|
||||||
|
const gpu = @import("my_gpu.zig");
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
const device = gpu.createGPUDevice(true);
|
||||||
|
if (device) |d| {
|
||||||
|
defer d.destroyGPUDevice();
|
||||||
|
|
||||||
|
const driver = d.getGPUDeviceDriver();
|
||||||
|
std.debug.print("GPU Driver: {s}\n", .{driver});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Command-Line Options
|
||||||
|
|
||||||
|
### Basic Options
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Output to file
|
||||||
|
zig build run -- header.h --output=output.zig
|
||||||
|
|
||||||
|
# Output to stdout
|
||||||
|
zig build run -- header.h
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mock Generation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate C mocks for testing
|
||||||
|
zig build run -- header.h --output=bindings.zig --mocks=mocks.c
|
||||||
|
```
|
||||||
|
|
||||||
|
The mock file contains stub implementations that return zero/null:
|
||||||
|
```c
|
||||||
|
void SDL_DestroyGPUDevice(SDL_GPUDevice *device) {
|
||||||
|
// Mock implementation
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Understanding the Output
|
||||||
|
|
||||||
|
### Type Name Conversion
|
||||||
|
|
||||||
|
The parser follows consistent naming rules:
|
||||||
|
|
||||||
|
| C Name | Zig Name | Rule |
|
||||||
|
|--------|----------|------|
|
||||||
|
| `SDL_GPUDevice` | `GPUDevice` | Strip `SDL_` prefix |
|
||||||
|
| `SDL_GPU_PRIMITIVE_TYPE_TRIANGLELIST` | `primitiveTypeTrianglelist` | Strip prefix, camelCase |
|
||||||
|
| `SDL_CreateGPUDevice` | `createGPUDevice` | Strip `SDL_`, camelCase |
|
||||||
|
|
||||||
|
### Method Grouping
|
||||||
|
|
||||||
|
Functions are organized as methods when possible:
|
||||||
|
|
||||||
|
**C API**:
|
||||||
|
```c
|
||||||
|
void SDL_DestroyGPUDevice(SDL_GPUDevice *device);
|
||||||
|
const char* SDL_GetGPUDeviceDriver(SDL_GPUDevice *device);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Generated Zig**:
|
||||||
|
```zig
|
||||||
|
pub const GPUDevice = opaque {
|
||||||
|
pub inline fn destroyGPUDevice(self: *GPUDevice) void { ... }
|
||||||
|
pub inline fn getGPUDeviceDriver(self: *GPUDevice) [*c]const u8 { ... }
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage becomes:
|
||||||
|
```zig
|
||||||
|
device.destroyGPUDevice(); // Instead of SDL_DestroyGPUDevice(device)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dependency Inclusion
|
||||||
|
|
||||||
|
Dependencies are automatically detected and included at the top of the file:
|
||||||
|
|
||||||
|
```zig
|
||||||
|
// Dependencies from included headers
|
||||||
|
pub const FColor = extern struct { ... };
|
||||||
|
pub const Rect = extern struct { ... };
|
||||||
|
pub const Window = opaque {};
|
||||||
|
|
||||||
|
// Primary declarations from SDL_gpu.h
|
||||||
|
pub const GPUDevice = opaque { ... };
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Workflows
|
||||||
|
|
||||||
|
### Generate Bindings for a Module
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# From lib/sdl3 directory
|
||||||
|
zig build regenerate-zig
|
||||||
|
```
|
||||||
|
|
||||||
|
This generates bindings for configured headers in `v2/` directory.
|
||||||
|
|
||||||
|
### Test Your Changes
|
||||||
|
|
||||||
|
After modifying the parser:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run unit tests
|
||||||
|
zig build test
|
||||||
|
|
||||||
|
# Test with a real header
|
||||||
|
zig build run -- ../SDL/include/SDL3/SDL_gpu.h --output=test.zig
|
||||||
|
|
||||||
|
# Verify output compiles
|
||||||
|
zig ast-check test.zig
|
||||||
|
```
|
||||||
|
|
||||||
|
### Debug Issues
|
||||||
|
|
||||||
|
If you encounter errors:
|
||||||
|
|
||||||
|
1. Check the console output for warnings about missing types
|
||||||
|
2. Look at the generated file for syntax errors
|
||||||
|
3. See [Known Issues](docs/KNOWN_ISSUES.md) for common problems
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
- Read [Architecture](docs/ARCHITECTURE.md) to understand how it works
|
||||||
|
- See [Dependency Resolution](docs/DEPENDENCY_RESOLUTION.md) for details on automatic type extraction
|
||||||
|
- Check [Known Issues](docs/KNOWN_ISSUES.md) for current limitations
|
||||||
|
- Review [Development](docs/DEVELOPMENT.md) to contribute
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build
|
||||||
|
zig build
|
||||||
|
|
||||||
|
# Test
|
||||||
|
zig build test
|
||||||
|
|
||||||
|
# Generate bindings
|
||||||
|
zig build run -- <header> --output=<output>
|
||||||
|
|
||||||
|
# Generate with mocks
|
||||||
|
zig build run -- <header> --output=<output> --mocks=<mocks>
|
||||||
|
|
||||||
|
# Generate all configured headers
|
||||||
|
cd .. && zig build regenerate-zig
|
||||||
|
```
|
||||||
|
|
||||||
|
## Getting Help
|
||||||
|
|
||||||
|
- **Documentation**: See `docs/` directory
|
||||||
|
- **Examples**: Check `test/` directory for usage examples
|
||||||
|
- **Issues**: See `docs/KNOWN_ISSUES.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Next**: Read [Architecture](docs/ARCHITECTURE.md) to understand the parser internals.
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
# SDL3 Parser Documentation
|
||||||
|
|
||||||
|
Complete documentation for the SDL3 C header to Zig bindings generator.
|
||||||
|
|
||||||
|
## Quick Links
|
||||||
|
|
||||||
|
- **[README](../README.md)** - Start here for project overview
|
||||||
|
- **[Getting Started](GETTING_STARTED.md)** - Installation and first use
|
||||||
|
- **[API Reference](API_REFERENCE.md)** - Command-line options
|
||||||
|
|
||||||
|
## User Guides
|
||||||
|
|
||||||
|
### Essential
|
||||||
|
|
||||||
|
1. **[Getting Started](GETTING_STARTED.md)** - Installation, first parse, basic usage
|
||||||
|
2. **[Quickstart Guide](QUICKSTART.md)** - Quick reference for common tasks
|
||||||
|
3. **[API Reference](API_REFERENCE.md)** - Complete command-line reference
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
4. **[Dependency Resolution](DEPENDENCY_RESOLUTION.md)** - How automatic type extraction works
|
||||||
|
5. **[Known Issues](KNOWN_ISSUES.md)** - Current limitations and workarounds
|
||||||
|
|
||||||
|
## Technical Documentation
|
||||||
|
|
||||||
|
### Architecture
|
||||||
|
|
||||||
|
6. **[Architecture Overview](ARCHITECTURE.md)** - System design and components
|
||||||
|
7. **[Dependency Flow](DEPENDENCY_FLOW.md)** - Complete technical walkthrough (845 lines)
|
||||||
|
8. **[Visual Flow Diagrams](VISUAL_FLOW.md)** - Quick reference diagrams
|
||||||
|
|
||||||
|
### Implementation Details
|
||||||
|
|
||||||
|
9. **[Multi-Field Structs](MULTI_FIELD_IMPLEMENTATION.md)** - How `int x, y;` parsing works
|
||||||
|
10. **[Typedef Support](TYPEDEF_IMPLEMENTATION.md)** - Simple typedef implementation
|
||||||
|
11. **[Multi-Header Testing](MULTI_HEADER_TEST_RESULTS.md)** - Test results across SDL headers
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
12. **[Development Guide](DEVELOPMENT.md)** - Contributing, extending, Zig 0.15 guidelines
|
||||||
|
13. **[Roadmap](ROADMAP.md)** - Future plans and priorities
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install
|
||||||
|
cd parser/
|
||||||
|
zig build
|
||||||
|
zig build test
|
||||||
|
|
||||||
|
# Generate bindings
|
||||||
|
zig build run -- ../SDL/include/SDL3/SDL_gpu.h --output=gpu.zig
|
||||||
|
|
||||||
|
# Use in code
|
||||||
|
const gpu = @import("gpu.zig");
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation by Use Case
|
||||||
|
|
||||||
|
### "I want to generate Zig bindings"
|
||||||
|
→ Start with [Getting Started](GETTING_STARTED.md)
|
||||||
|
|
||||||
|
### "I want to understand how it works"
|
||||||
|
→ Read [Architecture](ARCHITECTURE.md)
|
||||||
|
|
||||||
|
### "I'm hitting an error"
|
||||||
|
→ Check [Known Issues](KNOWN_ISSUES.md)
|
||||||
|
|
||||||
|
### "I want to extend the parser"
|
||||||
|
→ See [Development Guide](DEVELOPMENT.md)
|
||||||
|
|
||||||
|
### "I need technical details"
|
||||||
|
→ Deep dive: [Dependency Flow](DEPENDENCY_FLOW.md)
|
||||||
|
|
||||||
|
## Project Status
|
||||||
|
|
||||||
|
**Version**: 2.1
|
||||||
|
**Status**: Production ready for SDL_gpu.h
|
||||||
|
**Last Updated**: 2026-01-22
|
||||||
|
|
||||||
|
### Supported Headers
|
||||||
|
|
||||||
|
| Header | Status | Notes |
|
||||||
|
|--------|--------|-------|
|
||||||
|
| SDL_gpu.h | ✅ Complete | 100% dependency resolution |
|
||||||
|
| SDL_keyboard.h | ⚠️ Partial | Large enum issues |
|
||||||
|
| SDL_video.h | ⚠️ Partial | Some types not found |
|
||||||
|
| SDL_events.h | ⚠️ Partial | Parse errors |
|
||||||
|
|
||||||
|
See [Known Issues](KNOWN_ISSUES.md) for details.
|
||||||
|
|
||||||
|
## Key Features
|
||||||
|
|
||||||
|
✅ Automatic dependency resolution (100% for SDL_gpu.h)
|
||||||
|
✅ Multi-field struct parsing (`int x, y;`)
|
||||||
|
✅ Typedef support (`typedef Uint32 SDL_Type;`)
|
||||||
|
✅ Method organization (functions → methods)
|
||||||
|
✅ Mock generation for testing
|
||||||
|
✅ Comprehensive error reporting
|
||||||
|
|
||||||
|
## Statistics
|
||||||
|
|
||||||
|
- **Code**: ~900 lines (production)
|
||||||
|
- **Tests**: 26+ unit tests (100% passing)
|
||||||
|
- **Documentation**: 5,500+ lines
|
||||||
|
- **Success Rate**: 100% for SDL_gpu.h
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Archive
|
||||||
|
|
||||||
|
Historical planning and session documents are in `archive/` for reference.
|
||||||
|
|
@ -0,0 +1,340 @@
|
||||||
|
# Known Issues and Limitations
|
||||||
|
|
||||||
|
This document lists current limitations of the SDL3 header parser.
|
||||||
|
|
||||||
|
## Production Ready ✅
|
||||||
|
|
||||||
|
### SDL_gpu.h
|
||||||
|
- **Status**: 100% working
|
||||||
|
- **Dependencies**: All resolved automatically
|
||||||
|
- **Output**: Production-ready Zig bindings
|
||||||
|
- **Issue**: 1 minor (field name `type` shadows keyword)
|
||||||
|
|
||||||
|
## Known Limitations
|
||||||
|
|
||||||
|
### 1. Field Names That Shadow Zig Keywords
|
||||||
|
|
||||||
|
**Issue**: Fields named `type`, `error`, `if`, etc. cause compilation errors
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```c
|
||||||
|
typedef struct {
|
||||||
|
int type; // Shadows Zig keyword
|
||||||
|
} SDL_Something;
|
||||||
|
```
|
||||||
|
|
||||||
|
**Error**:
|
||||||
|
```
|
||||||
|
error: name shadows primitive 'type'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Workaround**: Manual edit
|
||||||
|
```zig
|
||||||
|
// Change:
|
||||||
|
type: GPUTextureType,
|
||||||
|
|
||||||
|
// To:
|
||||||
|
@"type": GPUTextureType,
|
||||||
|
```
|
||||||
|
|
||||||
|
**Priority**: Low
|
||||||
|
**Effort**: ~30 minutes to auto-escape
|
||||||
|
**Frequency**: Rare (a few SDL structs)
|
||||||
|
|
||||||
|
### 2. Large Enum Parsing
|
||||||
|
|
||||||
|
**Issue**: Enums with 300+ values generate syntax errors
|
||||||
|
|
||||||
|
**Affected**:
|
||||||
|
- SDL_Scancode (300+ keyboard scancodes)
|
||||||
|
- SDL_Keycode (300+ key codes)
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```c
|
||||||
|
typedef enum {
|
||||||
|
SDL_SCANCODE_A = 4,
|
||||||
|
SDL_SCANCODE_B = 5,
|
||||||
|
// ... 300 more values
|
||||||
|
} SDL_Scancode;
|
||||||
|
```
|
||||||
|
|
||||||
|
**Error**: 77+ syntax errors in generated enum
|
||||||
|
|
||||||
|
**Root Cause**: Special enum value expressions not fully supported
|
||||||
|
|
||||||
|
**Workaround**: Manual enum definition or use C directly
|
||||||
|
|
||||||
|
**Priority**: High (blocks SDL_keyboard.h)
|
||||||
|
**Effort**: ~1-2 hours
|
||||||
|
**Status**: Documented in MULTI_HEADER_TEST_RESULTS.md
|
||||||
|
|
||||||
|
### 3. Function Pointer Typedefs
|
||||||
|
|
||||||
|
**Issue**: Function pointer types not parsed
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```c
|
||||||
|
typedef void (*SDL_HitTest)(SDL_Window *window, const SDL_Point *pt, void *data);
|
||||||
|
typedef int (*SDL_EventFilter)(void *userdata, SDL_Event *event);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Impact**: Callback types not auto-resolved
|
||||||
|
|
||||||
|
**Workaround**: Manual definition
|
||||||
|
```zig
|
||||||
|
pub const HitTest = *const fn(?*Window, *const Point, ?*anyopaque) callconv(.C) void;
|
||||||
|
```
|
||||||
|
|
||||||
|
**Priority**: Medium
|
||||||
|
**Effort**: ~2-3 hours
|
||||||
|
**Frequency**: Uncommon in SDL public API
|
||||||
|
|
||||||
|
### 4. SDL_UINT64_C Macro in Bit Positions
|
||||||
|
|
||||||
|
**Issue**: Some 64-bit flag patterns may not parse correctly
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```c
|
||||||
|
#define SDL_WINDOW_FULLSCREEN SDL_UINT64_C(0x0000000000000001)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Status**: Enhanced support added, but not fully tested
|
||||||
|
|
||||||
|
**Workaround**: Manual flag definitions if needed
|
||||||
|
|
||||||
|
**Priority**: Medium
|
||||||
|
**Effort**: ~30 minutes validation
|
||||||
|
**Affected**: SDL_video.h WindowFlags
|
||||||
|
|
||||||
|
### 5. External Library Types
|
||||||
|
|
||||||
|
**Issue**: Types from external libraries (EGL, OpenGL) not found
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```c
|
||||||
|
SDL_EGLConfig
|
||||||
|
SDL_EGLDisplay
|
||||||
|
SDL_GLContext
|
||||||
|
```
|
||||||
|
|
||||||
|
**Status**: Expected behavior (not SDL types)
|
||||||
|
|
||||||
|
**Workaround**: Use C imports or manual definitions
|
||||||
|
|
||||||
|
**Priority**: N/A (expected)
|
||||||
|
|
||||||
|
### 6. Memory Leaks in Comment Handling
|
||||||
|
|
||||||
|
**Issue**: Small memory leaks (4-8 allocations per run) in struct comment parsing
|
||||||
|
|
||||||
|
**Impact**: ~1-2KB leaked per parse
|
||||||
|
|
||||||
|
**Status**: Functional but should be fixed
|
||||||
|
|
||||||
|
**Priority**: Low
|
||||||
|
**Effort**: ~30 minutes
|
||||||
|
|
||||||
|
### 7. Array Field Declarations
|
||||||
|
|
||||||
|
**Issue**: Array fields in multi-field syntax not supported
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```c
|
||||||
|
int array1[10], array2[20]; // Not handled
|
||||||
|
```
|
||||||
|
|
||||||
|
**Workaround**: Rare in SDL, can be manually defined
|
||||||
|
|
||||||
|
**Priority**: Low
|
||||||
|
**Effort**: ~1 hour
|
||||||
|
|
||||||
|
### 8. Bit Field Declarations
|
||||||
|
|
||||||
|
**Issue**: Bit fields not supported
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```c
|
||||||
|
struct {
|
||||||
|
unsigned a : 4;
|
||||||
|
unsigned b : 4;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
**Status**: Not used in SDL public API
|
||||||
|
|
||||||
|
**Priority**: Very Low
|
||||||
|
|
||||||
|
## Workaround Strategies
|
||||||
|
|
||||||
|
### Strategy 1: Manual Type Definitions
|
||||||
|
|
||||||
|
Create a supplementary file with missing types:
|
||||||
|
```zig
|
||||||
|
// manual_types.zig
|
||||||
|
pub const HitTest = *const fn(?*Window, *const Point, ?*anyopaque) callconv(.C) void;
|
||||||
|
pub const Scancode = c_int; // Simplified if full enum not needed
|
||||||
|
```
|
||||||
|
|
||||||
|
### Strategy 2: Direct C Import
|
||||||
|
|
||||||
|
For problematic types, use C directly:
|
||||||
|
```zig
|
||||||
|
const c = @cImport(@cInclude("SDL3/SDL.h"));
|
||||||
|
pub const Scancode = c.SDL_Scancode;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Strategy 3: Selective Generation
|
||||||
|
|
||||||
|
Only generate for headers that work:
|
||||||
|
```bash
|
||||||
|
# These work well:
|
||||||
|
zig build run -- SDL_gpu.h --output=gpu.zig
|
||||||
|
zig build run -- SDL_properties.h --output=properties.zig
|
||||||
|
|
||||||
|
# These need work:
|
||||||
|
# SDL_keyboard.h, SDL_events.h (use C import for now)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing Results by Header
|
||||||
|
|
||||||
|
### ✅ Fully Working
|
||||||
|
|
||||||
|
| Header | Declarations | Dependencies | Issues |
|
||||||
|
|--------|--------------|--------------|--------|
|
||||||
|
| SDL_gpu.h | 169 | 5/5 (100%) | 1 minor (field name) |
|
||||||
|
|
||||||
|
### ⚠️ Partial Support
|
||||||
|
|
||||||
|
| Header | Dependencies Resolved | Main Issue |
|
||||||
|
|--------|----------------------|------------|
|
||||||
|
| SDL_keyboard.h | 6/6 (100%) | Large enum syntax errors |
|
||||||
|
| SDL_video.h | 5/14 (36%) | Bit position parsing |
|
||||||
|
| SDL_events.h | Unknown | Parse errors |
|
||||||
|
|
||||||
|
## Error Messages Explained
|
||||||
|
|
||||||
|
### "Could not find definition for type: X"
|
||||||
|
|
||||||
|
**Meaning**: Type referenced but not found in any included header
|
||||||
|
|
||||||
|
**Possible Causes**:
|
||||||
|
1. Type is a function pointer (not supported)
|
||||||
|
2. Type is external (EGL, GL) (expected)
|
||||||
|
3. Type is in a header not included
|
||||||
|
4. Type uses unsupported pattern
|
||||||
|
|
||||||
|
**Action**: Check if type is needed, add manually if so
|
||||||
|
|
||||||
|
### "Syntax errors detected in generated code"
|
||||||
|
|
||||||
|
**Meaning**: Generated Zig code doesn't parse
|
||||||
|
|
||||||
|
**Possible Causes**:
|
||||||
|
1. Large enum parsing issue
|
||||||
|
2. Field name shadows keyword
|
||||||
|
3. Unsupported C pattern
|
||||||
|
|
||||||
|
**Action**: Check line numbers in error, see if manual fix needed
|
||||||
|
|
||||||
|
### "InvalidBitPosition"
|
||||||
|
|
||||||
|
**Meaning**: Flag value pattern not recognized
|
||||||
|
|
||||||
|
**Possible Causes**:
|
||||||
|
1. Uses SDL_UINT64_C macro (partially supported)
|
||||||
|
2. Complex bit expression
|
||||||
|
3. Non-standard format
|
||||||
|
|
||||||
|
**Action**: May need to manually define flags
|
||||||
|
|
||||||
|
### Memory Leak Warnings
|
||||||
|
|
||||||
|
**Meaning**: Small allocations not freed
|
||||||
|
|
||||||
|
**Impact**: Minimal (1-2KB per run)
|
||||||
|
|
||||||
|
**Status**: Known issue in comment handling, functional
|
||||||
|
|
||||||
|
**Action**: None required (will be fixed in future)
|
||||||
|
|
||||||
|
## Supported vs Unsupported
|
||||||
|
|
||||||
|
### ✅ Fully Supported
|
||||||
|
|
||||||
|
- Opaque types
|
||||||
|
- Simple structs
|
||||||
|
- Multi-field structs (`int x, y;`)
|
||||||
|
- Enums (up to ~100 values)
|
||||||
|
- Flags (with standard patterns)
|
||||||
|
- Typedefs (simple type aliases)
|
||||||
|
- Functions (extern declarations)
|
||||||
|
- Dependency resolution
|
||||||
|
- Type conversion
|
||||||
|
- Method grouping
|
||||||
|
|
||||||
|
### ⚠️ Partially Supported
|
||||||
|
|
||||||
|
- Large enums (300+ values) - needs work
|
||||||
|
- SDL_UINT64_C flags - enhanced but not fully tested
|
||||||
|
- Some bit position patterns
|
||||||
|
|
||||||
|
### ❌ Not Supported
|
||||||
|
|
||||||
|
- Function pointer typedefs
|
||||||
|
- #define-based type definitions (without typedef)
|
||||||
|
- Union types
|
||||||
|
- Bit field structs
|
||||||
|
- Complex macro expressions
|
||||||
|
- Non-SDL types
|
||||||
|
|
||||||
|
## Reporting Issues
|
||||||
|
|
||||||
|
When encountering a new issue:
|
||||||
|
|
||||||
|
1. **Check this document** - May already be known
|
||||||
|
2. **Test with simple case** - Isolate the problem
|
||||||
|
3. **Check generated output** - Look at line numbers in errors
|
||||||
|
4. **Document the pattern** - Save example for future reference
|
||||||
|
|
||||||
|
## Future Improvements
|
||||||
|
|
||||||
|
### High Priority
|
||||||
|
|
||||||
|
1. **Large enum support** - Would enable SDL_keyboard.h
|
||||||
|
2. **SDL_UINT64_C validation** - Complete SDL_video.h support
|
||||||
|
|
||||||
|
### Medium Priority
|
||||||
|
|
||||||
|
3. **Function pointer typedefs** - For callback types
|
||||||
|
4. **Field name escaping** - Auto-fix keyword shadowing
|
||||||
|
5. **Memory leak cleanup** - Fix comment handling
|
||||||
|
|
||||||
|
### Low Priority
|
||||||
|
|
||||||
|
6. **Union support** - Rarely used in SDL
|
||||||
|
7. **Bit field support** - Not in SDL public API
|
||||||
|
8. **Array fields** - Uncommon pattern
|
||||||
|
|
||||||
|
## Comparison with Manual Approach
|
||||||
|
|
||||||
|
### Manual Binding Creation
|
||||||
|
|
||||||
|
**Time**: ~30 minutes per header
|
||||||
|
**Error Rate**: High (missing fields, wrong types)
|
||||||
|
**Maintenance**: Manual updates needed
|
||||||
|
**Consistency**: Varies by developer
|
||||||
|
|
||||||
|
### Parser Approach
|
||||||
|
|
||||||
|
**Time**: ~0.5 seconds
|
||||||
|
**Error Rate**: Low (for supported patterns)
|
||||||
|
**Maintenance**: Automatic with SDL updates
|
||||||
|
**Consistency**: Perfect (deterministic)
|
||||||
|
|
||||||
|
**Conclusion**: Parser is vastly superior for supported patterns, with clear workarounds for unsupported cases.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Status**: Production ready for SDL_gpu.h, partial support for other headers.
|
||||||
|
**Recommendation**: Use parser for SDL_gpu.h, evaluate others case-by-case.
|
||||||
|
**Next**: See [Development](DEVELOPMENT.md) for how to fix remaining issues.
|
||||||
|
|
@ -1,146 +0,0 @@
|
||||||
# SDL3 Parser - C to Zig Binding Generator
|
|
||||||
|
|
||||||
A robust parser that automatically generates idiomatic Zig bindings from SDL3 C header files.
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
The SDL3 Parser analyzes C header files and generates type-safe Zig code with proper naming conventions, memory safety, and zero-cost abstractions. It handles opaque types, enums, structs, flags, and function declarations.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- ✅ **Automatic binding generation** - Parse C headers and output Zig code
|
|
||||||
- ✅ **Idiomatic naming** - Converts C naming to Zig conventions
|
|
||||||
- ✅ **Type safety** - Generates packed structs for flags, enums with backing types
|
|
||||||
- ✅ **Zero overhead** - Inline function wrappers with proper casts
|
|
||||||
- ✅ **Memory safe** - No memory leaks, validated with GPA
|
|
||||||
- ✅ **Well tested** - 18+ unit tests, integration tested with SDL_gpu.h
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
### Build
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd lib/sdl3/parser
|
|
||||||
zig build
|
|
||||||
```
|
|
||||||
|
|
||||||
### Parse a Header
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Generate Zig bindings
|
|
||||||
zig build run -- ../SDL/include/SDL3/SDL_gpu.h > output/gpu.zig
|
|
||||||
|
|
||||||
# With C mocks (planned feature)
|
|
||||||
zig build run -- ../SDL/include/SDL3/SDL_gpu.h --mocks
|
|
||||||
```
|
|
||||||
|
|
||||||
### Run Tests
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Unit tests
|
|
||||||
zig build test
|
|
||||||
|
|
||||||
# Test harness (planned)
|
|
||||||
cd test_project
|
|
||||||
zig build test
|
|
||||||
```
|
|
||||||
|
|
||||||
## Output Example
|
|
||||||
|
|
||||||
**Input (C):**
|
|
||||||
```c
|
|
||||||
typedef struct SDL_GPUDevice SDL_GPUDevice;
|
|
||||||
|
|
||||||
typedef enum SDL_GPUPrimitiveType {
|
|
||||||
SDL_GPU_PRIMITIVETYPE_TRIANGLELIST,
|
|
||||||
SDL_GPU_PRIMITIVETYPE_TRIANGLESTRIP,
|
|
||||||
} SDL_GPUPrimitiveType;
|
|
||||||
|
|
||||||
typedef Uint32 SDL_GPUTextureUsageFlags;
|
|
||||||
#define SDL_GPU_TEXTUREUSAGE_SAMPLER (1u << 0)
|
|
||||||
#define SDL_GPU_TEXTUREUSAGE_COLOR_TARGET (1u << 1)
|
|
||||||
|
|
||||||
extern SDL_DECLSPEC SDL_GPUDevice* SDLCALL SDL_CreateGPUDevice(bool debug_mode);
|
|
||||||
```
|
|
||||||
|
|
||||||
**Output (Zig):**
|
|
||||||
```zig
|
|
||||||
pub const GPUDevice = opaque {};
|
|
||||||
|
|
||||||
pub const GPUPrimitiveType = enum(c_int) {
|
|
||||||
primitivetypeTrianglelist,
|
|
||||||
primitivetypeTrianglestrip,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const GPUTextureUsageFlags = packed struct(u32) {
|
|
||||||
textureusageSampler: bool = false,
|
|
||||||
textureusageColorTarget: bool = false,
|
|
||||||
pad0: u29 = 0,
|
|
||||||
rsvd: bool = false,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub inline fn createGPUDevice(debug_mode: bool) ?*GPUDevice {
|
|
||||||
return @ptrCast(c.SDL_CreateGPUDevice(debug_mode));
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
The parser consists of four main components:
|
|
||||||
|
|
||||||
1. **Scanner** (`patterns.zig`) - Lexical analysis and pattern matching
|
|
||||||
2. **Naming** (`naming.zig`) - C to Zig name conversion
|
|
||||||
3. **Types** (`types.zig`) - C to Zig type mapping
|
|
||||||
4. **CodeGen** (`codegen.zig`) - Zig code generation
|
|
||||||
|
|
||||||
See [Architecture](architecture.md) for details.
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
- [Architecture](architecture.md) - System design and components
|
|
||||||
- [Usage Guide](usage.md) - Detailed usage instructions
|
|
||||||
- [Naming Conventions](naming.md) - How C names map to Zig
|
|
||||||
- [Test Harness Plan](../TEST_HARNESS_PLAN_V2.md) - Planned testing infrastructure
|
|
||||||
|
|
||||||
## Project Status
|
|
||||||
|
|
||||||
### Completed ✅
|
|
||||||
- Core parser functionality
|
|
||||||
- All C declaration types supported
|
|
||||||
- Proper naming conventions
|
|
||||||
- Memory leak free
|
|
||||||
- Comprehensive unit tests
|
|
||||||
- Integration tested with SDL_gpu.h
|
|
||||||
|
|
||||||
### Planned 🚧
|
|
||||||
- C mock generation (`--mocks` flag)
|
|
||||||
- Complete test harness with linkage testing
|
|
||||||
- Golden file regression testing
|
|
||||||
- Multiple header support
|
|
||||||
- Performance benchmarking
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
- Zig 0.14+ (tested with 0.15.2)
|
|
||||||
- SDL3 headers (for input)
|
|
||||||
- No runtime dependencies
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
The parser is currently under active development. See the [Test Harness Plan](../TEST_HARNESS_PLAN_V2.md) for upcoming features.
|
|
||||||
|
|
||||||
## Recent Changes
|
|
||||||
|
|
||||||
### Version 2024-01 (Current)
|
|
||||||
- Fixed critical flag parsing bug (empty structs)
|
|
||||||
- Fixed invalid identifier generation (numeric prefixes)
|
|
||||||
- Implemented "first underscore" naming rule
|
|
||||||
- Added 13 new unit tests
|
|
||||||
- Memory leak fixes
|
|
||||||
- Comprehensive documentation
|
|
||||||
|
|
||||||
See [IMPLEMENTATION_COMPLETE.md](../IMPLEMENTATION_COMPLETE.md) for detailed changes.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Part of the Backlog game engine project.
|
|
||||||
|
|
@ -1,285 +0,0 @@
|
||||||
# Architecture
|
|
||||||
|
|
||||||
The SDL3 Parser is a multi-stage pipeline that transforms C header declarations into idiomatic Zig code.
|
|
||||||
|
|
||||||
## Pipeline Overview
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────┐
|
|
||||||
│ C Header │
|
|
||||||
│ (SDL_gpu.h) │
|
|
||||||
└──────┬──────┘
|
|
||||||
│
|
|
||||||
v
|
|
||||||
┌─────────────────────────────────────────┐
|
|
||||||
│ Stage 1: Lexical Scanning (Scanner) │
|
|
||||||
│ - Read source file │
|
|
||||||
│ - Skip whitespace & comments │
|
|
||||||
│ - Extract doc comments │
|
|
||||||
└──────┬──────────────────────────────────┘
|
|
||||||
│
|
|
||||||
v
|
|
||||||
┌─────────────────────────────────────────┐
|
|
||||||
│ Stage 2: Pattern Matching │
|
|
||||||
│ - scanOpaque() │
|
|
||||||
│ - scanEnum() │
|
|
||||||
│ - scanStruct() │
|
|
||||||
│ - scanFlagTypedef() │
|
|
||||||
│ - scanFunction() │
|
|
||||||
└──────┬──────────────────────────────────┘
|
|
||||||
│
|
|
||||||
v
|
|
||||||
┌─────────────────────────────────────────┐
|
|
||||||
│ Stage 3: Naming Conversion │
|
|
||||||
│ - detectCommonPrefix() │
|
|
||||||
│ - enumValueToZig() │
|
|
||||||
│ - typeNameToZig() │
|
|
||||||
│ - functionNameToZig() │
|
|
||||||
└──────┬──────────────────────────────────┘
|
|
||||||
│
|
|
||||||
v
|
|
||||||
┌─────────────────────────────────────────┐
|
|
||||||
│ Stage 4: Code Generation │
|
|
||||||
│ - Generate type declarations │
|
|
||||||
│ - Generate inline functions │
|
|
||||||
│ - Add proper casts & annotations │
|
|
||||||
└──────┬──────────────────────────────────┘
|
|
||||||
│
|
|
||||||
v
|
|
||||||
┌─────────────┐
|
|
||||||
│ Zig Code │
|
|
||||||
│ (gpu.zig) │
|
|
||||||
└─────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
## Components
|
|
||||||
|
|
||||||
### 1. Scanner (patterns.zig)
|
|
||||||
|
|
||||||
**Purpose**: Tokenize and extract C declarations from source.
|
|
||||||
|
|
||||||
**Key Functions**:
|
|
||||||
- `scan()` - Main entry point, returns array of declarations
|
|
||||||
- `scanOpaque()` - Matches `typedef struct X X;`
|
|
||||||
- `scanEnum()` - Matches `typedef enum { ... } X;`
|
|
||||||
- `scanStruct()` - Matches `typedef struct { ... } X;`
|
|
||||||
- `scanFlagTypedef()` - Matches `typedef Uint32 XFlags;` + `#define` lines
|
|
||||||
- `scanFunction()` - Matches `extern SDL_DECLSPEC ... SDLCALL X(...);`
|
|
||||||
|
|
||||||
**Key Helpers**:
|
|
||||||
- `skipWhitespace()` - Skip whitespace/newlines (critical for flag parsing)
|
|
||||||
- `peekDocComment()` - Extract `/** ... */` documentation
|
|
||||||
- `readBracedBlock()` - Read `{ ... }` blocks with nesting support
|
|
||||||
|
|
||||||
**Data Structures**:
|
|
||||||
```zig
|
|
||||||
pub const Declaration = union(enum) {
|
|
||||||
opaque_type: OpaqueType,
|
|
||||||
enum_decl: EnumDecl,
|
|
||||||
struct_decl: StructDecl,
|
|
||||||
flag_decl: FlagDecl,
|
|
||||||
function_decl: FunctionDecl,
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Naming (naming.zig)
|
|
||||||
|
|
||||||
**Purpose**: Convert C naming conventions to Zig idioms.
|
|
||||||
|
|
||||||
**Key Algorithm - "First Underscore Rule"**:
|
|
||||||
|
|
||||||
```zig
|
|
||||||
// Input: SDL_GPU_PRIMITIVETYPE_TRIANGLELIST
|
|
||||||
// 1. Strip prefix: PRIMITIVETYPE_TRIANGLELIST
|
|
||||||
// 2. Find first underscore at position 13
|
|
||||||
// 3. Split: PRIMITIVETYPE + TRIANGLELIST
|
|
||||||
// 4. Convert: primitivetype + Trianglelist
|
|
||||||
// 5. Result: primitivetypeTrianglelist
|
|
||||||
```
|
|
||||||
|
|
||||||
**Key Functions**:
|
|
||||||
- `detectCommonPrefix()` - Returns `SDL_GPU_` or `SDL_` (NOT type name)
|
|
||||||
- `enumValueToZig()` - Applies first underscore rule
|
|
||||||
- `typeNameToZig()` - Strips SDL prefix: `SDL_GPUDevice` → `GPUDevice`
|
|
||||||
- `functionNameToZig()` - Lowercases leading acronyms: `SDL_CreateGPUDevice` → `createGPUDevice`
|
|
||||||
|
|
||||||
**Rationale for First Underscore**:
|
|
||||||
- Prevents invalid identifiers starting with numbers (`2d` → `texturetype2d`)
|
|
||||||
- Preserves semantic meaning (type + value)
|
|
||||||
- Handles multi-word values correctly (`2D_ARRAY` → `2dArray`)
|
|
||||||
|
|
||||||
### 3. Types (types.zig)
|
|
||||||
|
|
||||||
**Purpose**: Map C types to Zig types.
|
|
||||||
|
|
||||||
**Type Mappings**:
|
|
||||||
```zig
|
|
||||||
C Type → Zig Type
|
|
||||||
─────────────────────────────────
|
|
||||||
bool → bool
|
|
||||||
int → c_int
|
|
||||||
unsigned int → c_uint
|
|
||||||
float → f32
|
|
||||||
double → f64
|
|
||||||
char * → [*:0]const u8
|
|
||||||
void * → ?*anyopaque
|
|
||||||
const T * → *const T
|
|
||||||
T * → *T
|
|
||||||
Uint32 → u32
|
|
||||||
Sint64 → i64
|
|
||||||
```
|
|
||||||
|
|
||||||
**Cast Types**:
|
|
||||||
- `.ptr_cast` - For pointer conversions
|
|
||||||
- `.bit_cast` - For flag/enum conversions
|
|
||||||
- `.int_from_enum` - For enum to int
|
|
||||||
- `.enum_from_int` - For int to enum
|
|
||||||
|
|
||||||
### 4. CodeGen (codegen.zig)
|
|
||||||
|
|
||||||
**Purpose**: Generate final Zig code with proper formatting.
|
|
||||||
|
|
||||||
**Generation Strategy**:
|
|
||||||
|
|
||||||
**Opaque Types**:
|
|
||||||
```zig
|
|
||||||
pub const GPUDevice = opaque {};
|
|
||||||
```
|
|
||||||
|
|
||||||
**Enums**:
|
|
||||||
```zig
|
|
||||||
pub const GPUPrimitiveType = enum(c_int) {
|
|
||||||
primitivetypeTrianglelist,
|
|
||||||
primitivetypeTrianglestrip,
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
**Flags (Packed Structs)**:
|
|
||||||
```zig
|
|
||||||
pub const GPUTextureUsageFlags = packed struct(u32) {
|
|
||||||
textureusageSampler: bool = false,
|
|
||||||
textureusageColorTarget: bool = false,
|
|
||||||
// ... more flags
|
|
||||||
pad0: u24 = 0, // Calculated padding
|
|
||||||
rsvd: bool = false, // Reserved bit
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
**Functions (Inline Wrappers)**:
|
|
||||||
```zig
|
|
||||||
pub inline fn createGPUDevice(debug_mode: bool) ?*GPUDevice {
|
|
||||||
return @ptrCast(c.SDL_CreateGPUDevice(debug_mode));
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Why Inline Functions?**
|
|
||||||
- Zero overhead (inlined away at compile time)
|
|
||||||
- Type-safe wrappers around C calls
|
|
||||||
- Automatic cast insertion
|
|
||||||
- Better error messages
|
|
||||||
|
|
||||||
## Critical Implementation Details
|
|
||||||
|
|
||||||
### Flag Parsing Bug Fix
|
|
||||||
|
|
||||||
**Problem**: After reading `typedef Uint32 SDL_GPUTextureUsageFlags;`, scanner position is at newline. Calling `matchPrefix("#define ")` immediately fails.
|
|
||||||
|
|
||||||
**Solution**: Call `skipWhitespace()` before checking for `#define` statements.
|
|
||||||
|
|
||||||
```zig
|
|
||||||
// In scanFlagTypedef()
|
|
||||||
var flags = try std.ArrayList(FlagValue).initCapacity(self.allocator, 10);
|
|
||||||
|
|
||||||
self.skipWhitespace(); // <-- CRITICAL: Skip newlines
|
|
||||||
|
|
||||||
while (!self.isAtEnd()) {
|
|
||||||
if (!self.matchPrefix("#define ")) break;
|
|
||||||
// ... parse flag
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Invalid Identifier Fix
|
|
||||||
|
|
||||||
**Problem**: Using "last underscore" rule on `SDL_GPU_TEXTURETYPE_2D_ARRAY` splits as:
|
|
||||||
- Type: `TEXTURETYPE_2D`
|
|
||||||
- Value: `ARRAY`
|
|
||||||
- Result: `texturetype2dArray` ✓ Valid but wrong semantics
|
|
||||||
|
|
||||||
Using "last underscore" on `SDL_GPU_SAMPLECOUNT_1` splits as:
|
|
||||||
- Type: `SAMPLECOUNT`
|
|
||||||
- Value: `1`
|
|
||||||
- Result: `samplecount1` ✓ But "first underscore" gives same result
|
|
||||||
|
|
||||||
The key insight: **Always use first underscore after prefix**. This keeps type name intact and prevents semantic errors.
|
|
||||||
|
|
||||||
### Memory Management
|
|
||||||
|
|
||||||
**Allocation Points**:
|
|
||||||
1. Source file read (`readFileAlloc`)
|
|
||||||
2. Declaration storage (`ArrayList`)
|
|
||||||
3. String duplication (`allocator.dupe`)
|
|
||||||
4. Doc comments (`allocator.dupe`)
|
|
||||||
|
|
||||||
**Cleanup Strategy**:
|
|
||||||
- Use arena allocator in tests (automatic cleanup)
|
|
||||||
- Manual cleanup in main with defer blocks
|
|
||||||
- Free doc comments in declaration cleanup
|
|
||||||
- Free pending_doc_comment when skipping lines
|
|
||||||
|
|
||||||
**GPA Verification**:
|
|
||||||
```bash
|
|
||||||
zig build run -- SDL_gpu.h 2>&1 | grep -i leak
|
|
||||||
# Output: (empty = no leaks)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Performance Characteristics
|
|
||||||
|
|
||||||
- **Time Complexity**: O(n) where n = source file size
|
|
||||||
- **Memory**: O(d) where d = number of declarations
|
|
||||||
- **Typical Parse Time**: <500ms for SDL_gpu.h (169 declarations)
|
|
||||||
- **Memory Usage**: ~5MB peak for SDL_gpu.h
|
|
||||||
|
|
||||||
## Extension Points
|
|
||||||
|
|
||||||
To add support for new C patterns:
|
|
||||||
|
|
||||||
1. **Add pattern matcher** in `patterns.zig`:
|
|
||||||
```zig
|
|
||||||
fn scanNewPattern(self: *Scanner) !?NewDecl { ... }
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Add naming converter** in `naming.zig`:
|
|
||||||
```zig
|
|
||||||
pub fn newPatternToZig(c_name: []const u8) []const u8 { ... }
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Add code generator** in `codegen.zig`:
|
|
||||||
```zig
|
|
||||||
fn writeNewPattern(self: *CodeGen, decl: NewDecl) !void { ... }
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **Add to Declaration union**:
|
|
||||||
```zig
|
|
||||||
pub const Declaration = union(enum) {
|
|
||||||
// ... existing
|
|
||||||
new_pattern: NewDecl,
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing Strategy
|
|
||||||
|
|
||||||
**Unit Tests**: Test individual components in isolation
|
|
||||||
- Scanner tests: Verify pattern matching
|
|
||||||
- Naming tests: Verify conversion rules
|
|
||||||
- CodeGen tests: Verify output formatting
|
|
||||||
|
|
||||||
**Integration Tests**: Test complete pipeline
|
|
||||||
- Parse real SDL3 headers
|
|
||||||
- Verify output compiles
|
|
||||||
- Check declaration counts
|
|
||||||
|
|
||||||
**Regression Tests** (planned):
|
|
||||||
- Golden file comparison
|
|
||||||
- Detect unintended changes
|
|
||||||
|
|
||||||
See [Test Harness Plan](../TEST_HARNESS_PLAN_V2.md) for future testing infrastructure.
|
|
||||||
|
|
@ -1,369 +0,0 @@
|
||||||
# Naming Conventions
|
|
||||||
|
|
||||||
This document explains how the SDL3 Parser converts C naming conventions to idiomatic Zig code.
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
The parser applies systematic rules to transform SDL3's C naming patterns into Zig-friendly identifiers while preserving semantic meaning and avoiding invalid identifiers.
|
|
||||||
|
|
||||||
## Core Principle: The "First Underscore Rule"
|
|
||||||
|
|
||||||
The fundamental naming algorithm is the **first underscore rule**, which prevents invalid identifiers and preserves type semantics.
|
|
||||||
|
|
||||||
### Algorithm
|
|
||||||
|
|
||||||
For enum values like `SDL_GPU_PRIMITIVETYPE_TRIANGLELIST`:
|
|
||||||
|
|
||||||
1. **Strip SDL prefix**: `PRIMITIVETYPE_TRIANGLELIST`
|
|
||||||
2. **Find first underscore**: Position 13 (after `PRIMITIVETYPE`)
|
|
||||||
3. **Split into parts**:
|
|
||||||
- Type part: `PRIMITIVETYPE`
|
|
||||||
- Value part: `TRIANGLELIST`
|
|
||||||
4. **Convert casing**:
|
|
||||||
- Type → lowercase: `primitivetype`
|
|
||||||
- Value → TitleCase: `Trianglelist`
|
|
||||||
5. **Concatenate**: `primitivetypeTrianglelist`
|
|
||||||
|
|
||||||
### Why First Underscore?
|
|
||||||
|
|
||||||
**Problem with Last Underscore**:
|
|
||||||
```
|
|
||||||
SDL_GPU_TEXTURETYPE_2D_ARRAY
|
|
||||||
Split at LAST underscore: TEXTURETYPE_2D + ARRAY
|
|
||||||
Result: texturetype2dArray ✗ Wrong semantics
|
|
||||||
```
|
|
||||||
|
|
||||||
**First Underscore Solution**:
|
|
||||||
```
|
|
||||||
SDL_GPU_TEXTURETYPE_2D_ARRAY
|
|
||||||
Split at FIRST underscore: TEXTURETYPE + 2D_ARRAY
|
|
||||||
Result: texturetype2dArray ✓ Correct!
|
|
||||||
```
|
|
||||||
|
|
||||||
**Prevents Invalid Identifiers**:
|
|
||||||
```
|
|
||||||
SDL_GPU_INDEXELEMENTSIZE_16BIT
|
|
||||||
Split at FIRST underscore: INDEXELEMENTSIZE + 16BIT
|
|
||||||
Result: indexelementsize16bit ✓ Valid (starts with letter)
|
|
||||||
|
|
||||||
If we stripped too much:
|
|
||||||
Result: 16bit ✗ Invalid Zig identifier (starts with number)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Type Name Conversion
|
|
||||||
|
|
||||||
### Opaque Types, Enums, Structs, Flags
|
|
||||||
|
|
||||||
**Pattern**: Strip `SDL_` prefix, keep GPU prefix
|
|
||||||
|
|
||||||
| C Name | Zig Name |
|
|
||||||
|--------|----------|
|
|
||||||
| `SDL_GPUDevice` | `GPUDevice` |
|
|
||||||
| `SDL_GPUBuffer` | `GPUBuffer` |
|
|
||||||
| `SDL_GPUTextureUsageFlags` | `GPUTextureUsageFlags` |
|
|
||||||
| `SDL_Window` | `Window` |
|
|
||||||
|
|
||||||
**Rule**:
|
|
||||||
```zig
|
|
||||||
// Strip SDL_ or SDL_GPU_ prefix
|
|
||||||
typeNameToZig("SDL_GPUDevice") → "GPUDevice"
|
|
||||||
typeNameToZig("SDL_Window") → "Window"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Enum Value Conversion
|
|
||||||
|
|
||||||
### Standard Pattern
|
|
||||||
|
|
||||||
**C Enum**:
|
|
||||||
```c
|
|
||||||
typedef enum SDL_GPUPrimitiveType {
|
|
||||||
SDL_GPU_PRIMITIVETYPE_TRIANGLELIST,
|
|
||||||
SDL_GPU_PRIMITIVETYPE_TRIANGLESTRIP,
|
|
||||||
} SDL_GPUPrimitiveType;
|
|
||||||
```
|
|
||||||
|
|
||||||
**Zig Enum**:
|
|
||||||
```zig
|
|
||||||
pub const GPUPrimitiveType = enum(c_int) {
|
|
||||||
primitivetypeTrianglelist,
|
|
||||||
primitivetypeTrianglestrip,
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
### Numeric Suffixes
|
|
||||||
|
|
||||||
**C Enum**:
|
|
||||||
```c
|
|
||||||
typedef enum SDL_GPUSampleCount {
|
|
||||||
SDL_GPU_SAMPLECOUNT_1,
|
|
||||||
SDL_GPU_SAMPLECOUNT_2,
|
|
||||||
SDL_GPU_SAMPLECOUNT_4,
|
|
||||||
} SDL_GPUSampleCount;
|
|
||||||
```
|
|
||||||
|
|
||||||
**Zig Enum**:
|
|
||||||
```zig
|
|
||||||
pub const GPUSampleCount = enum(c_int) {
|
|
||||||
samplecount1,
|
|
||||||
samplecount2,
|
|
||||||
samplecount4,
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note**: The type prefix (`samplecount`) prevents the invalid identifier `1`, `2`, `4`.
|
|
||||||
|
|
||||||
### Multi-Word Values
|
|
||||||
|
|
||||||
**C Enum**:
|
|
||||||
```c
|
|
||||||
typedef enum SDL_GPUTextureType {
|
|
||||||
SDL_GPU_TEXTURETYPE_2D,
|
|
||||||
SDL_GPU_TEXTURETYPE_2D_ARRAY,
|
|
||||||
SDL_GPU_TEXTURETYPE_3D,
|
|
||||||
} SDL_GPUTextureType;
|
|
||||||
```
|
|
||||||
|
|
||||||
**Zig Enum**:
|
|
||||||
```zig
|
|
||||||
pub const GPUTextureType = enum(c_int) {
|
|
||||||
texturetype2d,
|
|
||||||
texturetype2dArray,
|
|
||||||
texturetype3d,
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
**Algorithm Applied**:
|
|
||||||
- `SDL_GPU_TEXTURETYPE_2D_ARRAY`
|
|
||||||
- Strip prefix: `TEXTURETYPE_2D_ARRAY`
|
|
||||||
- First underscore at position 11
|
|
||||||
- Type: `TEXTURETYPE` → `texturetype`
|
|
||||||
- Value: `2D_ARRAY` → `2dArray`
|
|
||||||
- Result: `texturetype2dArray`
|
|
||||||
|
|
||||||
## Flag Field Conversion
|
|
||||||
|
|
||||||
### C Flags Definition
|
|
||||||
|
|
||||||
```c
|
|
||||||
typedef Uint32 SDL_GPUTextureUsageFlags;
|
|
||||||
#define SDL_GPU_TEXTUREUSAGE_SAMPLER (1u << 0)
|
|
||||||
#define SDL_GPU_TEXTUREUSAGE_COLOR_TARGET (1u << 1)
|
|
||||||
#define SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET (1u << 2)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Zig Packed Struct
|
|
||||||
|
|
||||||
```zig
|
|
||||||
pub const GPUTextureUsageFlags = packed struct(u32) {
|
|
||||||
textureusageSampler: bool = false,
|
|
||||||
textureusageColorTarget: bool = false,
|
|
||||||
textureusageDepthStencilTarget: bool = false,
|
|
||||||
pad0: u29 = 0,
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
**Field Name Pattern**:
|
|
||||||
- Strip `SDL_GPU_` prefix: `TEXTUREUSAGE_SAMPLER`
|
|
||||||
- Apply first underscore rule: `textureusage` + `Sampler`
|
|
||||||
- Result: `textureusageSampler`
|
|
||||||
|
|
||||||
## Function Name Conversion
|
|
||||||
|
|
||||||
### Pattern: Lowercase Leading Acronyms
|
|
||||||
|
|
||||||
**C Function**:
|
|
||||||
```c
|
|
||||||
extern SDL_DECLSPEC SDL_GPUDevice* SDLCALL SDL_CreateGPUDevice(bool debug_mode);
|
|
||||||
```
|
|
||||||
|
|
||||||
**Zig Function**:
|
|
||||||
```zig
|
|
||||||
pub inline fn createGPUDevice(debug_mode: bool) ?*GPUDevice {
|
|
||||||
return @ptrCast(c.SDL_CreateGPUDevice(debug_mode));
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Rule**:
|
|
||||||
- Strip `SDL_` prefix: `CreateGPUDevice`
|
|
||||||
- Lowercase first character: `createGPUDevice`
|
|
||||||
- Preserve internal acronyms: GPU stays uppercase
|
|
||||||
|
|
||||||
### More Examples
|
|
||||||
|
|
||||||
| C Function | Zig Function |
|
|
||||||
|------------|--------------|
|
|
||||||
| `SDL_CreateGPUDevice` | `createGPUDevice` |
|
|
||||||
| `SDL_DestroyGPUDevice` | `destroyGPUDevice` |
|
|
||||||
| `SDL_CreateWindow` | `createWindow` |
|
|
||||||
| `SDL_GetGPUSwapchainTextureFormat` | `getGPUSwapchainTextureFormat` |
|
|
||||||
|
|
||||||
## Prefix Detection
|
|
||||||
|
|
||||||
### Common Prefix Algorithm
|
|
||||||
|
|
||||||
**Goal**: Detect `SDL_GPU_` vs `SDL_` prefix
|
|
||||||
|
|
||||||
```zig
|
|
||||||
detectCommonPrefix(["SDL_GPU_PRIMITIVETYPE_TRIANGLELIST",
|
|
||||||
"SDL_GPU_PRIMITIVETYPE_TRIANGLESTRIP"])
|
|
||||||
→ "SDL_GPU_"
|
|
||||||
|
|
||||||
detectCommonPrefix(["SDL_WINDOW_FULLSCREEN",
|
|
||||||
"SDL_WINDOW_RESIZABLE"])
|
|
||||||
→ "SDL_"
|
|
||||||
```
|
|
||||||
|
|
||||||
**Implementation**:
|
|
||||||
1. Check if first name starts with `SDL_GPU_` → return `"SDL_GPU_"`
|
|
||||||
2. Otherwise check if it starts with `SDL_` → return `"SDL_"`
|
|
||||||
3. Otherwise return empty string
|
|
||||||
|
|
||||||
**Critical**: The prefix is ONLY the SDL part, NOT the type name part.
|
|
||||||
|
|
||||||
## Edge Cases
|
|
||||||
|
|
||||||
### Single Word (No Underscore)
|
|
||||||
|
|
||||||
**C Enum**:
|
|
||||||
```c
|
|
||||||
SDL_GPU_INVALID
|
|
||||||
```
|
|
||||||
|
|
||||||
**Zig**:
|
|
||||||
```zig
|
|
||||||
invalid // No underscore, so just lowercase entire word
|
|
||||||
```
|
|
||||||
|
|
||||||
### Numbers at Start (After Strip)
|
|
||||||
|
|
||||||
**Prevented by Type Prefix**:
|
|
||||||
```
|
|
||||||
SDL_GPU_INDEXELEMENTSIZE_16BIT
|
|
||||||
→ indexelementsize16bit ✓ Starts with letter
|
|
||||||
|
|
||||||
Without type prefix (WRONG):
|
|
||||||
→ 16bit ✗ Invalid identifier
|
|
||||||
```
|
|
||||||
|
|
||||||
### Consecutive Underscores
|
|
||||||
|
|
||||||
**C**:
|
|
||||||
```c
|
|
||||||
SDL_GPU_SOME__VALUE // Double underscore
|
|
||||||
```
|
|
||||||
|
|
||||||
**Zig**:
|
|
||||||
```zig
|
|
||||||
someValue // Underscores treated as word separators
|
|
||||||
```
|
|
||||||
|
|
||||||
## Casing Helpers
|
|
||||||
|
|
||||||
### screaminToLowerCamel
|
|
||||||
|
|
||||||
Converts `SCREAMING_SNAKE_CASE` to `lowerCamelCase`:
|
|
||||||
|
|
||||||
```zig
|
|
||||||
screaminToLowerCamel("TRIANGLE_LIST") → "triangleList"
|
|
||||||
screaminToLowerCamel("INVALID") → "invalid"
|
|
||||||
```
|
|
||||||
|
|
||||||
**Algorithm**:
|
|
||||||
1. First word: all lowercase
|
|
||||||
2. Subsequent words: capitalize first letter
|
|
||||||
3. Underscores removed
|
|
||||||
|
|
||||||
### screaminToTitleCamel
|
|
||||||
|
|
||||||
Converts `SCREAMING_SNAKE_CASE` to `TitleCamelCase`:
|
|
||||||
|
|
||||||
```zig
|
|
||||||
screaminToTitleCamel("TRIANGLE_LIST") → "TriangleList"
|
|
||||||
screaminToTitleCamel("2D_ARRAY") → "2dArray"
|
|
||||||
```
|
|
||||||
|
|
||||||
**Algorithm**:
|
|
||||||
1. Every word: capitalize first letter, lowercase rest
|
|
||||||
2. Underscores removed
|
|
||||||
3. Numbers preserved
|
|
||||||
|
|
||||||
## Testing Strategy
|
|
||||||
|
|
||||||
The naming.zig module includes comprehensive tests for:
|
|
||||||
|
|
||||||
1. **Prefix detection**: Verify `SDL_GPU_` vs `SDL_` detection
|
|
||||||
2. **Enum value conversion**: Test first underscore rule
|
|
||||||
3. **Numeric prefixes**: Ensure no invalid identifiers
|
|
||||||
4. **Multi-word values**: Test underscore handling
|
|
||||||
5. **Type name conversion**: Verify SDL prefix stripping
|
|
||||||
6. **Function name conversion**: Test lowercase leading character
|
|
||||||
|
|
||||||
See naming.zig for 10+ unit tests validating these rules.
|
|
||||||
|
|
||||||
## Design Rationale
|
|
||||||
|
|
||||||
### Why Keep Type Prefix in Enum Values?
|
|
||||||
|
|
||||||
**Benefit 1: Prevents Invalid Identifiers**
|
|
||||||
```zig
|
|
||||||
// With type prefix
|
|
||||||
indexelementsize16bit ✓ Valid
|
|
||||||
|
|
||||||
// Without type prefix
|
|
||||||
16bit ✗ Invalid
|
|
||||||
```
|
|
||||||
|
|
||||||
**Benefit 2: Namespace Clarity**
|
|
||||||
```zig
|
|
||||||
// With type prefix - clear which type
|
|
||||||
primitivetypeTrianglelist
|
|
||||||
texturetypeTrianglelist
|
|
||||||
|
|
||||||
// Without - ambiguous
|
|
||||||
trianglelist // Which type?
|
|
||||||
```
|
|
||||||
|
|
||||||
**Benefit 3: Consistent Pattern**
|
|
||||||
```zig
|
|
||||||
// All enum values follow same pattern
|
|
||||||
primitivetypeTrianglelist
|
|
||||||
primitivetypeTrianglestrip
|
|
||||||
primitivetypeLineList
|
|
||||||
// Type prefix always present
|
|
||||||
```
|
|
||||||
|
|
||||||
### Why Inline Functions Instead of Direct Imports?
|
|
||||||
|
|
||||||
**Type Safety**:
|
|
||||||
```zig
|
|
||||||
// Inline function with proper types
|
|
||||||
pub inline fn createGPUDevice(debug_mode: bool) ?*GPUDevice {
|
|
||||||
return @ptrCast(c.SDL_CreateGPUDevice(debug_mode));
|
|
||||||
}
|
|
||||||
|
|
||||||
// vs direct C import
|
|
||||||
c.SDL_CreateGPUDevice(debug_mode) // Returns opaque C type
|
|
||||||
```
|
|
||||||
|
|
||||||
**Zero Overhead**:
|
|
||||||
- `inline` keyword ensures no runtime cost
|
|
||||||
- Compiler optimizes away the wrapper
|
|
||||||
- Identical performance to direct C call
|
|
||||||
|
|
||||||
**Better Error Messages**:
|
|
||||||
- Zig type names in errors
|
|
||||||
- Clear parameter names
|
|
||||||
- Type checking at call site
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
The SDL3 Parser naming system:
|
|
||||||
|
|
||||||
1. Uses **first underscore rule** for enum values
|
|
||||||
2. Strips **SDL prefix** from type names (keeps GPU)
|
|
||||||
3. **Lowercases first character** of function names
|
|
||||||
4. Converts **SCREAMING_SNAKE** to **camelCase**
|
|
||||||
5. **Preserves type prefixes** in enum values for safety
|
|
||||||
6. **Prevents invalid identifiers** starting with numbers
|
|
||||||
|
|
||||||
All conversions are deterministic, tested, and generate valid Zig code.
|
|
||||||
|
|
@ -1,265 +0,0 @@
|
||||||
# Usage Guide
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd lib/sdl3/parser
|
|
||||||
zig build
|
|
||||||
```
|
|
||||||
|
|
||||||
## Basic Usage
|
|
||||||
|
|
||||||
### Parse a Header File
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Output to stdout
|
|
||||||
zig build run -- ../SDL/include/SDL3/SDL_gpu.h
|
|
||||||
|
|
||||||
# Save to file with --output
|
|
||||||
zig build run -- ../SDL/include/SDL3/SDL_gpu.h --output=gpu.zig
|
|
||||||
|
|
||||||
# Generate with C mocks
|
|
||||||
zig build run -- ../SDL/include/SDL3/SDL_gpu.h --output=gpu.zig --mocks=gpu_mock.c
|
|
||||||
|
|
||||||
# Test mock generation (uses test_small.h)
|
|
||||||
zig build test-mocks
|
|
||||||
# Output: zig-out/test_small.zig and zig-out/test_small_mock.c
|
|
||||||
```
|
|
||||||
|
|
||||||
### Command Line Options
|
|
||||||
|
|
||||||
- `<header-file>` - Path to C header file to parse (required)
|
|
||||||
- `--output=<path>` - Write Zig bindings to specified file (optional, defaults to stdout)
|
|
||||||
- `--mocks=<path>` - Generate C mock implementations at specified path (optional)
|
|
||||||
|
|
||||||
### Run Tests
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# All unit tests
|
|
||||||
zig build test
|
|
||||||
|
|
||||||
# Test mock generation
|
|
||||||
zig build test-mocks
|
|
||||||
```
|
|
||||||
|
|
||||||
## Output Format
|
|
||||||
|
|
||||||
The parser outputs Zig code with this structure:
|
|
||||||
|
|
||||||
```zig
|
|
||||||
pub const c = @import("c.zig").c;
|
|
||||||
|
|
||||||
// 1. Opaque types
|
|
||||||
pub const GPUDevice = opaque {};
|
|
||||||
|
|
||||||
// 2. Enums
|
|
||||||
pub const GPUPrimitiveType = enum(c_int) { ... };
|
|
||||||
|
|
||||||
// 3. Flags (packed structs)
|
|
||||||
pub const GPUTextureUsageFlags = packed struct(u32) { ... };
|
|
||||||
|
|
||||||
// 4. Structs
|
|
||||||
pub const GPUViewport = extern struct { ... };
|
|
||||||
|
|
||||||
// 5. Functions (inline wrappers)
|
|
||||||
pub inline fn createGPUDevice(...) ... { ... }
|
|
||||||
```
|
|
||||||
|
|
||||||
## Integration
|
|
||||||
|
|
||||||
### Using Generated Bindings
|
|
||||||
|
|
||||||
```zig
|
|
||||||
// Your project
|
|
||||||
const gpu = @import("gpu.zig");
|
|
||||||
|
|
||||||
pub fn main() !void {
|
|
||||||
// Use opaque types
|
|
||||||
const device = gpu.createGPUDevice(false, false, null);
|
|
||||||
defer if (device) |d| gpu.destroyGPUDevice(d);
|
|
||||||
|
|
||||||
// Use enums
|
|
||||||
const prim_type = gpu.GPUPrimitiveType.primitivetypeTrianglelist;
|
|
||||||
|
|
||||||
// Use flags
|
|
||||||
var usage: gpu.GPUTextureUsageFlags = .{};
|
|
||||||
usage.textureusageSampler = true;
|
|
||||||
usage.textureusageColorTarget = true;
|
|
||||||
|
|
||||||
// Use structs
|
|
||||||
const viewport = gpu.GPUViewport{
|
|
||||||
.x = 0.0,
|
|
||||||
.y = 0.0,
|
|
||||||
.w = 800.0,
|
|
||||||
.h = 600.0,
|
|
||||||
.min_depth = 0.0,
|
|
||||||
.max_depth = 1.0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Required c.zig
|
|
||||||
|
|
||||||
The generated bindings expect a `c.zig` file that exports C declarations:
|
|
||||||
|
|
||||||
```zig
|
|
||||||
// c.zig
|
|
||||||
pub const c = @cImport({
|
|
||||||
@cInclude("SDL3/SDL.h");
|
|
||||||
@cInclude("SDL3/SDL_gpu.h");
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
Or link with SDL3 directly in your build.zig:
|
|
||||||
|
|
||||||
```zig
|
|
||||||
const exe = b.addExecutable(.{
|
|
||||||
.name = "my_app",
|
|
||||||
.root_source_file = b.path("src/main.zig"),
|
|
||||||
// ...
|
|
||||||
});
|
|
||||||
|
|
||||||
exe.linkSystemLibrary("SDL3");
|
|
||||||
exe.linkLibC();
|
|
||||||
```
|
|
||||||
|
|
||||||
## Common Patterns
|
|
||||||
|
|
||||||
### Handling Opaque Pointers
|
|
||||||
|
|
||||||
```zig
|
|
||||||
// Functions return optional pointers
|
|
||||||
const device: ?*gpu.GPUDevice = gpu.createGPUDevice(...);
|
|
||||||
|
|
||||||
// Check before use
|
|
||||||
if (device) |d| {
|
|
||||||
// Use d safely
|
|
||||||
gpu.destroyGPUDevice(d);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Working with Flags
|
|
||||||
|
|
||||||
```zig
|
|
||||||
// Initialize empty
|
|
||||||
var flags: gpu.GPUTextureUsageFlags = .{};
|
|
||||||
|
|
||||||
// Set individual bits
|
|
||||||
flags.textureusageSampler = true;
|
|
||||||
flags.textureusageColorTarget = true;
|
|
||||||
|
|
||||||
// Pass to functions
|
|
||||||
const texture = gpu.createGPUTexture(device, &.{
|
|
||||||
.usage = flags,
|
|
||||||
// ... other fields
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### Enum Comparisons
|
|
||||||
|
|
||||||
```zig
|
|
||||||
const prim_type = gpu.GPUPrimitiveType.primitivetypeTrianglelist;
|
|
||||||
|
|
||||||
if (prim_type == .primitivetypeTrianglelist) {
|
|
||||||
// Handle triangle list
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Issue: "error: use of undeclared identifier 'c'"
|
|
||||||
|
|
||||||
**Solution**: Create a `c.zig` file that imports SDL3 headers:
|
|
||||||
|
|
||||||
```zig
|
|
||||||
pub const c = @cImport({
|
|
||||||
@cInclude("SDL3/SDL.h");
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### Issue: Parser crashes on header file
|
|
||||||
|
|
||||||
**Cause**: Unsupported C pattern
|
|
||||||
|
|
||||||
**Solution**: Check parser output for errors, file an issue with the problematic pattern
|
|
||||||
|
|
||||||
### Issue: Generated names don't match expectations
|
|
||||||
|
|
||||||
**Cause**: Naming convention mismatch
|
|
||||||
|
|
||||||
**Solution**: See [Naming Conventions](naming.md) for the conversion rules
|
|
||||||
|
|
||||||
### Issue: Memory leak warnings
|
|
||||||
|
|
||||||
**Cause**: Parser bug (should not happen in current version)
|
|
||||||
|
|
||||||
**Solution**: Run with GPA to identify leak, file an issue
|
|
||||||
|
|
||||||
```bash
|
|
||||||
zig build run -- header.h 2>&1 | grep -i leak
|
|
||||||
```
|
|
||||||
|
|
||||||
## Performance Tips
|
|
||||||
|
|
||||||
### For Large Headers
|
|
||||||
|
|
||||||
- Parser is O(n) in source size, typically <500ms
|
|
||||||
- Memory usage is O(declarations), typically <10MB
|
|
||||||
- No performance tuning needed for typical SDL3 headers
|
|
||||||
|
|
||||||
### Batch Processing
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Parse multiple headers
|
|
||||||
for header in ../SDL/include/SDL3/*.h; do
|
|
||||||
basename="${header##*/}"
|
|
||||||
zig build run -- "$header" > "output/${basename%.h}.zig"
|
|
||||||
done
|
|
||||||
```
|
|
||||||
|
|
||||||
## Advanced Usage
|
|
||||||
|
|
||||||
### Custom Naming
|
|
||||||
|
|
||||||
Edit `naming.zig` to customize conversion rules:
|
|
||||||
|
|
||||||
```zig
|
|
||||||
pub fn typeNameToZig(c_name: []const u8) []const u8 {
|
|
||||||
// Custom logic here
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Adding New Patterns
|
|
||||||
|
|
||||||
See [Architecture](architecture.md#extension-points) for how to add support for new C patterns.
|
|
||||||
|
|
||||||
### Debugging
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Run with debug info
|
|
||||||
zig build -Doptimize=Debug
|
|
||||||
zig-out/bin/sdl-parser header.h
|
|
||||||
|
|
||||||
# Check what's being parsed
|
|
||||||
zig build run -- header.h 2>&1 | head -20
|
|
||||||
```
|
|
||||||
|
|
||||||
## FAQ
|
|
||||||
|
|
||||||
**Q: Does the parser support C++?**
|
|
||||||
A: No, only C headers. C++ requires a full C++ parser.
|
|
||||||
|
|
||||||
**Q: Can I use this for non-SDL libraries?**
|
|
||||||
A: Yes, but it's optimized for SDL3 naming conventions. You may need to adjust naming.zig.
|
|
||||||
|
|
||||||
**Q: Does it handle macros?**
|
|
||||||
A: Only `#define` for flag values. Complex macros are not supported.
|
|
||||||
|
|
||||||
**Q: What about function pointers?**
|
|
||||||
A: Basic support exists but may need refinement for complex signatures.
|
|
||||||
|
|
||||||
**Q: Can it generate C code?**
|
|
||||||
A: Not yet, but mock generation is planned (see TEST_HARNESS_PLAN_V2.md).
|
|
||||||
|
|
||||||
**Q: Is it production ready?**
|
|
||||||
A: Yes for SDL3. It's tested with SDL_gpu.h and generates valid, working bindings.
|
|
||||||
Loading…
Reference in New Issue