Compare commits
No commits in common. "41b11099be5c5113d33089d3c8415bbf07e55440" and "942e577775b82ccf16816db6744c0bdc493c718d" have entirely different histories.
41b11099be
...
942e577775
56
AGENTS.md
56
AGENTS.md
|
|
@ -1,56 +0,0 @@
|
||||||
# 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,6 +9,34 @@
|
||||||
"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,6 +1638,32 @@
|
||||||
"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,6 +49,14 @@ 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,14 +11,4 @@ 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");
|
|
||||||
|
|
|
||||||
188
src/parser.zig
188
src/parser.zig
|
|
@ -6,13 +6,6 @@ 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;
|
||||||
|
|
||||||
|
|
@ -81,7 +74,87 @@ 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 freeDecls(allocator, decls);
|
defer {
|
||||||
|
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});
|
||||||
|
|
||||||
|
|
@ -167,17 +240,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 {
|
||||||
|
|
@ -259,12 +332,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} dependency declarations with primary declarations...\n", .{dependency_decls.items.len});
|
std.debug.print("Combining {d} hardcoded + {d} dependency declarations with primary declarations...\n", .{ hardcoded_decls.len, 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);
|
||||||
|
|
||||||
|
|
@ -462,7 +535,80 @@ fn writeDebugFile(allocator: std.mem.Allocator, header_path: []const u8, output:
|
||||||
|
|
||||||
fn freeDeclDeep(allocator: std.mem.Allocator, decl: patterns.Declaration) void {
|
fn freeDeclDeep(allocator: std.mem.Allocator, decl: patterns.Declaration) void {
|
||||||
switch (decl) {
|
switch (decl) {
|
||||||
inline else => |*d| d.deinit(allocator),
|
.opaque_type => |o| {
|
||||||
|
allocator.free(o.name);
|
||||||
|
if (o.doc_comment) |doc| allocator.free(doc);
|
||||||
|
},
|
||||||
|
.typedef_decl => |t| {
|
||||||
|
allocator.free(t.name);
|
||||||
|
allocator.free(t.underlying_type);
|
||||||
|
if (t.doc_comment) |doc| allocator.free(doc);
|
||||||
|
},
|
||||||
|
.function_pointer_decl => |fp| {
|
||||||
|
allocator.free(fp.name);
|
||||||
|
allocator.free(fp.return_type);
|
||||||
|
if (fp.doc_comment) |doc| allocator.free(doc);
|
||||||
|
for (fp.params) |param| {
|
||||||
|
allocator.free(param.name);
|
||||||
|
allocator.free(param.type_name);
|
||||||
|
}
|
||||||
|
allocator.free(fp.params);
|
||||||
|
},
|
||||||
|
.c_type_alias => |a| {
|
||||||
|
allocator.free(a.name);
|
||||||
|
if (a.doc_comment) |doc| allocator.free(doc);
|
||||||
|
},
|
||||||
|
.enum_decl => |e| {
|
||||||
|
allocator.free(e.name);
|
||||||
|
if (e.doc_comment) |doc| allocator.free(doc);
|
||||||
|
for (e.values) |val| {
|
||||||
|
allocator.free(val.name);
|
||||||
|
if (val.value) |v| allocator.free(v);
|
||||||
|
if (val.comment) |c| allocator.free(c);
|
||||||
|
}
|
||||||
|
allocator.free(e.values);
|
||||||
|
},
|
||||||
|
.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);
|
||||||
|
},
|
||||||
|
.union_decl => |u| {
|
||||||
|
allocator.free(u.name);
|
||||||
|
if (u.doc_comment) |doc| allocator.free(doc);
|
||||||
|
for (u.fields) |field| {
|
||||||
|
allocator.free(field.name);
|
||||||
|
allocator.free(field.type_name);
|
||||||
|
if (field.comment) |c| allocator.free(c);
|
||||||
|
}
|
||||||
|
allocator.free(u.fields);
|
||||||
|
},
|
||||||
|
.flag_decl => |f| {
|
||||||
|
allocator.free(f.name);
|
||||||
|
allocator.free(f.underlying_type);
|
||||||
|
if (f.doc_comment) |doc| allocator.free(doc);
|
||||||
|
for (f.flags) |flag| {
|
||||||
|
allocator.free(flag.name);
|
||||||
|
allocator.free(flag.value);
|
||||||
|
if (flag.comment) |c| allocator.free(c);
|
||||||
|
}
|
||||||
|
allocator.free(f.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);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
206
src/patterns.zig
206
src/patterns.zig
|
|
@ -1,5 +1,4 @@
|
||||||
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 {
|
||||||
|
|
@ -27,28 +26,12 @@ pub const Declaration = union(enum) {
|
||||||
pub const OpaqueType = struct {
|
pub const OpaqueType = struct {
|
||||||
name: []const u8, // SDL_GPUDevice
|
name: []const u8, // SDL_GPUDevice
|
||||||
doc_comment: ?[]const u8,
|
doc_comment: ?[]const u8,
|
||||||
|
|
||||||
pub fn deinit(self: OpaqueType, allocator: Allocator) void {
|
|
||||||
allocator.free(self.name);
|
|
||||||
if (self.doc_comment) |doc| allocator.free(doc);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const EnumDecl = struct {
|
pub const EnumDecl = struct {
|
||||||
name: []const u8, // SDL_GPUPrimitiveType
|
name: []const u8, // SDL_GPUPrimitiveType
|
||||||
values: []EnumValue,
|
values: []EnumValue,
|
||||||
doc_comment: ?[]const u8,
|
doc_comment: ?[]const u8,
|
||||||
|
|
||||||
pub fn deinit(self: EnumDecl, allocator: Allocator) void {
|
|
||||||
allocator.free(self.name);
|
|
||||||
if (self.doc_comment) |doc| allocator.free(doc);
|
|
||||||
for (self.values) |val| {
|
|
||||||
allocator.free(val.name);
|
|
||||||
if (val.value) |v| allocator.free(v);
|
|
||||||
if (val.comment) |c| allocator.free(c);
|
|
||||||
}
|
|
||||||
allocator.free(self.values);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const EnumValue = struct {
|
pub const EnumValue = struct {
|
||||||
|
|
@ -62,34 +45,12 @@ pub const StructDecl = struct {
|
||||||
fields: []FieldDecl,
|
fields: []FieldDecl,
|
||||||
doc_comment: ?[]const u8,
|
doc_comment: ?[]const u8,
|
||||||
has_unions: bool = false, // If true, codegen should emit as opaque (C unions can't be represented in other languages)
|
has_unions: bool = false, // If true, codegen should emit as opaque (C unions can't be represented in other languages)
|
||||||
|
|
||||||
pub fn deinit(self: StructDecl, allocator: Allocator) void {
|
|
||||||
allocator.free(self.name);
|
|
||||||
if (self.doc_comment) |doc| allocator.free(doc);
|
|
||||||
for (self.fields) |field| {
|
|
||||||
allocator.free(field.name);
|
|
||||||
allocator.free(field.type_name);
|
|
||||||
if (field.comment) |c| allocator.free(c);
|
|
||||||
}
|
|
||||||
allocator.free(self.fields);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const UnionDecl = struct {
|
pub const UnionDecl = struct {
|
||||||
name: []const u8, // SDL_Event
|
name: []const u8, // SDL_Event
|
||||||
fields: []FieldDecl,
|
fields: []FieldDecl,
|
||||||
doc_comment: ?[]const u8,
|
doc_comment: ?[]const u8,
|
||||||
|
|
||||||
pub fn deinit(self: UnionDecl, allocator: Allocator) void {
|
|
||||||
allocator.free(self.name);
|
|
||||||
if (self.doc_comment) |doc| allocator.free(doc);
|
|
||||||
for (self.fields) |field| {
|
|
||||||
allocator.free(field.name);
|
|
||||||
allocator.free(field.type_name);
|
|
||||||
if (field.comment) |c| allocator.free(c);
|
|
||||||
}
|
|
||||||
allocator.free(self.fields);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const FieldDecl = struct {
|
pub const FieldDecl = struct {
|
||||||
|
|
@ -103,18 +64,6 @@ pub const FlagDecl = struct {
|
||||||
underlying_type: []const u8, // Uint32
|
underlying_type: []const u8, // Uint32
|
||||||
flags: []FlagValue,
|
flags: []FlagValue,
|
||||||
doc_comment: ?[]const u8,
|
doc_comment: ?[]const u8,
|
||||||
|
|
||||||
pub fn deinit(self: FlagDecl, allocator: Allocator) void {
|
|
||||||
allocator.free(self.name);
|
|
||||||
allocator.free(self.underlying_type);
|
|
||||||
if (self.doc_comment) |doc| allocator.free(doc);
|
|
||||||
for (self.flags) |flag| {
|
|
||||||
allocator.free(flag.name);
|
|
||||||
allocator.free(flag.value);
|
|
||||||
if (flag.comment) |c| allocator.free(c);
|
|
||||||
}
|
|
||||||
allocator.free(self.flags);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const FlagValue = struct {
|
pub const FlagValue = struct {
|
||||||
|
|
@ -127,12 +76,6 @@ pub const TypedefDecl = struct {
|
||||||
name: []const u8, // SDL_PropertiesID
|
name: []const u8, // SDL_PropertiesID
|
||||||
underlying_type: []const u8, // Uint32
|
underlying_type: []const u8, // Uint32
|
||||||
doc_comment: ?[]const u8,
|
doc_comment: ?[]const u8,
|
||||||
|
|
||||||
pub fn deinit(self: TypedefDecl, allocator: Allocator) void {
|
|
||||||
allocator.free(self.name);
|
|
||||||
allocator.free(self.underlying_type);
|
|
||||||
if (self.doc_comment) |doc| allocator.free(doc);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const FunctionPointerDecl = struct {
|
pub const FunctionPointerDecl = struct {
|
||||||
|
|
@ -140,17 +83,6 @@ pub const FunctionPointerDecl = struct {
|
||||||
return_type: []const u8, // Uint32
|
return_type: []const u8, // Uint32
|
||||||
params: []ParamDecl,
|
params: []ParamDecl,
|
||||||
doc_comment: ?[]const u8,
|
doc_comment: ?[]const u8,
|
||||||
|
|
||||||
pub fn deinit(self: FunctionPointerDecl, allocator: Allocator) void {
|
|
||||||
allocator.free(self.name);
|
|
||||||
allocator.free(self.return_type);
|
|
||||||
if (self.doc_comment) |doc| allocator.free(doc);
|
|
||||||
for (self.params) |param| {
|
|
||||||
allocator.free(param.name);
|
|
||||||
allocator.free(param.type_name);
|
|
||||||
}
|
|
||||||
allocator.free(self.params);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// C type alias - for function pointer typedefs that should alias to C type directly
|
/// C type alias - for function pointer typedefs that should alias to C type directly
|
||||||
|
|
@ -158,11 +90,6 @@ pub const FunctionPointerDecl = struct {
|
||||||
pub const CTypeAlias = struct {
|
pub const CTypeAlias = struct {
|
||||||
name: []const u8, // SDL_HitTest
|
name: []const u8, // SDL_HitTest
|
||||||
doc_comment: ?[]const u8,
|
doc_comment: ?[]const u8,
|
||||||
|
|
||||||
pub fn deinit(self: CTypeAlias, allocator: Allocator) void {
|
|
||||||
allocator.free(self.name);
|
|
||||||
if (self.doc_comment) |doc| allocator.free(doc);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const FunctionDecl = struct {
|
pub const FunctionDecl = struct {
|
||||||
|
|
@ -170,17 +97,6 @@ pub const FunctionDecl = struct {
|
||||||
return_type: []const u8, // SDL_GPUDevice *
|
return_type: []const u8, // SDL_GPUDevice *
|
||||||
params: []ParamDecl,
|
params: []ParamDecl,
|
||||||
doc_comment: ?[]const u8,
|
doc_comment: ?[]const u8,
|
||||||
|
|
||||||
pub fn deinit(self: FunctionDecl, allocator: Allocator) void {
|
|
||||||
allocator.free(self.name);
|
|
||||||
allocator.free(self.return_type);
|
|
||||||
if (self.doc_comment) |doc| allocator.free(doc);
|
|
||||||
for (self.params) |param| {
|
|
||||||
allocator.free(param.name);
|
|
||||||
allocator.free(param.type_name);
|
|
||||||
}
|
|
||||||
allocator.free(self.params);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const ParamDecl = struct {
|
pub const ParamDecl = struct {
|
||||||
|
|
@ -687,7 +603,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 embedded unions can't be represented in other languages
|
// Check if struct contains unions - C 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
|
||||||
|
|
@ -960,7 +876,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: [256]u8 = undefined;
|
var type_buf: [128]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| {
|
||||||
|
|
@ -973,7 +889,6 @@ 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;
|
||||||
|
|
@ -1261,15 +1176,12 @@ 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 (clang_acqrel) |macro| {
|
for (vararg_macros) |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;
|
||||||
|
|
@ -1289,13 +1201,6 @@ 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");
|
||||||
|
|
@ -1574,3 +1479,106 @@ 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,8 +1,6 @@
|
||||||
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 {
|
||||||
|
|
@ -23,34 +21,14 @@ 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");
|
||||||
var array_part = trimmed[bracket_pos..]; // "[2]"
|
const 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
|
||||||
|
|
@ -293,3 +271,41 @@ 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,6 +1,16 @@
|
||||||
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,6 +1039,15 @@ 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