Add comprehensive dependency resolution implementation plan
Plan for on-demand type resolution: - Single-file output with dependencies appended - Parse included headers only for missing types - Three-phase algorithm: parse, detect, resolve - Five components with clear interfaces and tests Key features: - Type reference scanner (finds all type usage) - Defined type collector (tracks what exists) - Include header parser (extracts #include directives) - Selective type extractor (finds specific types) - Main integration (wires everything together) Advantages: - Simple: one output file, no modules - Fast: parse dependencies once, extract many types - Minimal: only extract required types - Robust: Zig handles duplicate definitions - Testable: each component independently tested Ready to implement with: - Detailed code examples for each component - Complete test strategy - Success metrics - 5-day rollout plan - Risk mitigation
This commit is contained in:
parent
4c354f093c
commit
c7c7440c14
|
|
@ -0,0 +1,667 @@
|
||||||
|
# Dependency Resolution Implementation Plan (v2)
|
||||||
|
|
||||||
|
## Core Insight
|
||||||
|
|
||||||
|
**Single-file generation with on-demand type resolution**: Parse the primary header completely, identify missing types, then parse included headers ONLY for those specific types. Append them to the same output file. Zig's structural typing handles everything else.
|
||||||
|
|
||||||
|
## Why This Works
|
||||||
|
|
||||||
|
1. **No modules needed** - One output file with all types
|
||||||
|
2. **Zig deduplicates** - Multiple parsings of same type are safe
|
||||||
|
3. **Forward references work** - Zig allows using types before they're defined
|
||||||
|
4. **Simple implementation** - Just append to existing output
|
||||||
|
|
||||||
|
## Three-Phase Algorithm
|
||||||
|
|
||||||
|
### Phase 1: Primary Header Parsing (Existing)
|
||||||
|
```
|
||||||
|
Input: SDL_gpu.h
|
||||||
|
Output: declarations[], generated_code
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2: Missing Type Detection (NEW)
|
||||||
|
```
|
||||||
|
1. Scan generated_code for all type references
|
||||||
|
2. Build set of defined_types from declarations[]
|
||||||
|
3. missing_types = referenced_types - defined_types
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 3: Dependency Resolution (NEW)
|
||||||
|
```
|
||||||
|
For each missing_type:
|
||||||
|
1. Parse each included header
|
||||||
|
2. If type found, extract declaration
|
||||||
|
3. Append to output
|
||||||
|
```
|
||||||
|
|
||||||
|
## Implementation Details
|
||||||
|
|
||||||
|
## Implementation Details
|
||||||
|
|
||||||
|
### Component 1: Type Reference Scanner
|
||||||
|
|
||||||
|
**Purpose**: Find all type names used in generated code
|
||||||
|
|
||||||
|
**Location**: New file `src/dependency_resolver.zig`
|
||||||
|
|
||||||
|
```zig
|
||||||
|
pub const TypeReference = struct {
|
||||||
|
name: []const u8,
|
||||||
|
source_location: []const u8, // For debugging
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn scanTypeReferences(decls: []const Declaration) ![]TypeReference {
|
||||||
|
var refs = ArrayList(TypeReference).init(allocator);
|
||||||
|
|
||||||
|
for (decls) |decl| {
|
||||||
|
switch (decl) {
|
||||||
|
.function_decl => |func| {
|
||||||
|
// Scan return type
|
||||||
|
try scanType(func.return_type, &refs);
|
||||||
|
// Scan parameters
|
||||||
|
for (func.params) |param| {
|
||||||
|
try scanType(param.type_name, &refs);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.struct_decl => |struct_decl| {
|
||||||
|
// Scan field types
|
||||||
|
for (struct_decl.fields) |field| {
|
||||||
|
try scanType(field.type_name, &refs);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// opaque/enum don't reference other types
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return refs.toOwnedSlice();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scanType(type_str: []const u8, refs: *ArrayList(TypeReference)) !void {
|
||||||
|
// Extract base type from "?*const SDL_Type" → "SDL_Type"
|
||||||
|
const base_type = extractBaseType(type_str);
|
||||||
|
if (isSDLType(base_type)) {
|
||||||
|
try refs.append(.{ .name = base_type, .source_location = type_str });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key functions**:
|
||||||
|
- `extractBaseType()`: Strip pointers, const, optional from type string
|
||||||
|
- `isSDLType()`: Check if starts with "SDL_" or is known SDL type
|
||||||
|
- Handle edge cases: arrays, function pointers (skip for now)
|
||||||
|
|
||||||
|
### Component 2: Defined Type Collector
|
||||||
|
|
||||||
|
**Purpose**: Track what types are already defined
|
||||||
|
|
||||||
|
```zig
|
||||||
|
pub fn collectDefinedTypes(decls: []const Declaration) StringHashMap(void) {
|
||||||
|
var defined = StringHashMap(void).init(allocator);
|
||||||
|
|
||||||
|
for (decls) |decl| {
|
||||||
|
const type_name = switch (decl) {
|
||||||
|
.opaque_type => |o| o.name,
|
||||||
|
.enum_decl => |e| e.name,
|
||||||
|
.struct_decl => |s| s.name,
|
||||||
|
.flags_decl => |f| f.name,
|
||||||
|
.function_decl => continue, // Functions don't define types
|
||||||
|
};
|
||||||
|
try defined.put(type_name, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
return defined;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Component 3: Include Header Parser
|
||||||
|
|
||||||
|
**Purpose**: Extract #include directives
|
||||||
|
|
||||||
|
```zig
|
||||||
|
pub fn parseIncludes(source: []const u8) ![]const []const u8 {
|
||||||
|
var includes = ArrayList([]const u8).init(allocator);
|
||||||
|
|
||||||
|
var lines = std.mem.split(u8, source, "\n");
|
||||||
|
while (lines.next()) |line| {
|
||||||
|
// Match: #include <SDL3/SDL_something.h>
|
||||||
|
if (std.mem.indexOf(u8, line, "#include <SDL3/")) |start| {
|
||||||
|
const after_open = start + "#include <SDL3/".len;
|
||||||
|
if (std.mem.indexOf(u8, line[after_open..], ">")) |end| {
|
||||||
|
const header_name = line[after_open..][0..end];
|
||||||
|
try includes.append(try allocator.dupe(u8, header_name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return includes.toOwnedSlice();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Component 4: Selective Type Extractor
|
||||||
|
|
||||||
|
**Purpose**: Find specific type in a header
|
||||||
|
|
||||||
|
```zig
|
||||||
|
pub fn extractTypeFromHeader(
|
||||||
|
allocator: Allocator,
|
||||||
|
header_source: []const u8,
|
||||||
|
type_name: []const u8, // e.g., "SDL_Rect"
|
||||||
|
) !?Declaration {
|
||||||
|
// Parse the header
|
||||||
|
var scanner = Scanner.init(allocator, header_source);
|
||||||
|
const all_decls = try scanner.scan();
|
||||||
|
defer scanner.deinit();
|
||||||
|
|
||||||
|
// Find matching declaration
|
||||||
|
for (all_decls) |decl| {
|
||||||
|
const decl_name = switch (decl) {
|
||||||
|
.opaque_type => |o| o.name,
|
||||||
|
.enum_decl => |e| e.name,
|
||||||
|
.struct_decl => |s| s.name,
|
||||||
|
.flags_decl => |f| f.name,
|
||||||
|
else => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (std.mem.eql(u8, decl_name, type_name)) {
|
||||||
|
return try decl.clone(allocator); // Deep copy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null; // Not found
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Component 5: Main Integration
|
||||||
|
|
||||||
|
**Location**: Modify `src/parser.zig` main function
|
||||||
|
|
||||||
|
```zig
|
||||||
|
pub fn main() !void {
|
||||||
|
// ... existing setup ...
|
||||||
|
|
||||||
|
// 1. Parse primary header (existing)
|
||||||
|
var scanner = Scanner.init(allocator, source);
|
||||||
|
const primary_decls = try scanner.scan();
|
||||||
|
|
||||||
|
// 2. Identify missing types (NEW)
|
||||||
|
const references = try scanTypeReferences(primary_decls);
|
||||||
|
const defined = collectDefinedTypes(primary_decls);
|
||||||
|
|
||||||
|
var missing = ArrayList([]const u8).init(allocator);
|
||||||
|
for (references) |ref| {
|
||||||
|
if (!defined.contains(ref.name)) {
|
||||||
|
try missing.append(ref.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Extract missing types from dependencies (NEW)
|
||||||
|
var dependency_decls = ArrayList(Declaration).init(allocator);
|
||||||
|
|
||||||
|
if (missing.items.len > 0) {
|
||||||
|
const includes = try parseIncludes(source);
|
||||||
|
const header_dir = std.fs.path.dirname(header_path) orelse ".";
|
||||||
|
|
||||||
|
for (missing.items) |missing_type| {
|
||||||
|
var found = false;
|
||||||
|
for (includes) |include| {
|
||||||
|
const dep_path = try std.fs.path.join(
|
||||||
|
allocator,
|
||||||
|
&[_][]const u8{ header_dir, include }
|
||||||
|
);
|
||||||
|
defer allocator.free(dep_path);
|
||||||
|
|
||||||
|
const dep_source = std.fs.cwd().readFileAlloc(
|
||||||
|
allocator,
|
||||||
|
dep_path,
|
||||||
|
10 * 1024 * 1024
|
||||||
|
) catch continue;
|
||||||
|
defer allocator.free(dep_source);
|
||||||
|
|
||||||
|
if (try extractTypeFromHeader(allocator, dep_source, missing_type)) |decl| {
|
||||||
|
try dependency_decls.append(decl);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
std.debug.print(
|
||||||
|
"Warning: Could not find definition for type: {s}\n",
|
||||||
|
.{missing_type}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Combine declarations
|
||||||
|
var all_decls = ArrayList(Declaration).init(allocator);
|
||||||
|
try all_decls.appendSlice(dependency_decls.items); // Dependencies first!
|
||||||
|
try all_decls.appendSlice(primary_decls);
|
||||||
|
|
||||||
|
// 5. Generate code (existing, but with all declarations)
|
||||||
|
const output = try codegen.generate(allocator, all_decls.items);
|
||||||
|
|
||||||
|
// ... rest of existing code ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Key Implementation Decisions
|
||||||
|
|
||||||
|
1. **Dependencies go FIRST** in output
|
||||||
|
- Ensures types are defined before use
|
||||||
|
- More logical reading order
|
||||||
|
|
||||||
|
2. **Deep copy declarations**
|
||||||
|
- Avoid lifetime issues with parsed headers
|
||||||
|
- Each declaration owns its strings
|
||||||
|
|
||||||
|
3. **Warning for missing types**
|
||||||
|
- Don't fail build, just warn
|
||||||
|
- Allows gradual improvement
|
||||||
|
|
||||||
|
4. **Skip function pointers/unions**
|
||||||
|
- Add TODO comments
|
||||||
|
- Focus on common cases first
|
||||||
|
|
||||||
|
5. **Cache parsed headers**
|
||||||
|
- Parse each dependency header once
|
||||||
|
- Extract multiple types from same parse
|
||||||
|
|
||||||
|
## Testing Approach
|
||||||
|
|
||||||
|
### Unit Tests
|
||||||
|
|
||||||
|
```zig
|
||||||
|
test "scanTypeReferences finds SDL types" {
|
||||||
|
const decls = [_]Declaration{
|
||||||
|
.{ .function_decl = .{
|
||||||
|
.name = "test",
|
||||||
|
.return_type = "?*SDL_Window",
|
||||||
|
.params = &[_]Param{
|
||||||
|
.{ .name = "rect", .type_name = "*const SDL_Rect" },
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
};
|
||||||
|
|
||||||
|
const refs = try scanTypeReferences(&decls);
|
||||||
|
try testing.expect(refs.len == 2);
|
||||||
|
try testing.expectEqualStrings("SDL_Window", refs[0].name);
|
||||||
|
try testing.expectEqualStrings("SDL_Rect", refs[1].name);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "collectDefinedTypes tracks declarations" {
|
||||||
|
const decls = [_]Declaration{
|
||||||
|
.{ .opaque_type = .{ .name = "SDL_Device" }},
|
||||||
|
.{ .struct_decl = .{ .name = "SDL_Info", .fields = &[_]Field{} }},
|
||||||
|
};
|
||||||
|
|
||||||
|
const defined = collectDefinedTypes(&decls);
|
||||||
|
try testing.expect(defined.contains("SDL_Device"));
|
||||||
|
try testing.expect(defined.contains("SDL_Info"));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Integration Test
|
||||||
|
|
||||||
|
```zig
|
||||||
|
test "full dependency resolution with SDL_gpu.h" {
|
||||||
|
const source = try std.fs.cwd().readFileAlloc(
|
||||||
|
testing.allocator,
|
||||||
|
"SDL/include/SDL3/SDL_gpu.h",
|
||||||
|
10 * 1024 * 1024
|
||||||
|
);
|
||||||
|
defer testing.allocator.free(source);
|
||||||
|
|
||||||
|
// Parse and resolve
|
||||||
|
const output = try parseWithDependencies(testing.allocator, source, "SDL/include/SDL3");
|
||||||
|
defer testing.allocator.free(output);
|
||||||
|
|
||||||
|
// Verify missing types are present
|
||||||
|
try testing.expect(std.mem.indexOf(u8, output, "pub const Window = opaque") != null);
|
||||||
|
try testing.expect(std.mem.indexOf(u8, output, "pub const Rect = extern struct") != null);
|
||||||
|
try testing.expect(std.mem.indexOf(u8, output, "pub const FColor = extern struct") != null);
|
||||||
|
|
||||||
|
// Verify it compiles
|
||||||
|
var ast = try std.zig.Ast.parse(testing.allocator, output, .zig);
|
||||||
|
defer ast.deinit(testing.allocator);
|
||||||
|
try testing.expect(ast.errors.len == 0);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Validation Steps
|
||||||
|
|
||||||
|
1. **Remove manual definitions** from mock_test.zig:
|
||||||
|
```diff
|
||||||
|
- pub const Window = opaque {};
|
||||||
|
- pub const Rect = extern struct { ... };
|
||||||
|
- pub const FColor = extern struct { ... };
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Import generated file** directly:
|
||||||
|
```zig
|
||||||
|
const gpu = @import("../../zig-out/gpu_test.zig");
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Run tests**:
|
||||||
|
```bash
|
||||||
|
zig build test-mocks # Should still pass!
|
||||||
|
```
|
||||||
|
|
||||||
|
## Success Metrics
|
||||||
|
|
||||||
|
✅ `scanTypeReferences` finds 30+ type references in SDL_gpu.h
|
||||||
|
✅ `collectDefinedTypes` tracks 169 defined types
|
||||||
|
✅ Missing types: Window, Rect, FColor, FlipMode, PropertiesID detected
|
||||||
|
✅ All 5 missing types extracted from dependency headers
|
||||||
|
✅ Generated code compiles without manual definitions
|
||||||
|
✅ All 11 tests pass
|
||||||
|
✅ Build time increase < 1 second
|
||||||
|
|
||||||
|
## Rollout Plan
|
||||||
|
|
||||||
|
1. **Day 1**: Implement Components 1-2 (type scanning/collecting)
|
||||||
|
2. **Day 2**: Implement Components 3-4 (include parsing/type extraction)
|
||||||
|
3. **Day 3**: Integrate into main, test with SDL_gpu.h
|
||||||
|
4. **Day 4**: Refine, handle edge cases, update tests
|
||||||
|
5. **Day 5**: Documentation, final validation
|
||||||
|
|
||||||
|
## Risks & Mitigation
|
||||||
|
|
||||||
|
| Risk | Mitigation |
|
||||||
|
|------|-----------|
|
||||||
|
| Can't find header files | Require header directory as input |
|
||||||
|
| Type not in any header | Emit warning, generate placeholder |
|
||||||
|
| Parsing dependency fails | Catch error, continue with other headers |
|
||||||
|
| Performance (parsing multiple headers) | Cache parsed headers, parse once |
|
||||||
|
| Circular dependencies | Not an issue - all types in one file |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
This plan is **ready to implement**. Each component is well-defined with clear inputs/outputs, error handling, and test cases.
|
||||||
|
|
||||||
|
**File**: `src/type_collector.zig`
|
||||||
|
|
||||||
|
```zig
|
||||||
|
const TypeCollector = struct {
|
||||||
|
defined_types: StringHashMap(void), // Types defined in primary header
|
||||||
|
referenced_types: StringHashMap(void), // Types used in signatures
|
||||||
|
|
||||||
|
pub fn collectFromDeclarations(decls: []Declaration) TypeCollector;
|
||||||
|
pub fn getMissingTypes() []const []const u8;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
**Tasks**:
|
||||||
|
- Scan all declarations for type definitions (opaque, enum, struct, flags)
|
||||||
|
- Scan all function signatures for type references
|
||||||
|
- Return set difference: referenced - defined
|
||||||
|
|
||||||
|
### Phase 2: Include Directive Parsing (1 hour)
|
||||||
|
|
||||||
|
**File**: `src/patterns.zig` (extend existing)
|
||||||
|
|
||||||
|
```zig
|
||||||
|
pub fn parseIncludes(source: []const u8) ![]const []const u8 {
|
||||||
|
// Find all #include <SDL3/header.h> directives
|
||||||
|
// Return list of header filenames
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Tasks**:
|
||||||
|
- Add regex/pattern for `#include <SDL3/...>`
|
||||||
|
- Extract header filename from directive
|
||||||
|
- Return list of included headers
|
||||||
|
|
||||||
|
### Phase 3: Selective Type Extraction (2-3 hours)
|
||||||
|
|
||||||
|
**File**: `src/type_extractor.zig`
|
||||||
|
|
||||||
|
```zig
|
||||||
|
pub fn extractType(
|
||||||
|
allocator: Allocator,
|
||||||
|
header_source: []const u8,
|
||||||
|
type_name: []const u8, // e.g., "SDL_Rect"
|
||||||
|
) !?Declaration {
|
||||||
|
// Parse header looking for specific type
|
||||||
|
// Return the declaration if found
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extractTypes(
|
||||||
|
allocator: Allocator,
|
||||||
|
header_paths: []const []const u8,
|
||||||
|
missing_types: []const []const u8,
|
||||||
|
) ![]Declaration {
|
||||||
|
// For each missing type:
|
||||||
|
// For each header:
|
||||||
|
// Try to extract the type
|
||||||
|
// If found, add to results
|
||||||
|
// Return all found declarations
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Tasks**:
|
||||||
|
- Reuse existing Scanner but filter by type name
|
||||||
|
- Handle opaque types: `typedef struct SDL_Type SDL_Type;`
|
||||||
|
- Handle structs: `typedef struct { ... } SDL_Type;`
|
||||||
|
- Handle enums: `typedef enum { ... } SDL_Type;`
|
||||||
|
- Handle simple typedefs: `typedef uint32_t SDL_Type;`
|
||||||
|
|
||||||
|
### Phase 4: Integration (1-2 hours)
|
||||||
|
|
||||||
|
**File**: `src/parser.zig` (modify main function)
|
||||||
|
|
||||||
|
```zig
|
||||||
|
pub fn main() !void {
|
||||||
|
// 1. Parse primary header
|
||||||
|
var scanner = Scanner.init(allocator, source);
|
||||||
|
const decls = try scanner.scan();
|
||||||
|
|
||||||
|
// 2. Collect missing types
|
||||||
|
const collector = TypeCollector.collectFromDeclarations(decls);
|
||||||
|
const missing_types = try collector.getMissingTypes(allocator);
|
||||||
|
|
||||||
|
// 3. Parse included headers for missing types
|
||||||
|
const includes = try parseIncludes(source);
|
||||||
|
const header_dir = getHeaderDirectory(header_path);
|
||||||
|
const dependency_decls = try extractTypes(
|
||||||
|
allocator,
|
||||||
|
header_dir,
|
||||||
|
includes,
|
||||||
|
missing_types
|
||||||
|
);
|
||||||
|
|
||||||
|
// 4. Generate code with dependencies appended
|
||||||
|
var all_decls = std.ArrayList(Declaration).init(allocator);
|
||||||
|
try all_decls.appendSlice(decls);
|
||||||
|
try all_decls.appendSlice(dependency_decls);
|
||||||
|
|
||||||
|
const output = try codegen.generate(allocator, all_decls.items);
|
||||||
|
|
||||||
|
// 5. Write output
|
||||||
|
try writeOutput(output_file, output);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Tasks**:
|
||||||
|
- Wire together all components
|
||||||
|
- Handle header path resolution
|
||||||
|
- Add comment separators for dependencies
|
||||||
|
- Update error handling
|
||||||
|
|
||||||
|
### Phase 5: Code Generation Enhancement (1 hour)
|
||||||
|
|
||||||
|
**File**: `src/codegen.zig` (modify)
|
||||||
|
|
||||||
|
Add dependency section:
|
||||||
|
```zig
|
||||||
|
fn generate() ![]const u8 {
|
||||||
|
try output.appendSlice("pub const c = @import(\"c.zig\").c;\n\n");
|
||||||
|
|
||||||
|
// Add comment if we have dependencies
|
||||||
|
if (has_dependency_decls) {
|
||||||
|
try output.appendSlice(
|
||||||
|
\\// Dependencies from included headers
|
||||||
|
\\// These types are referenced by the primary header
|
||||||
|
\\
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate all declarations (primary + dependencies)
|
||||||
|
for (decls) |decl| {
|
||||||
|
try generateDeclaration(decl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Tasks**:
|
||||||
|
- Add dependency comment section
|
||||||
|
- Mark which declarations are dependencies (optional)
|
||||||
|
- Ensure proper ordering (dependencies before usage)
|
||||||
|
|
||||||
|
## Example Output
|
||||||
|
|
||||||
|
```zig
|
||||||
|
pub const c = @import("c.zig").c;
|
||||||
|
|
||||||
|
// Dependencies from included headers
|
||||||
|
// These types are referenced by SDL_gpu.h
|
||||||
|
|
||||||
|
// From SDL_rect.h
|
||||||
|
pub const Rect = extern struct {
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
w: i32,
|
||||||
|
h: i32,
|
||||||
|
};
|
||||||
|
|
||||||
|
// From SDL_pixels.h
|
||||||
|
pub const FColor = extern struct {
|
||||||
|
r: f32,
|
||||||
|
g: f32,
|
||||||
|
b: f32,
|
||||||
|
a: f32,
|
||||||
|
};
|
||||||
|
|
||||||
|
// From SDL_video.h
|
||||||
|
pub const Window = opaque {};
|
||||||
|
|
||||||
|
// From SDL_properties.h
|
||||||
|
pub const PropertiesID = u32;
|
||||||
|
|
||||||
|
// SDL_gpu.h declarations
|
||||||
|
pub const GPUDevice = opaque {
|
||||||
|
pub inline fn windowSupportsGPUSwapchainComposition(
|
||||||
|
gpudevice: *GPUDevice,
|
||||||
|
window: ?*Window, // ✅ Now defined!
|
||||||
|
swapchain_composition: GPUSwapchainComposition
|
||||||
|
) bool { ... }
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const GPURenderPass = opaque {
|
||||||
|
pub inline fn setGPUScissor(
|
||||||
|
gpurenderpass: *GPURenderPass,
|
||||||
|
scissor: *const Rect // ✅ Now defined!
|
||||||
|
) void { ... }
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Edge Cases
|
||||||
|
|
||||||
|
1. **Type not found in any header**
|
||||||
|
- Emit warning
|
||||||
|
- Generate placeholder: `pub const TypeName = opaque {};`
|
||||||
|
|
||||||
|
2. **Circular dependencies**
|
||||||
|
- Not an issue - all types in one file
|
||||||
|
- Zig allows forward references
|
||||||
|
|
||||||
|
3. **Multiple definitions**
|
||||||
|
- Keep first definition found
|
||||||
|
- Zig will error if layouts differ (good!)
|
||||||
|
|
||||||
|
4. **Typedef chains**
|
||||||
|
- `typedef SDL_Type1 Type2;`
|
||||||
|
- Resolve transitively or use Zig's type alias
|
||||||
|
|
||||||
|
5. **Complex types**
|
||||||
|
- Function pointers: Skip for now, add TODO comment
|
||||||
|
- Unions: Skip for now, add TODO comment
|
||||||
|
- Nested structs: Should work fine
|
||||||
|
|
||||||
|
## Testing Strategy
|
||||||
|
|
||||||
|
1. **Unit tests** for each component:
|
||||||
|
- TypeCollector: Test with known declarations
|
||||||
|
- parseIncludes: Test with sample headers
|
||||||
|
- extractType: Test finding types in headers
|
||||||
|
|
||||||
|
2. **Integration test**:
|
||||||
|
- Parse SDL_gpu.h
|
||||||
|
- Verify missing types are detected
|
||||||
|
- Verify dependencies are extracted
|
||||||
|
- Verify output compiles
|
||||||
|
|
||||||
|
3. **Validation**:
|
||||||
|
- Remove manual type definitions from mock_test.zig
|
||||||
|
- Import actual generated file
|
||||||
|
- All tests should still pass
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
|
||||||
|
✅ Parser detects 5 missing types from SDL_gpu.h
|
||||||
|
✅ Parser extracts types from dependency headers
|
||||||
|
✅ Generated code compiles standalone
|
||||||
|
✅ All tests pass without manual type definitions
|
||||||
|
✅ Single output file contains all needed types
|
||||||
|
|
||||||
|
## Time Estimate
|
||||||
|
|
||||||
|
- Phase 1 (Type Collection): 1-2 hours
|
||||||
|
- Phase 2 (Include Parsing): 1 hour
|
||||||
|
- Phase 3 (Type Extraction): 2-3 hours
|
||||||
|
- Phase 4 (Integration): 1-2 hours
|
||||||
|
- Phase 5 (Code Gen Enhancement): 1 hour
|
||||||
|
- Testing & Refinement: 2 hours
|
||||||
|
|
||||||
|
**Total: 8-11 hours**
|
||||||
|
|
||||||
|
## Advantages of This Approach
|
||||||
|
|
||||||
|
1. **Simple**: Single output file, no module management
|
||||||
|
2. **Fast**: Only parse dependency headers when needed
|
||||||
|
3. **Minimal**: Only extract required types
|
||||||
|
4. **Robust**: Zig handles duplicate definitions
|
||||||
|
5. **Maintainable**: Clear separation in output
|
||||||
|
|
||||||
|
## Open Questions
|
||||||
|
|
||||||
|
1. Should we cache parsed dependency headers?
|
||||||
|
- **Answer**: Yes, parse once, extract many types
|
||||||
|
|
||||||
|
2. How to handle nested dependencies (Type A needs Type B)?
|
||||||
|
- **Answer**: Recursive extraction, track visited types
|
||||||
|
|
||||||
|
3. Should dependencies go at top or bottom of file?
|
||||||
|
- **Answer**: Top, before primary declarations use them
|
||||||
|
|
||||||
|
4. What about #define constants?
|
||||||
|
- **Answer**: Skip for now, out of scope
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. Implement TypeCollector
|
||||||
|
2. Implement include parsing
|
||||||
|
3. Implement type extraction
|
||||||
|
4. Wire together in main
|
||||||
|
5. Test with SDL_gpu.h
|
||||||
|
6. Update documentation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Ready to implement? This plan provides:
|
||||||
|
- Clear phases with time estimates
|
||||||
|
- Concrete code examples
|
||||||
|
- Handles edge cases
|
||||||
|
- Simple single-file output
|
||||||
|
- Full testing strategy
|
||||||
Loading…
Reference in New Issue