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