can parse all major header files now tested with Dofficial and castholm repos
This commit is contained in:
parent
942e577775
commit
aaa8ffba47
|
|
@ -0,0 +1,56 @@
|
||||||
|
# SDL3 Header Parser - Agent Documentation
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
This is an SDL3 header parser that converts C header files into Zig interfaces, JSON objects, and C mocks. Built specifically for SDL3 headers (developed against SDL 3.2.10), it focuses on generating Zig APIs.
|
||||||
|
|
||||||
|
**Key Points:**
|
||||||
|
- Not a real C parser - best-effort text transformation specifically for SDL3 headers
|
||||||
|
- Does not do proper AST elaboration or tokenization
|
||||||
|
- Simple to modify but brittle
|
||||||
|
- Requires Zig 0.15.2 and git
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
The codebase consists of several modules:
|
||||||
|
- `parser.zig` - Main entry point and orchestration
|
||||||
|
- `patterns.zig` - SDL3 pattern matching and declaration scanning
|
||||||
|
- `codegen.zig` - Zig code generation
|
||||||
|
- `mock_codegen.zig` - C mock generation
|
||||||
|
- `json_serializer.zig` - JSON output generation
|
||||||
|
- `dependency_resolver.zig` - Type dependency resolution
|
||||||
|
- `header_cache.zig` - Header caching for cross-file type resolution
|
||||||
|
- `io.zig` - Error handling utilities
|
||||||
|
- `types.zig` - Type mapping and conversions
|
||||||
|
- `naming.zig` - Naming conventions
|
||||||
|
|
||||||
|
## Developing
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
**CRITICAL:** Whenever actual errors are encountered (parsing failures, invalid declarations, missing types, etc.), you MUST call `io.emitError` to report them properly. Do NOT use `std.debug.print` for actual errors.
|
||||||
|
|
||||||
|
```zig
|
||||||
|
// Good - proper error reporting
|
||||||
|
try io.emitError(allocator, "Failed to parse struct field: {s}", .{field_name});
|
||||||
|
|
||||||
|
// Bad - don't use for errors
|
||||||
|
std.debug.print("Error: Failed to parse...\n", .{});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Validation Process
|
||||||
|
After generating Zig code:
|
||||||
|
1. Output written to `tmp/` directory
|
||||||
|
2. `zig ast-check` runs on the temporary file
|
||||||
|
3. If passes: `zig fmt` runs, then copied to final destination
|
||||||
|
4. If fails: debug output written to `debug/` with `_fmterror.zig` suffix
|
||||||
|
|
||||||
|
### Memory Management
|
||||||
|
- Two cleanup functions exist with identical logic:
|
||||||
|
- `freeDecls()` - frees a slice of declarations
|
||||||
|
- `freeDeclDeep()` - frees a single declaration
|
||||||
|
- Both free all nested allocations (names, types, doc comments, params/fields/values/flags)
|
||||||
|
|
||||||
|
### Main APIs (Priority)
|
||||||
|
Focus areas: gpu, video, gamepad, joystick, input, event
|
||||||
|
|
||||||
|
Anything beyond these is not actively maintained.
|
||||||
|
|
@ -9,34 +9,6 @@
|
||||||
"unions": [],
|
"unions": [],
|
||||||
"flags": [],
|
"flags": [],
|
||||||
"functions": [
|
"functions": [
|
||||||
{
|
|
||||||
"name": "SDL_SetError",
|
|
||||||
"return_type": "bool",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "fmt",
|
|
||||||
"type": "const char *"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "",
|
|
||||||
"type": "..."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "SDL_SetErrorV",
|
|
||||||
"return_type": "bool",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "fmt",
|
|
||||||
"type": "const char *"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "ap",
|
|
||||||
"type": "va_list"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "SDL_OutOfMemory",
|
"name": "SDL_OutOfMemory",
|
||||||
"return_type": "bool",
|
"return_type": "bool",
|
||||||
|
|
|
||||||
|
|
@ -1638,32 +1638,6 @@
|
||||||
"type": "const char *"
|
"type": "const char *"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "SDL_RenderDebugTextFormat",
|
|
||||||
"return_type": "bool",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "renderer",
|
|
||||||
"type": "SDL_Renderer *"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "x",
|
|
||||||
"type": "float"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "y",
|
|
||||||
"type": "float"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "fmt",
|
|
||||||
"type": "const char *"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "",
|
|
||||||
"type": "..."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -49,14 +49,6 @@ pub const DependencyResolver = struct {
|
||||||
return try missing.toOwnedSlice(allocator);
|
return try missing.toOwnedSlice(allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get hardcoded declarations for special types that can't be resolved from headers
|
|
||||||
/// Currently unused - function pointer typedefs are now handled as c_type_alias in patterns
|
|
||||||
pub fn getHardcodedDeclarations(self: *DependencyResolver, allocator: Allocator) ![]Declaration {
|
|
||||||
_ = self;
|
|
||||||
var hardcoded = std.ArrayList(Declaration){};
|
|
||||||
return try hardcoded.toOwnedSlice(allocator);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn collectDefinedTypes(self: *DependencyResolver, decls: []const Declaration) !void {
|
fn collectDefinedTypes(self: *DependencyResolver, decls: []const Declaration) !void {
|
||||||
for (decls) |decl| {
|
for (decls) |decl| {
|
||||||
const type_name = switch (decl) {
|
const type_name = switch (decl) {
|
||||||
|
|
|
||||||
10
src/io.zig
10
src/io.zig
|
|
@ -11,4 +11,14 @@ pub fn initWriter() void {
|
||||||
stdout = &stdout_writer.interface;
|
stdout = &stdout_writer.interface;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn emitError(src: std.builtin.SourceLocation, errorMessage: []const u8) void {
|
||||||
|
std.debug.print("----- Error occured -----\n", .{});
|
||||||
|
std.debug.print("in {s}:{d} module: {s}", .{ src.file, src.line, src.module });
|
||||||
|
std.debug.print("function: {s}", .{src.fn_name});
|
||||||
|
std.debug.print("message: '{s}'", .{errorMessage});
|
||||||
|
std.debug.dumpCurrentStackTrace(null);
|
||||||
|
std.debug.print("---- /Error occured -----\n", .{});
|
||||||
|
}
|
||||||
|
|
||||||
pub const std = @import("std");
|
pub const std = @import("std");
|
||||||
|
const buildOptions = @import("buildOptions");
|
||||||
|
|
|
||||||
113
src/parser.zig
113
src/parser.zig
|
|
@ -6,6 +6,13 @@ const json_serializer = @import("json_serializer.zig");
|
||||||
const io = @import("io.zig");
|
const io = @import("io.zig");
|
||||||
const header_cache = @import("header_cache.zig");
|
const header_cache = @import("header_cache.zig");
|
||||||
|
|
||||||
|
pub fn freeDecls(allocator: std.mem.Allocator, decls: []patterns.Declaration) void {
|
||||||
|
for (decls) |decl| {
|
||||||
|
freeDeclDeep(allocator, decl);
|
||||||
|
}
|
||||||
|
allocator.free(decls);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
const allocator = std.heap.smp_allocator;
|
const allocator = std.heap.smp_allocator;
|
||||||
|
|
||||||
|
|
@ -74,87 +81,7 @@ pub fn main() !void {
|
||||||
// Parse declarations
|
// Parse declarations
|
||||||
var scanner = patterns.Scanner.init(allocator, source);
|
var scanner = patterns.Scanner.init(allocator, source);
|
||||||
const decls = try scanner.scan();
|
const decls = try scanner.scan();
|
||||||
defer {
|
defer freeDecls(allocator, decls);
|
||||||
for (decls) |decl| {
|
|
||||||
switch (decl) {
|
|
||||||
.opaque_type => |opaque_decl| {
|
|
||||||
allocator.free(opaque_decl.name);
|
|
||||||
if (opaque_decl.doc_comment) |doc| allocator.free(doc);
|
|
||||||
},
|
|
||||||
.typedef_decl => |typedef_decl| {
|
|
||||||
allocator.free(typedef_decl.name);
|
|
||||||
allocator.free(typedef_decl.underlying_type);
|
|
||||||
if (typedef_decl.doc_comment) |doc| allocator.free(doc);
|
|
||||||
},
|
|
||||||
.function_pointer_decl => |func_ptr_decl| {
|
|
||||||
allocator.free(func_ptr_decl.name);
|
|
||||||
allocator.free(func_ptr_decl.return_type);
|
|
||||||
if (func_ptr_decl.doc_comment) |doc| allocator.free(doc);
|
|
||||||
for (func_ptr_decl.params) |param| {
|
|
||||||
allocator.free(param.name);
|
|
||||||
allocator.free(param.type_name);
|
|
||||||
}
|
|
||||||
allocator.free(func_ptr_decl.params);
|
|
||||||
},
|
|
||||||
.c_type_alias => |alias| {
|
|
||||||
allocator.free(alias.name);
|
|
||||||
if (alias.doc_comment) |doc| allocator.free(doc);
|
|
||||||
},
|
|
||||||
.enum_decl => |enum_decl| {
|
|
||||||
allocator.free(enum_decl.name);
|
|
||||||
if (enum_decl.doc_comment) |doc| allocator.free(doc);
|
|
||||||
for (enum_decl.values) |val| {
|
|
||||||
allocator.free(val.name);
|
|
||||||
if (val.value) |v| allocator.free(v);
|
|
||||||
if (val.comment) |c| allocator.free(c);
|
|
||||||
}
|
|
||||||
allocator.free(enum_decl.values);
|
|
||||||
},
|
|
||||||
.struct_decl => |struct_decl| {
|
|
||||||
allocator.free(struct_decl.name);
|
|
||||||
if (struct_decl.doc_comment) |doc| allocator.free(doc);
|
|
||||||
for (struct_decl.fields) |field| {
|
|
||||||
allocator.free(field.name);
|
|
||||||
allocator.free(field.type_name);
|
|
||||||
if (field.comment) |c| allocator.free(c);
|
|
||||||
}
|
|
||||||
allocator.free(struct_decl.fields);
|
|
||||||
},
|
|
||||||
.union_decl => |union_decl| {
|
|
||||||
allocator.free(union_decl.name);
|
|
||||||
if (union_decl.doc_comment) |doc| allocator.free(doc);
|
|
||||||
for (union_decl.fields) |field| {
|
|
||||||
allocator.free(field.name);
|
|
||||||
allocator.free(field.type_name);
|
|
||||||
if (field.comment) |c| allocator.free(c);
|
|
||||||
}
|
|
||||||
allocator.free(union_decl.fields);
|
|
||||||
},
|
|
||||||
.flag_decl => |flag_decl| {
|
|
||||||
allocator.free(flag_decl.name);
|
|
||||||
allocator.free(flag_decl.underlying_type);
|
|
||||||
if (flag_decl.doc_comment) |doc| allocator.free(doc);
|
|
||||||
for (flag_decl.flags) |flag| {
|
|
||||||
allocator.free(flag.name);
|
|
||||||
allocator.free(flag.value);
|
|
||||||
if (flag.comment) |c| allocator.free(c);
|
|
||||||
}
|
|
||||||
allocator.free(flag_decl.flags);
|
|
||||||
},
|
|
||||||
.function_decl => |func| {
|
|
||||||
allocator.free(func.name);
|
|
||||||
allocator.free(func.return_type);
|
|
||||||
if (func.doc_comment) |doc| allocator.free(doc);
|
|
||||||
for (func.params) |param| {
|
|
||||||
allocator.free(param.name);
|
|
||||||
allocator.free(param.type_name);
|
|
||||||
}
|
|
||||||
allocator.free(func.params);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
allocator.free(decls);
|
|
||||||
}
|
|
||||||
|
|
||||||
std.debug.print("Found {d} declarations\n", .{decls.len});
|
std.debug.print("Found {d} declarations\n", .{decls.len});
|
||||||
|
|
||||||
|
|
@ -240,17 +167,17 @@ pub fn main() !void {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get hardcoded declarations for special types (like SDL_HitTest)
|
// Get hardcoded declarations for special types (like SDL_HitTest)
|
||||||
const hardcoded_decls = try resolver.getHardcodedDeclarations(allocator);
|
// const hardcoded_decls = try resolver.getHardcodedDeclarations(allocator);
|
||||||
defer {
|
// defer {
|
||||||
for (hardcoded_decls) |hd| {
|
// for (hardcoded_decls) |hd| {
|
||||||
freeDeclDeep(allocator, hd);
|
// freeDeclDeep(allocator, hd);
|
||||||
}
|
// }
|
||||||
allocator.free(hardcoded_decls);
|
// allocator.free(hardcoded_decls);
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (hardcoded_decls.len > 0) {
|
// if (hardcoded_decls.len > 0) {
|
||||||
std.debug.print("Adding {d} hardcoded type declarations\n", .{hardcoded_decls.len});
|
// std.debug.print("Adding {d} hardcoded type declarations\n", .{hardcoded_decls.len});
|
||||||
}
|
// }
|
||||||
|
|
||||||
var dependency_decls = std.ArrayList(patterns.Declaration){};
|
var dependency_decls = std.ArrayList(patterns.Declaration){};
|
||||||
defer {
|
defer {
|
||||||
|
|
@ -332,12 +259,12 @@ pub fn main() !void {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Combine declarations (hardcoded first, then dependencies, then primary!)
|
// Combine declarations (hardcoded first, then dependencies, then primary!)
|
||||||
std.debug.print("Combining {d} hardcoded + {d} dependency declarations with primary declarations...\n", .{ hardcoded_decls.len, dependency_decls.items.len });
|
std.debug.print("Combining {d} dependency declarations with primary declarations...\n", .{dependency_decls.items.len});
|
||||||
|
|
||||||
var all_decls = std.ArrayList(patterns.Declaration){};
|
var all_decls = std.ArrayList(patterns.Declaration){};
|
||||||
defer all_decls.deinit(allocator);
|
defer all_decls.deinit(allocator);
|
||||||
|
|
||||||
try all_decls.appendSlice(allocator, hardcoded_decls);
|
// try all_decls.appendSlice(allocator, hardcoded_decls);
|
||||||
try all_decls.appendSlice(allocator, dependency_decls.items);
|
try all_decls.appendSlice(allocator, dependency_decls.items);
|
||||||
try all_decls.appendSlice(allocator, decls);
|
try all_decls.appendSlice(allocator, decls);
|
||||||
|
|
||||||
|
|
|
||||||
123
src/patterns.zig
123
src/patterns.zig
|
|
@ -1,4 +1,5 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const io = @import("io");
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
fn fixupZigName(name: []const u8) []u8 {
|
fn fixupZigName(name: []const u8) []u8 {
|
||||||
|
|
@ -603,7 +604,7 @@ pub const Scanner = struct {
|
||||||
const body = try self.readBracedBlock();
|
const body = try self.readBracedBlock();
|
||||||
defer self.allocator.free(body);
|
defer self.allocator.free(body);
|
||||||
|
|
||||||
// Check if struct contains unions - C unions can't be represented in other languages
|
// Check if struct contains unions - C embedded unions can't be represented in other languages
|
||||||
const has_unions = std.mem.indexOf(u8, body, "union ") != null or
|
const has_unions = std.mem.indexOf(u8, body, "union ") != null or
|
||||||
std.mem.indexOf(u8, body, "union{") != null or
|
std.mem.indexOf(u8, body, "union{") != null or
|
||||||
std.mem.indexOf(u8, body, "union\n") != null or
|
std.mem.indexOf(u8, body, "union\n") != null or
|
||||||
|
|
@ -876,7 +877,7 @@ pub const Scanner = struct {
|
||||||
const type_parts = parts_list[0 .. parts_count - 1];
|
const type_parts = parts_list[0 .. parts_count - 1];
|
||||||
|
|
||||||
// Reconstruct type with array notation
|
// Reconstruct type with array notation
|
||||||
var type_buf: [128]u8 = undefined;
|
var type_buf: [256]u8 = undefined;
|
||||||
var fbs = std.io.fixedBufferStream(&type_buf);
|
var fbs = std.io.fixedBufferStream(&type_buf);
|
||||||
const writer = fbs.writer();
|
const writer = fbs.writer();
|
||||||
for (type_parts, 0..) |part, i| {
|
for (type_parts, 0..) |part, i| {
|
||||||
|
|
@ -889,6 +890,7 @@ pub const Scanner = struct {
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.writeAll(bracket_part) catch {
|
writer.writeAll(bracket_part) catch {
|
||||||
if (comment) |c| self.allocator.free(c);
|
if (comment) |c| self.allocator.free(c);
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -1176,12 +1178,15 @@ pub const Scanner = struct {
|
||||||
"SDL_PRINTF_VARARG_FUNCV",
|
"SDL_PRINTF_VARARG_FUNCV",
|
||||||
"SDL_WPRINTF_VARARG_FUNC",
|
"SDL_WPRINTF_VARARG_FUNC",
|
||||||
"SDL_SCANF_VARARG_FUNC",
|
"SDL_SCANF_VARARG_FUNC",
|
||||||
|
};
|
||||||
|
|
||||||
|
// these mean nothing in any other language than C, we just blow it away
|
||||||
|
const clang_acqrel = [_][]const u8{
|
||||||
"SDL_ACQUIRE",
|
"SDL_ACQUIRE",
|
||||||
"SDL_RELEASE",
|
"SDL_RELEASE",
|
||||||
};
|
};
|
||||||
for (vararg_macros) |macro| {
|
for (clang_acqrel) |macro| {
|
||||||
if (std.mem.indexOf(u8, text, macro)) |pos| {
|
if (std.mem.indexOf(u8, text, macro)) |pos| {
|
||||||
// Find semicolon after this position
|
|
||||||
if (std.mem.indexOfScalarPos(u8, text, pos, ';')) |semi_pos| {
|
if (std.mem.indexOfScalarPos(u8, text, pos, ';')) |semi_pos| {
|
||||||
// Find the closing ) before semicolon
|
// Find the closing ) before semicolon
|
||||||
var paren_pos = semi_pos;
|
var paren_pos = semi_pos;
|
||||||
|
|
@ -1201,6 +1206,13 @@ pub const Scanner = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (vararg_macros) |macro| {
|
||||||
|
if (std.mem.indexOf(u8, text, macro)) |pos| {
|
||||||
|
_ = pos;
|
||||||
|
return null; // we dont attempt to generate vararg functions.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Find SDLCALL to split return type and function name
|
// Find SDLCALL to split return type and function name
|
||||||
const sdlcall_pos = std.mem.indexOf(u8, text, "SDLCALL ") orelse return null;
|
const sdlcall_pos = std.mem.indexOf(u8, text, "SDLCALL ") orelse return null;
|
||||||
const return_type_str = std.mem.trim(u8, text[0..sdlcall_pos], " \t\n");
|
const return_type_str = std.mem.trim(u8, text[0..sdlcall_pos], " \t\n");
|
||||||
|
|
@ -1479,106 +1491,3 @@ pub const Scanner = struct {
|
||||||
return comment;
|
return comment;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
test "scan opaque typedef" {
|
|
||||||
const source = "typedef struct SDL_GPUDevice SDL_GPUDevice;";
|
|
||||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
|
||||||
defer arena.deinit();
|
|
||||||
const allocator = arena.allocator();
|
|
||||||
|
|
||||||
var scanner = Scanner.init(allocator, source);
|
|
||||||
const decls = try scanner.scan();
|
|
||||||
|
|
||||||
try std.testing.expectEqual(@as(usize, 1), decls.len);
|
|
||||||
try std.testing.expect(decls[0] == .opaque_type);
|
|
||||||
try std.testing.expectEqualStrings("SDL_GPUDevice", decls[0].opaque_type.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "scan function declaration" {
|
|
||||||
const source =
|
|
||||||
\\extern SDL_DECLSPEC bool SDLCALL SDL_GPUSupportsShaderFormats(
|
|
||||||
\\ SDL_GPUShaderFormat format_flags,
|
|
||||||
\\ const char *name);
|
|
||||||
;
|
|
||||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
|
||||||
defer arena.deinit();
|
|
||||||
const allocator = arena.allocator();
|
|
||||||
|
|
||||||
var scanner = Scanner.init(allocator, source);
|
|
||||||
const decls = try scanner.scan();
|
|
||||||
|
|
||||||
try std.testing.expectEqual(@as(usize, 1), decls.len);
|
|
||||||
try std.testing.expect(decls[0] == .function_decl);
|
|
||||||
const func = decls[0].function_decl;
|
|
||||||
try std.testing.expectEqualStrings("SDL_GPUSupportsShaderFormats", func.name);
|
|
||||||
try std.testing.expectEqualStrings("bool", func.return_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "scan flag typedef with newline before defines" {
|
|
||||||
const source =
|
|
||||||
\\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)
|
|
||||||
;
|
|
||||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
|
||||||
defer arena.deinit();
|
|
||||||
const allocator = arena.allocator();
|
|
||||||
|
|
||||||
var scanner = Scanner.init(allocator, source);
|
|
||||||
const decls = try scanner.scan();
|
|
||||||
|
|
||||||
try std.testing.expectEqual(@as(usize, 1), decls.len);
|
|
||||||
try std.testing.expect(decls[0] == .flag_decl);
|
|
||||||
const flag = decls[0].flag_decl;
|
|
||||||
try std.testing.expectEqualStrings("SDL_GPUTextureUsageFlags", flag.name);
|
|
||||||
try std.testing.expectEqualStrings("Uint32", flag.underlying_type);
|
|
||||||
try std.testing.expectEqual(@as(usize, 3), flag.flags.len);
|
|
||||||
try std.testing.expectEqualStrings("SDL_GPU_TEXTUREUSAGE_SAMPLER", flag.flags[0].name);
|
|
||||||
try std.testing.expectEqualStrings("SDL_GPU_TEXTUREUSAGE_COLOR_TARGET", flag.flags[1].name);
|
|
||||||
try std.testing.expectEqualStrings("SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET", flag.flags[2].name);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "scan flag typedef with multiple blank lines" {
|
|
||||||
const source =
|
|
||||||
\\typedef Uint32 SDL_GPUBufferUsageFlags;
|
|
||||||
\\
|
|
||||||
\\
|
|
||||||
\\#define SDL_GPU_BUFFERUSAGE_VERTEX (1u << 0)
|
|
||||||
\\#define SDL_GPU_BUFFERUSAGE_INDEX (1u << 1)
|
|
||||||
;
|
|
||||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
|
||||||
defer arena.deinit();
|
|
||||||
const allocator = arena.allocator();
|
|
||||||
|
|
||||||
var scanner = Scanner.init(allocator, source);
|
|
||||||
const decls = try scanner.scan();
|
|
||||||
|
|
||||||
try std.testing.expectEqual(@as(usize, 1), decls.len);
|
|
||||||
try std.testing.expect(decls[0] == .flag_decl);
|
|
||||||
const flag = decls[0].flag_decl;
|
|
||||||
try std.testing.expectEqual(@as(usize, 2), flag.flags.len);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "scan flag typedef with comments before defines" {
|
|
||||||
const source =
|
|
||||||
\\typedef Uint32 SDL_GPUColorComponentFlags;
|
|
||||||
\\
|
|
||||||
\\/* Comment here */
|
|
||||||
;
|
|
||||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
|
||||||
defer arena.deinit();
|
|
||||||
const allocator = arena.allocator();
|
|
||||||
|
|
||||||
var scanner = Scanner.init(allocator, source);
|
|
||||||
const decls = try scanner.scan();
|
|
||||||
|
|
||||||
// Should still parse the typedef even if no #defines follow
|
|
||||||
try std.testing.expectEqual(@as(usize, 1), decls.len);
|
|
||||||
try std.testing.expect(decls[0] == .flag_decl);
|
|
||||||
const flag = decls[0].flag_decl;
|
|
||||||
try std.testing.expectEqualStrings("SDL_GPUColorComponentFlags", flag.name);
|
|
||||||
// No flags found, but that's ok
|
|
||||||
try std.testing.expectEqual(@as(usize, 0), flag.flags.len);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
const io = @import("io");
|
||||||
|
|
||||||
|
var fmtBuffer: [256]u8 = undefined;
|
||||||
/// Convert C type to Zig type
|
/// Convert C type to Zig type
|
||||||
/// Simple table-based conversion for SDL3 types
|
/// Simple table-based conversion for SDL3 types
|
||||||
pub fn convertType(c_type: []const u8, allocator: Allocator) ![]const u8 {
|
pub fn convertType(c_type: []const u8, allocator: Allocator) ![]const u8 {
|
||||||
|
|
@ -21,14 +23,34 @@ pub fn convertType(c_type: []const u8, allocator: Allocator) ![]const u8 {
|
||||||
// Handle array types: "Uint8[2]" -> "[2]u8"
|
// Handle array types: "Uint8[2]" -> "[2]u8"
|
||||||
if (std.mem.indexOf(u8, trimmed, "[")) |bracket_pos| {
|
if (std.mem.indexOf(u8, trimmed, "[")) |bracket_pos| {
|
||||||
const base_type = std.mem.trim(u8, trimmed[0..bracket_pos], " \t");
|
const base_type = std.mem.trim(u8, trimmed[0..bracket_pos], " \t");
|
||||||
const array_part = trimmed[bracket_pos..]; // "[2]"
|
var array_part = trimmed[bracket_pos..]; // "[2]"
|
||||||
|
|
||||||
// Recursively convert the base type
|
// Recursively convert the base type
|
||||||
const zig_base = try convertType(base_type, allocator);
|
const zig_base = try convertType(base_type, allocator);
|
||||||
defer allocator.free(zig_base);
|
defer allocator.free(zig_base);
|
||||||
|
|
||||||
|
const inner = array_part[1 .. array_part.len - 1];
|
||||||
|
if (inner.len > 0) {
|
||||||
|
var alias_to_c: bool = false;
|
||||||
|
|
||||||
|
_ = std.fmt.parseInt(u32, inner, 0) catch {
|
||||||
|
// not a real error
|
||||||
|
alias_to_c = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (alias_to_c) {
|
||||||
|
var b = std.io.fixedBufferStream(&fmtBuffer);
|
||||||
|
_ = try b.write("[");
|
||||||
|
_ = try b.write("c.");
|
||||||
|
_ = try b.write(inner);
|
||||||
|
_ = try b.write("]");
|
||||||
|
array_part = b.getWritten();
|
||||||
|
std.debug.print("arrya_part = {s}\n", .{array_part});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Return Zig array notation: [size]Type
|
// Return Zig array notation: [size]Type
|
||||||
return try std.fmt.allocPrint(allocator, "{s}{s}", .{array_part, zig_base});
|
return try std.fmt.allocPrint(allocator, "{s}{s}", .{ array_part, zig_base });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Primitives
|
// Primitives
|
||||||
|
|
@ -271,41 +293,3 @@ pub const CastType = enum {
|
||||||
int_from_enum,
|
int_from_enum,
|
||||||
enum_from_int,
|
enum_from_int,
|
||||||
};
|
};
|
||||||
|
|
||||||
test "convert primitive types" {
|
|
||||||
const t1 = try convertType("float", std.testing.allocator);
|
|
||||||
defer std.testing.allocator.free(t1);
|
|
||||||
try std.testing.expectEqualStrings("f32", t1);
|
|
||||||
|
|
||||||
const t2 = try convertType("Uint32", std.testing.allocator);
|
|
||||||
defer std.testing.allocator.free(t2);
|
|
||||||
try std.testing.expectEqualStrings("u32", t2);
|
|
||||||
|
|
||||||
const t3 = try convertType("bool", std.testing.allocator);
|
|
||||||
defer std.testing.allocator.free(t3);
|
|
||||||
try std.testing.expectEqualStrings("bool", t3);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "convert SDL types" {
|
|
||||||
const t1 = try convertType("SDL_GPUDevice", std.testing.allocator);
|
|
||||||
defer std.testing.allocator.free(t1);
|
|
||||||
try std.testing.expectEqualStrings("GPUDevice", t1);
|
|
||||||
|
|
||||||
const t2 = try convertType("SDL_GPUDevice *", std.testing.allocator);
|
|
||||||
defer std.testing.allocator.free(t2);
|
|
||||||
try std.testing.expectEqualStrings("?*GPUDevice", t2);
|
|
||||||
|
|
||||||
const t3 = try convertType("const SDL_GPUViewport *", std.testing.allocator);
|
|
||||||
defer std.testing.allocator.free(t3);
|
|
||||||
try std.testing.expectEqualStrings("*const GPUViewport", t3);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "convert pointer types" {
|
|
||||||
const t1 = try convertType("const char *", std.testing.allocator);
|
|
||||||
defer std.testing.allocator.free(t1);
|
|
||||||
try std.testing.expectEqualStrings("[*c]const u8", t1);
|
|
||||||
|
|
||||||
const t2 = try convertType("void *", std.testing.allocator);
|
|
||||||
defer std.testing.allocator.free(t2);
|
|
||||||
try std.testing.expectEqualStrings("?*anyopaque", t2);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
10
v2/error.zig
10
v2/error.zig
|
|
@ -1,16 +1,6 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
pub const c = @import("c.zig").c;
|
pub const c = @import("c.zig").c;
|
||||||
|
|
||||||
pub inline fn setError(fmt: [*c]const u8, ...) bool {
|
|
||||||
return c.SDL_SetError(
|
|
||||||
fmt,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn setErrorV(fmt: [*c]const u8, ap: std.builtin.VaList) bool {
|
|
||||||
return c.SDL_SetErrorV(fmt, ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn outOfMemory() bool {
|
pub inline fn outOfMemory() bool {
|
||||||
return c.SDL_OutOfMemory();
|
return c.SDL_OutOfMemory();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1039,15 +1039,6 @@ pub const Renderer = opaque {
|
||||||
pub inline fn renderDebugText(renderer: *Renderer, x: f32, y: f32, str: [*c]const u8) bool {
|
pub inline fn renderDebugText(renderer: *Renderer, x: f32, y: f32, str: [*c]const u8) bool {
|
||||||
return c.SDL_RenderDebugText(renderer, x, y, str);
|
return c.SDL_RenderDebugText(renderer, x, y, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn renderDebugTextFormat(renderer: *Renderer, x: f32, y: f32, fmt: [*c]const u8, ...) bool {
|
|
||||||
return c.SDL_RenderDebugTextFormat(
|
|
||||||
renderer,
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
fmt,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Texture = opaque {
|
pub const Texture = opaque {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue