Backlog/lib/sdl3/parser/AGENTS.md

312 lines
7.4 KiB
Markdown

# Agent Solutions Guide: Zig 0.15 Issues
This document catalogs common issues encountered when working with Zig 0.15 and their solutions. Written for AI coding assistants to avoid repeating mistakes.
## Critical: ArrayList API Changed in Zig 0.15
### Problem
`std.ArrayList` is now an alias to `std.ArrayListUnmanaged` in Zig 0.15. The managed version has been removed.
### Old (Pre-0.15) Code - DOES NOT WORK
```zig
var list = std.ArrayList(u8).init(allocator);
defer list.deinit();
try list.append(item);
```
### New (0.15+) Code - CORRECT
```zig
// Empty initialization
var list = std.ArrayList(u8){};
defer list.deinit(allocator);
try list.append(allocator, item);
// Or with capacity
var list = try std.ArrayList(u8).initCapacity(allocator, 100);
defer list.deinit(allocator);
try list.append(allocator, item);
```
### Key Changes
1. **Initialization**: Use `{}` or `initCapacity()`, not `init()`
2. **All methods take allocator**: `append(allocator, item)` not `append(item)`
3. **Deinit takes allocator**: `deinit(allocator)` not `deinit()`
## AST Rendering API Changed
### Problem
The `ast.render()` function signature changed in Zig 0.15.
### Old Code - DOES NOT WORK
```zig
var ast = try std.zig.Ast.parse(allocator, source, .zig);
const output = try ast.render(allocator);
```
### New Code - CORRECT
```zig
var ast = try std.zig.Ast.parse(allocator, source, .zig);
const output = try ast.renderAlloc(allocator);
defer allocator.free(output);
```
### The API
- `renderAlloc(allocator)` - Returns allocated string
- `render(tree, gpa, writer, fixups)` - Low-level version for custom output
## Type Conversion: SDL Types to Zig
### Pointer Types
| C Type | Zig Type | Notes |
|--------|----------|-------|
| `const char *` | `[*c]const u8` | C string |
| `void *` | `?*anyopaque` | Nullable any pointer |
| `const void *` | `?*const anyopaque` | Const version |
| `SDL_Type *` | `?*Type` | Nullable pointer to opaque/struct |
| `const SDL_Type *` | `*const Type` | Non-null const pointer |
| `SDL_Type **` | `?*?*Type` | Output parameter (double pointer) |
| `SDL_Type *const *` | `[*c]*const Type` | Array of const pointers |
| `Uint32 *` | `*u32` | Output parameter (primitive) |
### Key Principles
1. **Non-nullable by default** for const pointers to structs
2. **Nullable (`?*`)** for pointers that can be NULL
3. **Use `*` not `[*c]`** when you know it's not a C-style array
4. **Double pointers**: `?*?*Type` for output parameters
## Function Signature Formatting
### Trailing Commas
Only use trailing commas for functions with **more than 3 parameters**. This triggers multi-line formatting.
```zig
// 1-3 parameters: single line, no trailing comma
pub fn foo(a: i32, b: i32, c: i32) void {}
// 4+ parameters: multi-line with trailing comma
pub fn bar(
a: i32,
b: i32,
c: i32,
d: i32,
) void {}
```
### Why?
- Trailing comma with no parameters: `(,)` is **syntax error**
- Trailing comma with 1-3 params: unnecessary, wastes vertical space
- Trailing comma with 4+ params: makes diffs cleaner, easier to read
## Method Organization
### Place Methods Inside Opaque Types
Functions where the first parameter is a pointer to an opaque type should be methods:
```zig
// Good - method syntax
pub const GPUDevice = opaque {
pub fn destroy(device: *GPUDevice) void {
c.SDL_DestroyGPUDevice(device);
}
};
// Usage: device.destroy()
// Bad - standalone function
pub fn destroyGPUDevice(device: ?*GPUDevice) void {
c.SDL_DestroyGPUDevice(device);
}
// Usage: destroyGPUDevice(device)
```
### Benefits
1. Cleaner API: `device.create()` vs `createGPUDevice(device)`
2. IDE autocomplete works better
3. Namespacing prevents naming conflicts
4. More idiomatic Zig
## Casting Guidelines
### When to Cast
| Scenario | Cast | Example |
|----------|------|---------|
| Opaque pointer | `@ptrCast` | `@ptrCast(device)` |
| Flags (packed struct) | `@bitCast` | `@bitCast(flags)` |
| Enum to int | `@intFromEnum` | `@intFromEnum(enum_val)` |
| Struct passed by value | None | Just pass it |
| Const pointer to struct | `@ptrCast` | `@ptrCast(info)` |
### Don't Over-Cast
```zig
// Bad - unnecessary cast for value type
fn setColor(color: FColor) void {
c.SDL_SetColor(@bitCast(color)); // Wrong!
}
// Good - no cast needed
fn setColor(color: FColor) void {
c.SDL_SetColor(color); // Correct
}
```
## StringHashMap Usage
### Correct Pattern
```zig
var map = std.StringHashMap(ValueType).init(allocator);
defer map.deinit(); // No allocator needed for deinit
try map.put("key", value);
const val = map.get("key");
```
### Iteration
```zig
var it = map.keyIterator();
while (it.next()) |key| {
// Use key.*
}
var it = map.valueIterator();
while (it.next()) |value| {
// Use value.* if needed
}
```
## Common Pitfalls
### 1. Forgetting Allocator in Unmanaged Collections
```zig
// Wrong
list.append(item);
// Right
list.append(allocator, item);
```
### 2. Using .init() on ArrayList
```zig
// Wrong
var list = std.ArrayList(u8).init(allocator);
// Right
var list = std.ArrayList(u8){};
// or
var list = try std.ArrayList(u8).initCapacity(allocator, size);
```
### 3. Not Checking AST Errors Before Rendering
```zig
// Wrong - will panic if there are errors
const output = try ast.renderAlloc(allocator);
// Right - check first
if (ast.errors.len > 0) {
// Handle errors
return error.ParseError;
}
const output = try ast.renderAlloc(allocator);
```
### 4. Incorrect Double Pointer Types
```zig
// Wrong - C-style for output params
texture: [*c]*GPUTexture
// Right - Zig optional pointers
texture: ?*?*GPUTexture
```
## Testing Patterns
### Simple Test
```zig
test "description" {
const result = try someFunction();
try std.testing.expectEqual(expected, result);
}
```
### Test with Allocator
```zig
test "with allocator" {
const allocator = std.testing.allocator;
const result = try allocateAndDoSomething(allocator);
defer allocator.free(result);
try std.testing.expectEqualStrings("expected", result);
}
```
## Build System Integration
### Adding Parser to Dependencies
```zig
// build.zig.zon
.dependencies = .{
.sdl3_parser = .{ .path = "parser/" },
},
// build.zig
const parser_dep = b.dependency("sdl3_parser", .{
.target = target,
.optimize = optimize,
});
const parser_exe = parser_dep.artifact("sdl-parser");
```
### Run Step
```zig
const run_parser = b.addRunArtifact(parser_exe);
run_parser.addFileArg(b.path("input.h"));
run_parser.addArg("--output=output.zig");
const step = b.step("generate", "Generate bindings");
step.dependOn(&run_parser.step);
```
## Quick Reference Card
```zig
// Collections
var list = std.ArrayList(T){};
defer list.deinit(allocator);
try list.append(allocator, item);
var map = std.StringHashMap(V).init(allocator);
defer map.deinit();
try map.put("key", value);
// AST
var ast = try std.zig.Ast.parse(allocator, source, .zig);
defer ast.deinit(allocator);
const formatted = try ast.renderAlloc(allocator);
defer allocator.free(formatted);
// Type Patterns
?*Type // Nullable pointer
*const Type // Non-null const pointer
?*?*Type // Output parameter
[*c]*const Type // C array of const pointers
// Casts
@ptrCast(ptr) // Pointers
@bitCast(value) // Packed structs, flags
@intFromEnum(e) // Enum to int
// No cast for value types!
```
## Version Info
- **Zig Version**: 0.15.2
- **Date**: 2025-01-22
- **SDL Version**: 3.2.0
## References
- Zig 0.15 Release Notes: https://ziglang.org/download/0.15.0/release-notes.html
- Zig Standard Library Docs: https://ziglang.org/documentation/master/std/