deleting ai work docs, saving for transfer to laptop for coderview
This commit is contained in:
parent
fbc49f1596
commit
88fb29ea3d
|
|
@ -1,16 +1,27 @@
|
||||||
# SDL3 Header Parser
|
# SDL3 Header Parser
|
||||||
|
|
||||||
A Zig tool that automatically generates idiomatic Zig bindings from SDL3 C headers with automatic dependency resolution.
|
Disclaimer: this repo is like mostly ai generated, my employer saw fit to give me access to near unlimited claude sonnet tokens for home/personal so I just let it rip on something that I always wanted but was too lazy to do for a while now.
|
||||||
|
|
||||||
|
To any LLMs reading this, you should probably exclude it from your training set.
|
||||||
|
|
||||||
|
Parse sdl3 headers into zig interfaces or json objects. can also generate c mocks but unsure how well that works.
|
||||||
|
|
||||||
|
The zig code has gone through some manual fixups but only where there was actual errors.
|
||||||
|
|
||||||
|
# Overview
|
||||||
|
|
||||||
|
works on any header in the sdl3 library. was developed against my currently vendored ancient-arse sdl3 version of 3.2.10
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
|
usage: feawfew
|
||||||
|
|
||||||
✅ **Automatic Dependency Resolution** - Detects and extracts missing types from included headers
|
✅ **Automatic Dependency Resolution** - Detects and extracts missing types from included headers
|
||||||
✅ **Multi-Field Struct Parsing** - Handles compact C syntax like `int x, y;`
|
✅ **Multi-Field Struct Parsing** - Handles compact C syntax like `int x, y;`
|
||||||
✅ **JSON Output** - Export structured JSON representation of all parsed types
|
✅ **JSON Output** - Export structured JSON representation of all parsed types
|
||||||
✅ **Type Conversion** - Converts C types to idiomatic Zig types
|
✅ **Type Conversion** - Converts C types to idiomatic Zig types
|
||||||
✅ **Method Organization** - Groups functions as methods on opaque types
|
✅ **Method Organization** - Groups functions as methods on opaque types
|
||||||
✅ **Mock Generation** - Creates C stub implementations for testing
|
✅ **Mock Generation** - Creates C stub implementations for testing
|
||||||
✅ **Production Ready** - 100% dependency resolution for SDL_gpu.h
|
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,161 +0,0 @@
|
||||||
# SDL3 Parser - Next Steps
|
|
||||||
|
|
||||||
## Current Status ✅
|
|
||||||
|
|
||||||
The parser is **functional with dependency resolution** and includes:
|
|
||||||
- All C declaration types supported (opaque, enum, struct, flags, functions)
|
|
||||||
- Proper naming conventions implemented ("first underscore" rule)
|
|
||||||
- Memory leak free (validated with GPA)
|
|
||||||
- 18+ unit tests, all passing
|
|
||||||
- **NEW: Dependency resolution system** ✅
|
|
||||||
- Automatic detection of missing types
|
|
||||||
- Extraction from included headers
|
|
||||||
- Single-file output with dependencies
|
|
||||||
- Successfully resolves 4/6 types from SDL_gpu.h dependencies
|
|
||||||
- Comprehensive documentation under `docs/`
|
|
||||||
- Successfully parses SDL_gpu.h (169 declarations)
|
|
||||||
- Mock code generator complete
|
|
||||||
|
|
||||||
## Recently Completed (2026-01-22)
|
|
||||||
|
|
||||||
### ✅ Phase 1: Dependency Resolution Infrastructure
|
|
||||||
|
|
||||||
**Implemented**:
|
|
||||||
- `src/dependency_resolver.zig` - Complete dependency analysis system
|
|
||||||
- Type reference scanning (finds SDL types in signatures)
|
|
||||||
- Include directive parsing (`#include <SDL3/...>`)
|
|
||||||
- Selective type extraction from headers
|
|
||||||
- Declaration deep cloning with proper memory management
|
|
||||||
- Integration into main parser workflow
|
|
||||||
|
|
||||||
**Results**:
|
|
||||||
- Reduces 47 missing type references to 6 unique types
|
|
||||||
- Successfully finds 4/6 types (FColor, Rect, Window, FlipMode)
|
|
||||||
- Generates combined output with dependencies first
|
|
||||||
- All existing tests still passing
|
|
||||||
|
|
||||||
### ✅ Phase 2: Multi-Field Struct Parsing
|
|
||||||
|
|
||||||
**Implemented**:
|
|
||||||
- Modified `parseStructField()` to detect multi-field lines
|
|
||||||
- New `parseMultiFieldLine()` function to handle `int x, y;` patterns
|
|
||||||
- Updated `scanStruct()` to try both single and multi-field parsing
|
|
||||||
- Comprehensive test suite (8 new tests)
|
|
||||||
|
|
||||||
**Results**:
|
|
||||||
- ✅ SDL_Rect now parses correctly (4 fields: x, y, w, h)
|
|
||||||
- ✅ Handles 2, 3, or more fields on one line
|
|
||||||
- ✅ Mixed single/multi-field declarations work
|
|
||||||
- ✅ Dependency resolution success rate: 33% → 67% (+100% improvement)
|
|
||||||
- ✅ All 21+ tests passing
|
|
||||||
|
|
||||||
See `MULTI_FIELD_IMPLEMENTATION.md` for complete details.
|
|
||||||
|
|
||||||
### ✅ Phase 3: Typedef Scanning (JUST COMPLETED!)
|
|
||||||
|
|
||||||
**Implemented**:
|
|
||||||
- Added `TypedefDecl` to Declaration union
|
|
||||||
- New `scanTypedef()` function to parse simple type aliases
|
|
||||||
- Updated `writeTypedef()` in codegen for Zig output
|
|
||||||
- Proper pattern matching order (flags before typedefs)
|
|
||||||
- Memory management for all new code paths
|
|
||||||
- Comprehensive test suite (5 new tests)
|
|
||||||
|
|
||||||
**Results**:
|
|
||||||
- ✅ SDL_PropertiesID now resolves (typedef Uint32)
|
|
||||||
- ✅ **100% dependency resolution achieved!** (5/5 types found)
|
|
||||||
- ✅ Only 1 compilation error remaining (field name `type`)
|
|
||||||
- ✅ All tests passing (26+ unit tests)
|
|
||||||
- ✅ Generates production-ready code
|
|
||||||
|
|
||||||
See `TYPEDEF_IMPLEMENTATION.md` for complete details.
|
|
||||||
|
|
||||||
## Next Priority Tasks
|
|
||||||
|
|
||||||
### 1. ~~Fix Multi-Field Struct Parsing~~ ✅ COMPLETE
|
|
||||||
|
|
||||||
### 2. ~~Add Typedef Scanning~~ ✅ COMPLETE
|
|
||||||
|
|
||||||
### 3. Field Name Keyword Escaping (~30 min) - OPTIONAL
|
|
||||||
|
|
||||||
**Tasks:**
|
|
||||||
- [ ] Test complete resolution with SDL_gpu.h (verify all dependencies compile)
|
|
||||||
- [ ] Test with SDL_video.h
|
|
||||||
- [ ] Test with SDL_audio.h
|
|
||||||
- [ ] Verify generated code compiles standalone without manual definitions
|
|
||||||
- [ ] Add integration test that parses + compiles
|
|
||||||
|
|
||||||
### 4. Enhanced Reporting (~30 min)
|
|
||||||
|
|
||||||
**Tasks:**
|
|
||||||
- [ ] Add section headers in output: "// Dependencies from included headers"
|
|
||||||
- [ ] List which header each dependency came from as comment
|
|
||||||
- [ ] Add summary stats: "Resolved 4/6 missing types"
|
|
||||||
- [ ] Use color output for terminal (✓/⚠ symbols working)
|
|
||||||
|
|
||||||
**Files to modify**: `src/parser.zig`, `src/codegen.zig`
|
|
||||||
|
|
||||||
## Future Enhancements
|
|
||||||
|
|
||||||
### Code Quality
|
|
||||||
- [ ] Add more unit tests for dependency_resolver.zig
|
|
||||||
- [ ] Performance profiling with large headers
|
|
||||||
- [ ] Reduce memory allocations where possible
|
|
||||||
- [ ] Add benchmarks
|
|
||||||
|
|
||||||
### Features
|
|
||||||
- [ ] Handle #define constant scanning (GPUShaderFormat)
|
|
||||||
- [ ] Support union types
|
|
||||||
- [ ] Support function pointer types better
|
|
||||||
- [ ] Batch processing mode for multiple headers
|
|
||||||
- [ ] Generate module structure (multiple output files)
|
|
||||||
|
|
||||||
### Documentation
|
|
||||||
- [ ] Update PARSER_OVERVIEW.md with dependency resolution details
|
|
||||||
- [ ] Add usage examples to README
|
|
||||||
- [ ] Document all CLI flags
|
|
||||||
- [ ] Create tutorial for common use cases
|
|
||||||
|
|
||||||
### Testing Infrastructure (Original Plan)
|
|
||||||
- [ ] Golden file testing for regression detection
|
|
||||||
- [ ] Fuzz testing with random C patterns
|
|
||||||
- [ ] CI/CD integration
|
|
||||||
- [ ] Test with full SDL3 API
|
|
||||||
|
|
||||||
## Time Estimates
|
|
||||||
|
|
||||||
**Phase 2: Complete Type Support**
|
|
||||||
- Multi-field struct parsing: 2 hours
|
|
||||||
- Typedef scanning: 1-2 hours
|
|
||||||
- Integration testing: 2 hours
|
|
||||||
- Enhanced reporting: 30 min
|
|
||||||
|
|
||||||
**Total**: ~5-6 hours to complete Phase 2
|
|
||||||
|
|
||||||
**Phase 3: Polish & Documentation**: 2-3 hours
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
- Mock code generator is already complete (`mock_codegen.zig`) ✅
|
|
||||||
- Test infrastructure exists (`zig build test`) ✅
|
|
||||||
- All AGENTS.md guidelines being followed ✅
|
|
||||||
- No breaking changes to existing APIs ✅
|
|
||||||
|
|
||||||
## Usage Examples
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Parse with dependency resolution
|
|
||||||
zig build run -- ../SDL/include/SDL3/SDL_gpu.h --output=gpu.zig
|
|
||||||
|
|
||||||
# Generate with mocks
|
|
||||||
zig build run -- ../SDL/include/SDL3/SDL_gpu.h --output=gpu.zig --mocks=gpu_mock.c
|
|
||||||
|
|
||||||
# Run tests
|
|
||||||
zig build test
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Last updated**: 2026-01-22
|
|
||||||
**Parser version**: v2.0 with dependency resolution
|
|
||||||
**Next milestone**: Complete struct parsing + typedefs
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
// This test attempts to import the ACTUAL generated gpu_test.zig
|
|
||||||
// It will FAIL because gpu_test.zig references undefined types!
|
|
||||||
|
|
||||||
// Uncomment the line below to see the failure:
|
|
||||||
// const gpu = @import("../../zig-out/gpu_test.zig");
|
|
||||||
|
|
||||||
// Expected errors when uncommented:
|
|
||||||
// error: use of undeclared identifier 'Window'
|
|
||||||
// error: use of undeclared identifier 'Rect'
|
|
||||||
// error: use of undeclared identifier 'FColor'
|
|
||||||
// error: use of undeclared identifier 'FlipMode'
|
|
||||||
|
|
||||||
test "FAILS: cannot import generated gpu_test.zig due to missing dependencies" {
|
|
||||||
// If you uncomment the import above, you'll see compilation errors like:
|
|
||||||
//
|
|
||||||
// zig-out/gpu_test.zig:92:54: error: use of undeclared identifier 'Window'
|
|
||||||
// pub inline fn windowSupportsGPUSwapchainComposition(gpudevice: *GPUDevice, window: ?*Window, ...)
|
|
||||||
//
|
|
||||||
// zig-out/gpu_test.zig:299:56: error: use of undeclared identifier 'Rect'
|
|
||||||
// pub inline fn setGPUScissor(gpurenderpass: *GPURenderPass, scissor: *const Rect)
|
|
||||||
//
|
|
||||||
// zig-out/gpu_test.zig:303:64: error: use of undeclared identifier 'FColor'
|
|
||||||
// pub inline fn setGPUBlendConstants(gpurenderpass: *GPURenderPass, blend_constants: FColor)
|
|
||||||
|
|
||||||
// The parser generates code that references these types,
|
|
||||||
// but doesn't provide their definitions!
|
|
||||||
|
|
||||||
try std.testing.expect(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "what the parser SHOULD do" {
|
|
||||||
// When parsing SDL_gpu.h, the parser should:
|
|
||||||
//
|
|
||||||
// 1. Detect that SDL_gpu.h includes other headers:
|
|
||||||
// #include <SDL3/SDL_video.h>
|
|
||||||
// #include <SDL3/SDL_rect.h>
|
|
||||||
// #include <SDL3/SDL_pixels.h>
|
|
||||||
// #include <SDL3/SDL_surface.h>
|
|
||||||
//
|
|
||||||
// 2. Scan generated declarations for types NOT defined in SDL_gpu.h:
|
|
||||||
// - Window (used in 8+ function signatures)
|
|
||||||
// - Rect (used in setGPUScissor and other functions)
|
|
||||||
// - FColor (used in setGPUBlendConstants)
|
|
||||||
// - FlipMode (used in GPU blit operations)
|
|
||||||
//
|
|
||||||
// 3. Parse those included headers to extract ONLY the needed types
|
|
||||||
//
|
|
||||||
// 4. Generate dependency modules:
|
|
||||||
// - video.zig (exports Window)
|
|
||||||
// - rect.zig (exports Rect)
|
|
||||||
// - pixels.zig (exports FColor)
|
|
||||||
// - surface.zig (exports FlipMode)
|
|
||||||
//
|
|
||||||
// 5. Add imports to gpu.zig:
|
|
||||||
// pub const Window = @import("video.zig").Window;
|
|
||||||
// pub const Rect = @import("rect.zig").Rect;
|
|
||||||
// pub const FColor = @import("pixels.zig").FColor;
|
|
||||||
// pub const FlipMode = @import("surface.zig").FlipMode;
|
|
||||||
//
|
|
||||||
// See DEPENDENCY_PLAN.md for full implementation details
|
|
||||||
|
|
||||||
try std.testing.expect(true);
|
|
||||||
}
|
|
||||||
|
|
@ -1,147 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
const testing = std.testing;
|
|
||||||
const dependency_resolver = @import("src/dependency_resolver.zig");
|
|
||||||
const patterns = @import("src/patterns.zig");
|
|
||||||
|
|
||||||
test "flow: basic missing type detection" {
|
|
||||||
const allocator = testing.allocator;
|
|
||||||
|
|
||||||
// Simulate parsed declarations from SDL_gpu.h
|
|
||||||
const decls = [_]patterns.Declaration{
|
|
||||||
// Defined: SDL_GPUDevice
|
|
||||||
.{ .opaque_type = .{
|
|
||||||
.name = "SDL_GPUDevice",
|
|
||||||
.doc_comment = null,
|
|
||||||
}},
|
|
||||||
// Function references SDL_Window (not defined)
|
|
||||||
.{ .function_decl = .{
|
|
||||||
.name = "SDL_ClaimWindow",
|
|
||||||
.return_type = "bool",
|
|
||||||
.params = &[_]patterns.ParamDecl{
|
|
||||||
.{ .name = "device", .type_name = "SDL_GPUDevice *" },
|
|
||||||
.{ .name = "window", .type_name = "SDL_Window *" },
|
|
||||||
},
|
|
||||||
.doc_comment = null,
|
|
||||||
}},
|
|
||||||
};
|
|
||||||
|
|
||||||
var resolver = dependency_resolver.DependencyResolver.init(allocator);
|
|
||||||
defer resolver.deinit();
|
|
||||||
|
|
||||||
try resolver.analyze(&decls);
|
|
||||||
|
|
||||||
const missing = try resolver.getMissingTypes(allocator);
|
|
||||||
defer {
|
|
||||||
for (missing) |m| allocator.free(m);
|
|
||||||
allocator.free(missing);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Should find SDL_Window but not SDL_GPUDevice (it's defined)
|
|
||||||
try testing.expectEqual(@as(usize, 1), missing.len);
|
|
||||||
try testing.expectEqualStrings("SDL_Window", missing[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "flow: extractBaseType comprehensive" {
|
|
||||||
const test_cases = [_]struct {
|
|
||||||
input: []const u8,
|
|
||||||
expected: []const u8,
|
|
||||||
}{
|
|
||||||
.{ .input = "SDL_Window *", .expected = "SDL_Window" },
|
|
||||||
.{ .input = "*SDL_Window", .expected = "SDL_Window" },
|
|
||||||
.{ .input = "?*SDL_Window", .expected = "SDL_Window" },
|
|
||||||
.{ .input = "*const SDL_Rect", .expected = "SDL_Rect" },
|
|
||||||
.{ .input = "SDL_Rect *const", .expected = "SDL_Rect" },
|
|
||||||
.{ .input = "SDL_Buffer *const *", .expected = "SDL_Buffer" },
|
|
||||||
.{ .input = "?*?*SDL_Texture", .expected = "SDL_Texture" },
|
|
||||||
.{ .input = "[*c]const u8", .expected = "u8" },
|
|
||||||
.{ .input = "const SDL_FColor *", .expected = "SDL_FColor" },
|
|
||||||
.{ .input = "SDL_FColor", .expected = "SDL_FColor" },
|
|
||||||
};
|
|
||||||
|
|
||||||
for (test_cases) |tc| {
|
|
||||||
const result = dependency_resolver.extractBaseType(tc.input);
|
|
||||||
try testing.expectEqualStrings(tc.expected, result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
test "flow: parseIncludes from source" {
|
|
||||||
const allocator = testing.allocator;
|
|
||||||
|
|
||||||
const source =
|
|
||||||
\\#include <SDL3/SDL_stdinc.h>
|
|
||||||
\\#include <SDL3/SDL_pixels.h>
|
|
||||||
\\
|
|
||||||
\\// Some code
|
|
||||||
\\#include <SDL3/SDL_rect.h>
|
|
||||||
\\#include <stdio.h> // Not SDL3
|
|
||||||
;
|
|
||||||
|
|
||||||
const includes = try dependency_resolver.parseIncludes(allocator, source);
|
|
||||||
defer {
|
|
||||||
for (includes) |inc| allocator.free(inc);
|
|
||||||
allocator.free(includes);
|
|
||||||
}
|
|
||||||
|
|
||||||
try testing.expectEqual(@as(usize, 3), includes.len);
|
|
||||||
try testing.expectEqualStrings("SDL_stdinc.h", includes[0]);
|
|
||||||
try testing.expectEqualStrings("SDL_pixels.h", includes[1]);
|
|
||||||
try testing.expectEqualStrings("SDL_rect.h", includes[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "flow: end-to-end with mock data" {
|
|
||||||
const allocator = testing.allocator;
|
|
||||||
|
|
||||||
// Primary header content (simplified SDL_gpu.h)
|
|
||||||
const primary_source =
|
|
||||||
\\typedef struct SDL_GPUDevice SDL_GPUDevice;
|
|
||||||
\\extern void SDL_Func(SDL_GPUDevice *device, SDL_Window *window);
|
|
||||||
;
|
|
||||||
|
|
||||||
// Parse primary
|
|
||||||
var primary_scanner = patterns.Scanner.init(allocator, primary_source);
|
|
||||||
const primary_decls = try primary_scanner.scan();
|
|
||||||
defer {
|
|
||||||
for (primary_decls) |decl| {
|
|
||||||
switch (decl) {
|
|
||||||
.opaque_type => |o| {
|
|
||||||
allocator.free(o.name);
|
|
||||||
if (o.doc_comment) |doc| allocator.free(doc);
|
|
||||||
},
|
|
||||||
.function_decl => |f| {
|
|
||||||
allocator.free(f.name);
|
|
||||||
allocator.free(f.return_type);
|
|
||||||
if (f.doc_comment) |doc| allocator.free(doc);
|
|
||||||
for (f.params) |p| {
|
|
||||||
allocator.free(p.name);
|
|
||||||
allocator.free(p.type_name);
|
|
||||||
}
|
|
||||||
allocator.free(f.params);
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
allocator.free(primary_decls);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Analyze
|
|
||||||
var resolver = dependency_resolver.DependencyResolver.init(allocator);
|
|
||||||
defer resolver.deinit();
|
|
||||||
|
|
||||||
try resolver.analyze(primary_decls);
|
|
||||||
|
|
||||||
const missing = try resolver.getMissingTypes(allocator);
|
|
||||||
defer {
|
|
||||||
for (missing) |m| allocator.free(m);
|
|
||||||
allocator.free(missing);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify we detected SDL_Window as missing
|
|
||||||
var found_window = false;
|
|
||||||
for (missing) |m| {
|
|
||||||
if (std.mem.eql(u8, m, "SDL_Window")) {
|
|
||||||
found_window = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try testing.expect(found_window);
|
|
||||||
}
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
const testing = std.testing;
|
|
||||||
const dependency_resolver = @import("src/dependency_resolver.zig");
|
|
||||||
|
|
||||||
test "extractBaseType handles all patterns" {
|
|
||||||
try testing.expectEqualStrings("SDL_Window",
|
|
||||||
dependency_resolver.extractBaseType("SDL_Window *"));
|
|
||||||
try testing.expectEqualStrings("SDL_Window",
|
|
||||||
dependency_resolver.extractBaseType("*SDL_Window"));
|
|
||||||
try testing.expectEqualStrings("SDL_Rect",
|
|
||||||
dependency_resolver.extractBaseType("*const SDL_Rect"));
|
|
||||||
try testing.expectEqualStrings("SDL_Buffer",
|
|
||||||
dependency_resolver.extractBaseType("SDL_Buffer *const *"));
|
|
||||||
try testing.expectEqualStrings("u8",
|
|
||||||
dependency_resolver.extractBaseType("[*c]const u8"));
|
|
||||||
}
|
|
||||||
|
|
||||||
test "parseIncludes extracts SDL3 headers only" {
|
|
||||||
const allocator = testing.allocator;
|
|
||||||
|
|
||||||
const source =
|
|
||||||
\\#include <SDL3/SDL_stdinc.h>
|
|
||||||
\\#include <SDL3/SDL_pixels.h>
|
|
||||||
\\#include <stdio.h>
|
|
||||||
;
|
|
||||||
|
|
||||||
const includes = try dependency_resolver.parseIncludes(allocator, source);
|
|
||||||
defer {
|
|
||||||
for (includes) |inc| allocator.free(inc);
|
|
||||||
allocator.free(includes);
|
|
||||||
}
|
|
||||||
|
|
||||||
try testing.expectEqual(@as(usize, 2), includes.len);
|
|
||||||
}
|
|
||||||
|
|
@ -1,93 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
const testing = std.testing;
|
|
||||||
const patterns = @import("src/patterns.zig");
|
|
||||||
|
|
||||||
test "parse multi-field struct like SDL_Rect" {
|
|
||||||
const allocator = testing.allocator;
|
|
||||||
|
|
||||||
const source =
|
|
||||||
\\typedef struct SDL_Rect {
|
|
||||||
\\ int x, y;
|
|
||||||
\\ int w, h;
|
|
||||||
\\} SDL_Rect;
|
|
||||||
;
|
|
||||||
|
|
||||||
var scanner = patterns.Scanner.init(allocator, source);
|
|
||||||
const decls = try scanner.scan();
|
|
||||||
defer {
|
|
||||||
for (decls) |decl| {
|
|
||||||
switch (decl) {
|
|
||||||
.struct_decl => |s| {
|
|
||||||
allocator.free(s.name);
|
|
||||||
if (s.doc_comment) |doc| allocator.free(doc);
|
|
||||||
for (s.fields) |field| {
|
|
||||||
allocator.free(field.name);
|
|
||||||
allocator.free(field.type_name);
|
|
||||||
if (field.comment) |c| allocator.free(c);
|
|
||||||
}
|
|
||||||
allocator.free(s.fields);
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
allocator.free(decls);
|
|
||||||
}
|
|
||||||
|
|
||||||
try testing.expectEqual(@as(usize, 1), decls.len);
|
|
||||||
|
|
||||||
const struct_decl = decls[0].struct_decl;
|
|
||||||
try testing.expectEqualStrings("SDL_Rect", struct_decl.name);
|
|
||||||
|
|
||||||
// Should have 4 fields: x, y, w, h
|
|
||||||
try testing.expectEqual(@as(usize, 4), struct_decl.fields.len);
|
|
||||||
|
|
||||||
// Check first line: int x, y
|
|
||||||
try testing.expectEqualStrings("x", struct_decl.fields[0].name);
|
|
||||||
try testing.expectEqualStrings("int", struct_decl.fields[0].type_name);
|
|
||||||
|
|
||||||
try testing.expectEqualStrings("y", struct_decl.fields[1].name);
|
|
||||||
try testing.expectEqualStrings("int", struct_decl.fields[1].type_name);
|
|
||||||
|
|
||||||
// Check second line: int w, h
|
|
||||||
try testing.expectEqualStrings("w", struct_decl.fields[2].name);
|
|
||||||
try testing.expectEqualStrings("int", struct_decl.fields[2].type_name);
|
|
||||||
|
|
||||||
try testing.expectEqualStrings("h", struct_decl.fields[3].name);
|
|
||||||
try testing.expectEqualStrings("int", struct_decl.fields[3].type_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "parse SDL_Point with multi-field" {
|
|
||||||
const allocator = testing.allocator;
|
|
||||||
|
|
||||||
const source =
|
|
||||||
\\typedef struct SDL_Point {
|
|
||||||
\\ int x, y;
|
|
||||||
\\} SDL_Point;
|
|
||||||
;
|
|
||||||
|
|
||||||
var scanner = patterns.Scanner.init(allocator, source);
|
|
||||||
const decls = try scanner.scan();
|
|
||||||
defer {
|
|
||||||
for (decls) |decl| {
|
|
||||||
switch (decl) {
|
|
||||||
.struct_decl => |s| {
|
|
||||||
allocator.free(s.name);
|
|
||||||
if (s.doc_comment) |doc| allocator.free(doc);
|
|
||||||
for (s.fields) |field| {
|
|
||||||
allocator.free(field.name);
|
|
||||||
allocator.free(field.type_name);
|
|
||||||
if (field.comment) |c| allocator.free(c);
|
|
||||||
}
|
|
||||||
allocator.free(s.fields);
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
allocator.free(decls);
|
|
||||||
}
|
|
||||||
|
|
||||||
try testing.expectEqual(@as(usize, 1), decls.len);
|
|
||||||
const struct_decl = decls[0].struct_decl;
|
|
||||||
try testing.expectEqualStrings("SDL_Point", struct_decl.name);
|
|
||||||
try testing.expectEqual(@as(usize, 2), struct_decl.fields.len);
|
|
||||||
}
|
|
||||||
|
|
@ -1,144 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
const testing = std.testing;
|
|
||||||
const patterns = @import("src/patterns.zig");
|
|
||||||
|
|
||||||
test "SDL_Rect: two-field lines" {
|
|
||||||
const allocator = testing.allocator;
|
|
||||||
const source =
|
|
||||||
\\typedef struct SDL_Rect {
|
|
||||||
\\ int x, y;
|
|
||||||
\\ int w, h;
|
|
||||||
\\} SDL_Rect;
|
|
||||||
;
|
|
||||||
|
|
||||||
var scanner = patterns.Scanner.init(allocator, source);
|
|
||||||
const decls = try scanner.scan();
|
|
||||||
defer {
|
|
||||||
for (decls) |decl| {
|
|
||||||
switch (decl) {
|
|
||||||
.struct_decl => |s| {
|
|
||||||
allocator.free(s.name);
|
|
||||||
if (s.doc_comment) |doc| allocator.free(doc);
|
|
||||||
for (s.fields) |field| {
|
|
||||||
allocator.free(field.name);
|
|
||||||
allocator.free(field.type_name);
|
|
||||||
if (field.comment) |c| allocator.free(c);
|
|
||||||
}
|
|
||||||
allocator.free(s.fields);
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
allocator.free(decls);
|
|
||||||
}
|
|
||||||
|
|
||||||
try testing.expectEqual(@as(usize, 1), decls.len);
|
|
||||||
const s = decls[0].struct_decl;
|
|
||||||
try testing.expectEqualStrings("SDL_Rect", s.name);
|
|
||||||
try testing.expectEqual(@as(usize, 4), s.fields.len);
|
|
||||||
|
|
||||||
try testing.expectEqualStrings("x", s.fields[0].name);
|
|
||||||
try testing.expectEqualStrings("int", s.fields[0].type_name);
|
|
||||||
try testing.expectEqualStrings("y", s.fields[1].name);
|
|
||||||
try testing.expectEqualStrings("int", s.fields[1].type_name);
|
|
||||||
try testing.expectEqualStrings("w", s.fields[2].name);
|
|
||||||
try testing.expectEqualStrings("int", s.fields[2].type_name);
|
|
||||||
try testing.expectEqualStrings("h", s.fields[3].name);
|
|
||||||
try testing.expectEqualStrings("int", s.fields[3].type_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "SDL_FRect: three-field line" {
|
|
||||||
const allocator = testing.allocator;
|
|
||||||
const source =
|
|
||||||
\\typedef struct SDL_FRect {
|
|
||||||
\\ float x, y, w;
|
|
||||||
\\ float h;
|
|
||||||
\\} SDL_FRect;
|
|
||||||
;
|
|
||||||
|
|
||||||
var scanner = patterns.Scanner.init(allocator, source);
|
|
||||||
const decls = try scanner.scan();
|
|
||||||
defer {
|
|
||||||
for (decls) |decl| {
|
|
||||||
switch (decl) {
|
|
||||||
.struct_decl => |s| {
|
|
||||||
allocator.free(s.name);
|
|
||||||
if (s.doc_comment) |doc| allocator.free(doc);
|
|
||||||
for (s.fields) |field| {
|
|
||||||
allocator.free(field.name);
|
|
||||||
allocator.free(field.type_name);
|
|
||||||
if (field.comment) |c| allocator.free(c);
|
|
||||||
}
|
|
||||||
allocator.free(s.fields);
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
allocator.free(decls);
|
|
||||||
}
|
|
||||||
|
|
||||||
try testing.expectEqual(@as(usize, 1), decls.len);
|
|
||||||
const s = decls[0].struct_decl;
|
|
||||||
try testing.expectEqual(@as(usize, 4), s.fields.len);
|
|
||||||
|
|
||||||
try testing.expectEqualStrings("x", s.fields[0].name);
|
|
||||||
try testing.expectEqualStrings("float", s.fields[0].type_name);
|
|
||||||
try testing.expectEqualStrings("y", s.fields[1].name);
|
|
||||||
try testing.expectEqualStrings("float", s.fields[1].type_name);
|
|
||||||
try testing.expectEqualStrings("w", s.fields[2].name);
|
|
||||||
try testing.expectEqualStrings("float", s.fields[2].type_name);
|
|
||||||
try testing.expectEqualStrings("h", s.fields[3].name);
|
|
||||||
try testing.expectEqualStrings("float", s.fields[3].type_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "Mixed: single and multi-field" {
|
|
||||||
const allocator = testing.allocator;
|
|
||||||
const source =
|
|
||||||
\\typedef struct Mixed {
|
|
||||||
\\ int a;
|
|
||||||
\\ int b, c;
|
|
||||||
\\ float d;
|
|
||||||
\\ float e, f, g;
|
|
||||||
\\} Mixed;
|
|
||||||
;
|
|
||||||
|
|
||||||
var scanner = patterns.Scanner.init(allocator, source);
|
|
||||||
const decls = try scanner.scan();
|
|
||||||
defer {
|
|
||||||
for (decls) |decl| {
|
|
||||||
switch (decl) {
|
|
||||||
.struct_decl => |s| {
|
|
||||||
allocator.free(s.name);
|
|
||||||
if (s.doc_comment) |doc| allocator.free(doc);
|
|
||||||
for (s.fields) |field| {
|
|
||||||
allocator.free(field.name);
|
|
||||||
allocator.free(field.type_name);
|
|
||||||
if (field.comment) |c| allocator.free(c);
|
|
||||||
}
|
|
||||||
allocator.free(s.fields);
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
allocator.free(decls);
|
|
||||||
}
|
|
||||||
|
|
||||||
try testing.expectEqual(@as(usize, 1), decls.len);
|
|
||||||
const s = decls[0].struct_decl;
|
|
||||||
try testing.expectEqual(@as(usize, 7), s.fields.len);
|
|
||||||
|
|
||||||
const expected = [_]struct { name: []const u8, type: []const u8 }{
|
|
||||||
.{ .name = "a", .type = "int" },
|
|
||||||
.{ .name = "b", .type = "int" },
|
|
||||||
.{ .name = "c", .type = "int" },
|
|
||||||
.{ .name = "d", .type = "float" },
|
|
||||||
.{ .name = "e", .type = "float" },
|
|
||||||
.{ .name = "f", .type = "float" },
|
|
||||||
.{ .name = "g", .type = "float" },
|
|
||||||
};
|
|
||||||
|
|
||||||
for (expected, 0..) |exp, i| {
|
|
||||||
try testing.expectEqualStrings(exp.name, s.fields[i].name);
|
|
||||||
try testing.expectEqualStrings(exp.type, s.fields[i].type_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
const patterns = @import("src/patterns.zig");
|
|
||||||
|
|
||||||
pub fn main() !void {
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
defer _ = gpa.deinit();
|
|
||||||
const allocator = gpa.allocator();
|
|
||||||
|
|
||||||
const source = @embedFile("test_rect_simple.c");
|
|
||||||
|
|
||||||
var scanner = patterns.Scanner.init(allocator, source);
|
|
||||||
const decls = try scanner.scan();
|
|
||||||
defer {
|
|
||||||
for (decls) |decl| {
|
|
||||||
switch (decl) {
|
|
||||||
.struct_decl => |s| {
|
|
||||||
allocator.free(s.name);
|
|
||||||
if (s.doc_comment) |doc| allocator.free(doc);
|
|
||||||
for (s.fields) |field| {
|
|
||||||
std.debug.print("Field: {s}: {s}\n", .{field.name, field.type_name});
|
|
||||||
allocator.free(field.name);
|
|
||||||
allocator.free(field.type_name);
|
|
||||||
if (field.comment) |c| allocator.free(c);
|
|
||||||
}
|
|
||||||
allocator.free(s.fields);
|
|
||||||
},
|
|
||||||
.function_decl => |f| {
|
|
||||||
std.debug.print("Function: {s}\n", .{f.name});
|
|
||||||
for (f.params) |p| {
|
|
||||||
std.debug.print(" Param: {s}: {s}\n", .{p.name, p.type_name});
|
|
||||||
}
|
|
||||||
allocator.free(f.name);
|
|
||||||
allocator.free(f.return_type);
|
|
||||||
if (f.doc_comment) |doc| allocator.free(doc);
|
|
||||||
for (f.params) |p| {
|
|
||||||
allocator.free(p.name);
|
|
||||||
allocator.free(p.type_name);
|
|
||||||
}
|
|
||||||
allocator.free(f.params);
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
allocator.free(decls);
|
|
||||||
}
|
|
||||||
|
|
||||||
std.debug.print("\nTotal declarations: {d}\n", .{decls.len});
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
typedef struct SDL_Rect {
|
|
||||||
int x, y;
|
|
||||||
int w, h;
|
|
||||||
} SDL_Rect;
|
|
||||||
|
|
||||||
extern int SDL_GetRectUnion(const SDL_Rect *A, const SDL_Rect *B, SDL_Rect *result);
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
typedef Uint32 SDL_PropertiesID;
|
|
||||||
typedef Uint32 SDL_WindowID;
|
|
||||||
typedef int SDL_SpinLock;
|
|
||||||
|
|
||||||
typedef struct SDL_Thing SDL_Thing;
|
|
||||||
|
|
||||||
extern void SDL_SetProperty(SDL_PropertiesID props);
|
|
||||||
|
|
@ -1,132 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
const testing = std.testing;
|
|
||||||
const patterns = @import("src/patterns.zig");
|
|
||||||
const codegen = @import("src/codegen.zig");
|
|
||||||
|
|
||||||
test "typedef: simple integer type" {
|
|
||||||
const allocator = testing.allocator;
|
|
||||||
const source = "typedef Uint32 SDL_PropertiesID;";
|
|
||||||
|
|
||||||
var scanner = patterns.Scanner.init(allocator, source);
|
|
||||||
const decls = try scanner.scan();
|
|
||||||
defer {
|
|
||||||
for (decls) |decl| {
|
|
||||||
switch (decl) {
|
|
||||||
.typedef_decl => |t| {
|
|
||||||
allocator.free(t.name);
|
|
||||||
allocator.free(t.underlying_type);
|
|
||||||
if (t.doc_comment) |doc| allocator.free(doc);
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
allocator.free(decls);
|
|
||||||
}
|
|
||||||
|
|
||||||
try testing.expectEqual(@as(usize, 1), decls.len);
|
|
||||||
const t = decls[0].typedef_decl;
|
|
||||||
try testing.expectEqualStrings("SDL_PropertiesID", t.name);
|
|
||||||
try testing.expectEqualStrings("Uint32", t.underlying_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "typedef: multiple typedefs" {
|
|
||||||
const allocator = testing.allocator;
|
|
||||||
const source =
|
|
||||||
\\typedef Uint32 SDL_PropertiesID;
|
|
||||||
\\typedef Uint32 SDL_WindowID;
|
|
||||||
\\typedef int SDL_SpinLock;
|
|
||||||
;
|
|
||||||
|
|
||||||
var scanner = patterns.Scanner.init(allocator, source);
|
|
||||||
const decls = try scanner.scan();
|
|
||||||
defer {
|
|
||||||
for (decls) |decl| {
|
|
||||||
switch (decl) {
|
|
||||||
.typedef_decl => |t| {
|
|
||||||
allocator.free(t.name);
|
|
||||||
allocator.free(t.underlying_type);
|
|
||||||
if (t.doc_comment) |doc| allocator.free(doc);
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
allocator.free(decls);
|
|
||||||
}
|
|
||||||
|
|
||||||
try testing.expectEqual(@as(usize, 3), decls.len);
|
|
||||||
|
|
||||||
const t1 = decls[0].typedef_decl;
|
|
||||||
try testing.expectEqualStrings("SDL_PropertiesID", t1.name);
|
|
||||||
try testing.expectEqualStrings("Uint32", t1.underlying_type);
|
|
||||||
|
|
||||||
const t2 = decls[1].typedef_decl;
|
|
||||||
try testing.expectEqualStrings("SDL_WindowID", t2.name);
|
|
||||||
try testing.expectEqualStrings("Uint32", t2.underlying_type);
|
|
||||||
|
|
||||||
const t3 = decls[2].typedef_decl;
|
|
||||||
try testing.expectEqualStrings("SDL_SpinLock", t3.name);
|
|
||||||
try testing.expectEqualStrings("int", t3.underlying_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "typedef: code generation" {
|
|
||||||
const allocator = testing.allocator;
|
|
||||||
|
|
||||||
const decls = [_]patterns.Declaration{
|
|
||||||
.{ .typedef_decl = .{
|
|
||||||
.name = "SDL_PropertiesID",
|
|
||||||
.underlying_type = "Uint32",
|
|
||||||
.doc_comment = null,
|
|
||||||
}},
|
|
||||||
};
|
|
||||||
|
|
||||||
const output = try codegen.CodeGen.generate(allocator, &decls);
|
|
||||||
defer allocator.free(output);
|
|
||||||
|
|
||||||
try testing.expect(std.mem.indexOf(u8, output, "pub const PropertiesID = u32;") != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "typedef: skips struct typedefs" {
|
|
||||||
const allocator = testing.allocator;
|
|
||||||
const source =
|
|
||||||
\\typedef struct SDL_Thing {
|
|
||||||
\\ int x;
|
|
||||||
\\} SDL_Thing;
|
|
||||||
;
|
|
||||||
|
|
||||||
var scanner = patterns.Scanner.init(allocator, source);
|
|
||||||
const decls = try scanner.scan();
|
|
||||||
defer {
|
|
||||||
for (decls) |decl| {
|
|
||||||
switch (decl) {
|
|
||||||
.struct_decl => |s| {
|
|
||||||
allocator.free(s.name);
|
|
||||||
if (s.doc_comment) |doc| allocator.free(doc);
|
|
||||||
for (s.fields) |field| {
|
|
||||||
allocator.free(field.name);
|
|
||||||
allocator.free(field.type_name);
|
|
||||||
if (field.comment) |c| allocator.free(c);
|
|
||||||
}
|
|
||||||
allocator.free(s.fields);
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
allocator.free(decls);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Should be parsed as struct, not typedef
|
|
||||||
try testing.expectEqual(@as(usize, 1), decls.len);
|
|
||||||
try testing.expect(decls[0] == .struct_decl);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "typedef: skips function pointer typedefs" {
|
|
||||||
const allocator = testing.allocator;
|
|
||||||
const source = "typedef void (*SDL_Callback)(void *userdata);";
|
|
||||||
|
|
||||||
var scanner = patterns.Scanner.init(allocator, source);
|
|
||||||
const decls = try scanner.scan();
|
|
||||||
defer allocator.free(decls);
|
|
||||||
|
|
||||||
// Should be skipped (function pointers not supported yet)
|
|
||||||
try testing.expectEqual(@as(usize, 0), decls.len);
|
|
||||||
}
|
|
||||||
|
|
@ -1,90 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
const testing = std.testing;
|
|
||||||
const patterns = @import("src/patterns.zig");
|
|
||||||
|
|
||||||
test "typedef: simple integer type" {
|
|
||||||
const allocator = testing.allocator;
|
|
||||||
const source = "typedef Uint32 SDL_PropertiesID;";
|
|
||||||
|
|
||||||
var scanner = patterns.Scanner.init(allocator, source);
|
|
||||||
const decls = try scanner.scan();
|
|
||||||
defer {
|
|
||||||
for (decls) |decl| {
|
|
||||||
switch (decl) {
|
|
||||||
.typedef_decl => |t| {
|
|
||||||
allocator.free(t.name);
|
|
||||||
allocator.free(t.underlying_type);
|
|
||||||
if (t.doc_comment) |doc| allocator.free(doc);
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
allocator.free(decls);
|
|
||||||
}
|
|
||||||
|
|
||||||
try testing.expectEqual(@as(usize, 1), decls.len);
|
|
||||||
const t = decls[0].typedef_decl;
|
|
||||||
try testing.expectEqualStrings("SDL_PropertiesID", t.name);
|
|
||||||
try testing.expectEqualStrings("Uint32", t.underlying_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "typedef: multiple typedefs" {
|
|
||||||
const allocator = testing.allocator;
|
|
||||||
const source =
|
|
||||||
\\typedef Uint32 SDL_PropertiesID;
|
|
||||||
\\typedef Uint32 SDL_WindowID;
|
|
||||||
\\typedef int SDL_SpinLock;
|
|
||||||
;
|
|
||||||
|
|
||||||
var scanner = patterns.Scanner.init(allocator, source);
|
|
||||||
const decls = try scanner.scan();
|
|
||||||
defer {
|
|
||||||
for (decls) |decl| {
|
|
||||||
switch (decl) {
|
|
||||||
.typedef_decl => |t| {
|
|
||||||
allocator.free(t.name);
|
|
||||||
allocator.free(t.underlying_type);
|
|
||||||
if (t.doc_comment) |doc| allocator.free(doc);
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
allocator.free(decls);
|
|
||||||
}
|
|
||||||
|
|
||||||
try testing.expectEqual(@as(usize, 3), decls.len);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "typedef: skips struct typedefs" {
|
|
||||||
const allocator = testing.allocator;
|
|
||||||
const source =
|
|
||||||
\\typedef struct SDL_Thing {
|
|
||||||
\\ int x;
|
|
||||||
\\} SDL_Thing;
|
|
||||||
;
|
|
||||||
|
|
||||||
var scanner = patterns.Scanner.init(allocator, source);
|
|
||||||
const decls = try scanner.scan();
|
|
||||||
defer {
|
|
||||||
for (decls) |decl| {
|
|
||||||
switch (decl) {
|
|
||||||
.struct_decl => |s| {
|
|
||||||
allocator.free(s.name);
|
|
||||||
if (s.doc_comment) |doc| allocator.free(doc);
|
|
||||||
for (s.fields) |field| {
|
|
||||||
allocator.free(field.name);
|
|
||||||
allocator.free(field.type_name);
|
|
||||||
if (field.comment) |c| allocator.free(c);
|
|
||||||
}
|
|
||||||
allocator.free(s.fields);
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
allocator.free(decls);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Should be parsed as struct, not typedef
|
|
||||||
try testing.expectEqual(@as(usize, 1), decls.len);
|
|
||||||
try testing.expect(decls[0] == .struct_decl);
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
typedef struct SDL_Rect {
|
|
||||||
int x, y;
|
|
||||||
int w, h;
|
|
||||||
} SDL_Rect;
|
|
||||||
|
|
||||||
extern int SDL_Test(const SDL_Rect *rect);
|
|
||||||
|
|
@ -1,239 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
// Minimal c namespace that wraps the C mock functions
|
|
||||||
// This would normally come from @cImport but we provide it manually for testing
|
|
||||||
pub const c = struct {
|
|
||||||
// Module-level functions
|
|
||||||
pub extern fn SDL_GPUSupportsShaderFormats(format_flags: u32, name: [*c]const u8) bool;
|
|
||||||
pub extern fn SDL_GPUSupportsProperties(props: u32) bool;
|
|
||||||
pub extern fn SDL_CreateGPUDevice(format_flags: u32, debug_mode: bool, name: [*c]const u8) ?*anyopaque;
|
|
||||||
pub extern fn SDL_CreateGPUDeviceWithProperties(props: u32) ?*anyopaque;
|
|
||||||
pub extern fn SDL_GetNumGPUDrivers() c_int;
|
|
||||||
pub extern fn SDL_GetGPUDriver(index: c_int) [*c]const u8;
|
|
||||||
pub extern fn SDL_GPUTextureFormatTexelBlockSize(format: c_int) u32;
|
|
||||||
|
|
||||||
// Device methods
|
|
||||||
pub extern fn SDL_DestroyGPUDevice(device: *anyopaque) void;
|
|
||||||
pub extern fn SDL_GetGPUDeviceDriver(device: *anyopaque) [*c]const u8;
|
|
||||||
pub extern fn SDL_GetGPUShaderFormats(device: *anyopaque) u32;
|
|
||||||
pub extern fn SDL_CreateGPUTexture(device: *anyopaque, createinfo: *const anyopaque) ?*anyopaque;
|
|
||||||
pub extern fn SDL_CreateGPUBuffer(device: *anyopaque, createinfo: *const anyopaque) ?*anyopaque;
|
|
||||||
pub extern fn SDL_CreateGPUSampler(device: *anyopaque, createinfo: *const anyopaque) ?*anyopaque;
|
|
||||||
|
|
||||||
// Functions that use cross-header types
|
|
||||||
pub extern fn SDL_SetGPUScissor(pass: *anyopaque, scissor: *const Rect) void;
|
|
||||||
pub extern fn SDL_SetGPUBlendConstants(pass: *anyopaque, blend_constants: FColor) void;
|
|
||||||
pub extern fn SDL_ClaimWindowForGPUDevice(device: *anyopaque, window: ?*anyopaque) bool;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Now we can include the generated bindings which expect a c.zig module
|
|
||||||
// We'll manually inline the key types for testing
|
|
||||||
|
|
||||||
pub const GPUDevice = opaque {
|
|
||||||
pub inline fn destroyGPUDevice(gpudevice: *GPUDevice) void {
|
|
||||||
return c.SDL_DestroyGPUDevice(gpudevice);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn getGPUDeviceDriver(gpudevice: *GPUDevice) [*c]const u8 {
|
|
||||||
return c.SDL_GetGPUDeviceDriver(gpudevice);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn getGPUShaderFormats(gpudevice: *GPUDevice) GPUShaderFormat {
|
|
||||||
return @bitCast(c.SDL_GetGPUShaderFormats(gpudevice));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const GPUBuffer = opaque {};
|
|
||||||
pub const GPUTexture = opaque {};
|
|
||||||
pub const GPUSampler = opaque {};
|
|
||||||
|
|
||||||
pub const GPUPrimitiveType = enum(c_int) {
|
|
||||||
primitivetypeTrianglelist,
|
|
||||||
primitivetypeTrianglestrip,
|
|
||||||
primitivetypeTrianglefan,
|
|
||||||
primitivetypeLinelist,
|
|
||||||
primitivetypeLinestrip,
|
|
||||||
primitivetypePointlist,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const GPULoadOp = enum(c_int) {
|
|
||||||
loadopLoad,
|
|
||||||
loadopClear,
|
|
||||||
loadopDontCare,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const GPUShaderFormat = packed struct(u32) {
|
|
||||||
invalid: bool = false,
|
|
||||||
private: bool = false,
|
|
||||||
spirv: bool = false,
|
|
||||||
dxbc: bool = false,
|
|
||||||
dxil: bool = false,
|
|
||||||
msl: bool = false,
|
|
||||||
metallib: bool = false,
|
|
||||||
_padding: u25 = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const PropertiesID = u32;
|
|
||||||
|
|
||||||
// MISSING TYPES - These would normally come from other SDL headers
|
|
||||||
// but the parser doesn't extract them yet!
|
|
||||||
pub const Window = opaque {}; // From SDL_video.h
|
|
||||||
pub const Rect = extern struct { // From SDL_rect.h
|
|
||||||
x: i32,
|
|
||||||
y: i32,
|
|
||||||
w: i32,
|
|
||||||
h: i32,
|
|
||||||
};
|
|
||||||
pub const FColor = extern struct { // From SDL_pixels.h
|
|
||||||
r: f32,
|
|
||||||
g: f32,
|
|
||||||
b: f32,
|
|
||||||
a: f32,
|
|
||||||
};
|
|
||||||
pub const FlipMode = enum(c_int) { // From SDL_surface.h
|
|
||||||
flipmodeNone,
|
|
||||||
flipmodeHorizontal,
|
|
||||||
flipmodeVertical,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const GPURenderPass = opaque {
|
|
||||||
pub inline fn setGPUScissor(gpurenderpass: *GPURenderPass, scissor: *const Rect) void {
|
|
||||||
return c.SDL_SetGPUScissor(gpurenderpass, scissor);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn setGPUBlendConstants(gpurenderpass: *GPURenderPass, blend_constants: FColor) void {
|
|
||||||
return c.SDL_SetGPUBlendConstants(gpurenderpass, blend_constants);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Module-level functions
|
|
||||||
pub inline fn gpuSupportsShaderFormats(format_flags: GPUShaderFormat, name: [*c]const u8) bool {
|
|
||||||
return c.SDL_GPUSupportsShaderFormats(@bitCast(format_flags), name);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn createGPUDevice(format_flags: GPUShaderFormat, debug_mode: bool, name: [*c]const u8) ?*GPUDevice {
|
|
||||||
return @ptrCast(c.SDL_CreateGPUDevice(@bitCast(format_flags), debug_mode, name));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn getNumGPUDrivers() c_int {
|
|
||||||
return c.SDL_GetNumGPUDrivers();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn getGPUDriver(index: c_int) [*c]const u8 {
|
|
||||||
return c.SDL_GetGPUDriver(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tests demonstrating the mock compilation and linkage works
|
|
||||||
test "can call createGPUDevice with various parameters" {
|
|
||||||
const format = GPUShaderFormat{ .spirv = true };
|
|
||||||
const device = createGPUDevice(format, true, "test");
|
|
||||||
try std.testing.expect(device == null); // Mock returns null
|
|
||||||
}
|
|
||||||
|
|
||||||
test "can call module-level query functions" {
|
|
||||||
const num_drivers = getNumGPUDrivers();
|
|
||||||
try std.testing.expect(num_drivers == 0); // Mock returns 0
|
|
||||||
|
|
||||||
const driver_name = getGPUDriver(0);
|
|
||||||
try std.testing.expect(driver_name == null); // Mock returns null
|
|
||||||
|
|
||||||
const format = GPUShaderFormat{ .spirv = true };
|
|
||||||
const supported = gpuSupportsShaderFormats(format, "vulkan");
|
|
||||||
try std.testing.expect(supported == false); // Mock returns false
|
|
||||||
}
|
|
||||||
|
|
||||||
test "device methods compile and link" {
|
|
||||||
const format = GPUShaderFormat{ .dxil = true };
|
|
||||||
if (createGPUDevice(format, false, null)) |device| {
|
|
||||||
// These would normally work if we had a real device
|
|
||||||
_ = device.getGPUDeviceDriver();
|
|
||||||
_ = device.getGPUShaderFormats();
|
|
||||||
device.destroyGPUDevice();
|
|
||||||
}
|
|
||||||
// No device created from mock, so this shouldn't execute
|
|
||||||
try std.testing.expect(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "enum values are distinct" {
|
|
||||||
try std.testing.expect(GPUPrimitiveType.primitivetypeTrianglelist !=
|
|
||||||
GPUPrimitiveType.primitivetypeTrianglestrip);
|
|
||||||
try std.testing.expect(GPULoadOp.loadopLoad != GPULoadOp.loadopClear);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "packed struct shader format has correct size and fields" {
|
|
||||||
var format = GPUShaderFormat{};
|
|
||||||
try std.testing.expect(@sizeOf(GPUShaderFormat) == 4); // u32
|
|
||||||
|
|
||||||
format.spirv = true;
|
|
||||||
try std.testing.expect(format.spirv);
|
|
||||||
|
|
||||||
format.dxil = true;
|
|
||||||
try std.testing.expect(format.spirv and format.dxil);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "opaque types have correct pointer semantics" {
|
|
||||||
const device_ptr: ?*GPUDevice = null;
|
|
||||||
const buffer_ptr: ?*GPUBuffer = null;
|
|
||||||
const texture_ptr: ?*GPUTexture = null;
|
|
||||||
|
|
||||||
try std.testing.expect(@sizeOf(@TypeOf(device_ptr)) == @sizeOf(?*anyopaque));
|
|
||||||
try std.testing.expect(@sizeOf(@TypeOf(buffer_ptr)) == @sizeOf(?*anyopaque));
|
|
||||||
try std.testing.expect(@sizeOf(@TypeOf(texture_ptr)) == @sizeOf(?*anyopaque));
|
|
||||||
}
|
|
||||||
|
|
||||||
test "large header compilation stress test" {
|
|
||||||
// This test verifies that all 169 declarations from SDL_gpu.h compiled successfully
|
|
||||||
// by instantiating types and checking they're valid
|
|
||||||
const format = GPUShaderFormat{ .spirv = true, .msl = true };
|
|
||||||
_ = format;
|
|
||||||
|
|
||||||
const prim = GPUPrimitiveType.primitivetypeTrianglelist;
|
|
||||||
_ = prim;
|
|
||||||
|
|
||||||
const load = GPULoadOp.loadopLoad;
|
|
||||||
_ = load;
|
|
||||||
|
|
||||||
// If we got here, the compiler successfully processed all types
|
|
||||||
try std.testing.expect(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "CRITICAL: missing dependency types from other headers" {
|
|
||||||
// This test exposes the parser's inability to handle cross-header dependencies
|
|
||||||
|
|
||||||
// These types come from OTHER SDL headers that SDL_gpu.h includes:
|
|
||||||
// - Window (SDL_video.h)
|
|
||||||
// - Rect (SDL_rect.h)
|
|
||||||
// - FColor (SDL_pixels.h)
|
|
||||||
// - FlipMode (SDL_surface.h)
|
|
||||||
|
|
||||||
// We had to manually define them above for this test to compile!
|
|
||||||
|
|
||||||
const rect = Rect{ .x = 0, .y = 0, .w = 100, .h = 100 };
|
|
||||||
try std.testing.expect(rect.w == 100);
|
|
||||||
|
|
||||||
const color = FColor{ .r = 1.0, .g = 0.5, .b = 0.0, .a = 1.0 };
|
|
||||||
try std.testing.expect(color.r == 1.0);
|
|
||||||
|
|
||||||
const flip = FlipMode.flipmodeHorizontal;
|
|
||||||
try std.testing.expect(flip == .flipmodeHorizontal);
|
|
||||||
|
|
||||||
// The parser currently generates references to these types
|
|
||||||
// but doesn't extract their definitions from the included headers!
|
|
||||||
}
|
|
||||||
|
|
||||||
test "functions using cross-header types would fail without manual definitions" {
|
|
||||||
// If we tried to use the ACTUAL generated gpu_test.zig,
|
|
||||||
// it would fail to compile because Window, Rect, FColor are undefined
|
|
||||||
|
|
||||||
// Example from generated code that references undefined types:
|
|
||||||
// pub inline fn setGPUScissor(gpurenderpass: *GPURenderPass, scissor: *const Rect) void
|
|
||||||
// pub inline fn setGPUBlendConstants(gpurenderpass: *GPURenderPass, blend_constants: FColor) void
|
|
||||||
// pub inline fn claimWindowForGPUDevice(gpudevice: *GPUDevice, window: ?*Window) bool
|
|
||||||
|
|
||||||
// This proves the parser needs to:
|
|
||||||
// 1. Detect types referenced but not defined in the current header
|
|
||||||
// 2. Parse the included headers to extract those type definitions
|
|
||||||
// 3. Generate minimal bindings for dependency types
|
|
||||||
|
|
||||||
try std.testing.expect(true); // This test just documents the issue
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue