8.4 KiB
Naming Conventions
This document explains how the SDL3 Parser converts C naming conventions to idiomatic Zig code.
Overview
The parser applies systematic rules to transform SDL3's C naming patterns into Zig-friendly identifiers while preserving semantic meaning and avoiding invalid identifiers.
Core Principle: The "First Underscore Rule"
The fundamental naming algorithm is the first underscore rule, which prevents invalid identifiers and preserves type semantics.
Algorithm
For enum values like SDL_GPU_PRIMITIVETYPE_TRIANGLELIST:
- Strip SDL prefix:
PRIMITIVETYPE_TRIANGLELIST - Find first underscore: Position 13 (after
PRIMITIVETYPE) - Split into parts:
- Type part:
PRIMITIVETYPE - Value part:
TRIANGLELIST
- Type part:
- Convert casing:
- Type → lowercase:
primitivetype - Value → TitleCase:
Trianglelist
- Type → lowercase:
- Concatenate:
primitivetypeTrianglelist
Why First Underscore?
Problem with Last Underscore:
SDL_GPU_TEXTURETYPE_2D_ARRAY
Split at LAST underscore: TEXTURETYPE_2D + ARRAY
Result: texturetype2dArray ✗ Wrong semantics
First Underscore Solution:
SDL_GPU_TEXTURETYPE_2D_ARRAY
Split at FIRST underscore: TEXTURETYPE + 2D_ARRAY
Result: texturetype2dArray ✓ Correct!
Prevents Invalid Identifiers:
SDL_GPU_INDEXELEMENTSIZE_16BIT
Split at FIRST underscore: INDEXELEMENTSIZE + 16BIT
Result: indexelementsize16bit ✓ Valid (starts with letter)
If we stripped too much:
Result: 16bit ✗ Invalid Zig identifier (starts with number)
Type Name Conversion
Opaque Types, Enums, Structs, Flags
Pattern: Strip SDL_ prefix, keep GPU prefix
| C Name | Zig Name |
|---|---|
SDL_GPUDevice |
GPUDevice |
SDL_GPUBuffer |
GPUBuffer |
SDL_GPUTextureUsageFlags |
GPUTextureUsageFlags |
SDL_Window |
Window |
Rule:
// Strip SDL_ or SDL_GPU_ prefix
typeNameToZig("SDL_GPUDevice") → "GPUDevice"
typeNameToZig("SDL_Window") → "Window"
Enum Value Conversion
Standard Pattern
C Enum:
typedef enum SDL_GPUPrimitiveType {
SDL_GPU_PRIMITIVETYPE_TRIANGLELIST,
SDL_GPU_PRIMITIVETYPE_TRIANGLESTRIP,
} SDL_GPUPrimitiveType;
Zig Enum:
pub const GPUPrimitiveType = enum(c_int) {
primitivetypeTrianglelist,
primitivetypeTrianglestrip,
};
Numeric Suffixes
C Enum:
typedef enum SDL_GPUSampleCount {
SDL_GPU_SAMPLECOUNT_1,
SDL_GPU_SAMPLECOUNT_2,
SDL_GPU_SAMPLECOUNT_4,
} SDL_GPUSampleCount;
Zig Enum:
pub const GPUSampleCount = enum(c_int) {
samplecount1,
samplecount2,
samplecount4,
};
Note: The type prefix (samplecount) prevents the invalid identifier 1, 2, 4.
Multi-Word Values
C Enum:
typedef enum SDL_GPUTextureType {
SDL_GPU_TEXTURETYPE_2D,
SDL_GPU_TEXTURETYPE_2D_ARRAY,
SDL_GPU_TEXTURETYPE_3D,
} SDL_GPUTextureType;
Zig Enum:
pub const GPUTextureType = enum(c_int) {
texturetype2d,
texturetype2dArray,
texturetype3d,
};
Algorithm Applied:
SDL_GPU_TEXTURETYPE_2D_ARRAY- Strip prefix:
TEXTURETYPE_2D_ARRAY - First underscore at position 11
- Type:
TEXTURETYPE→texturetype - Value:
2D_ARRAY→2dArray - Result:
texturetype2dArray
Flag Field Conversion
C Flags Definition
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)
Zig Packed Struct
pub const GPUTextureUsageFlags = packed struct(u32) {
textureusageSampler: bool = false,
textureusageColorTarget: bool = false,
textureusageDepthStencilTarget: bool = false,
pad0: u29 = 0,
};
Field Name Pattern:
- Strip
SDL_GPU_prefix:TEXTUREUSAGE_SAMPLER - Apply first underscore rule:
textureusage+Sampler - Result:
textureusageSampler
Function Name Conversion
Pattern: Lowercase Leading Acronyms
C Function:
extern SDL_DECLSPEC SDL_GPUDevice* SDLCALL SDL_CreateGPUDevice(bool debug_mode);
Zig Function:
pub inline fn createGPUDevice(debug_mode: bool) ?*GPUDevice {
return @ptrCast(c.SDL_CreateGPUDevice(debug_mode));
}
Rule:
- Strip
SDL_prefix:CreateGPUDevice - Lowercase first character:
createGPUDevice - Preserve internal acronyms: GPU stays uppercase
More Examples
| C Function | Zig Function |
|---|---|
SDL_CreateGPUDevice |
createGPUDevice |
SDL_DestroyGPUDevice |
destroyGPUDevice |
SDL_CreateWindow |
createWindow |
SDL_GetGPUSwapchainTextureFormat |
getGPUSwapchainTextureFormat |
Prefix Detection
Common Prefix Algorithm
Goal: Detect SDL_GPU_ vs SDL_ prefix
detectCommonPrefix(["SDL_GPU_PRIMITIVETYPE_TRIANGLELIST",
"SDL_GPU_PRIMITIVETYPE_TRIANGLESTRIP"])
→ "SDL_GPU_"
detectCommonPrefix(["SDL_WINDOW_FULLSCREEN",
"SDL_WINDOW_RESIZABLE"])
→ "SDL_"
Implementation:
- Check if first name starts with
SDL_GPU_→ return"SDL_GPU_" - Otherwise check if it starts with
SDL_→ return"SDL_" - Otherwise return empty string
Critical: The prefix is ONLY the SDL part, NOT the type name part.
Edge Cases
Single Word (No Underscore)
C Enum:
SDL_GPU_INVALID
Zig:
invalid // No underscore, so just lowercase entire word
Numbers at Start (After Strip)
Prevented by Type Prefix:
SDL_GPU_INDEXELEMENTSIZE_16BIT
→ indexelementsize16bit ✓ Starts with letter
Without type prefix (WRONG):
→ 16bit ✗ Invalid identifier
Consecutive Underscores
C:
SDL_GPU_SOME__VALUE // Double underscore
Zig:
someValue // Underscores treated as word separators
Casing Helpers
screaminToLowerCamel
Converts SCREAMING_SNAKE_CASE to lowerCamelCase:
screaminToLowerCamel("TRIANGLE_LIST") → "triangleList"
screaminToLowerCamel("INVALID") → "invalid"
Algorithm:
- First word: all lowercase
- Subsequent words: capitalize first letter
- Underscores removed
screaminToTitleCamel
Converts SCREAMING_SNAKE_CASE to TitleCamelCase:
screaminToTitleCamel("TRIANGLE_LIST") → "TriangleList"
screaminToTitleCamel("2D_ARRAY") → "2dArray"
Algorithm:
- Every word: capitalize first letter, lowercase rest
- Underscores removed
- Numbers preserved
Testing Strategy
The naming.zig module includes comprehensive tests for:
- Prefix detection: Verify
SDL_GPU_vsSDL_detection - Enum value conversion: Test first underscore rule
- Numeric prefixes: Ensure no invalid identifiers
- Multi-word values: Test underscore handling
- Type name conversion: Verify SDL prefix stripping
- Function name conversion: Test lowercase leading character
See naming.zig for 10+ unit tests validating these rules.
Design Rationale
Why Keep Type Prefix in Enum Values?
Benefit 1: Prevents Invalid Identifiers
// With type prefix
indexelementsize16bit ✓ Valid
// Without type prefix
16bit ✗ Invalid
Benefit 2: Namespace Clarity
// With type prefix - clear which type
primitivetypeTrianglelist
texturetypeTrianglelist
// Without - ambiguous
trianglelist // Which type?
Benefit 3: Consistent Pattern
// All enum values follow same pattern
primitivetypeTrianglelist
primitivetypeTrianglestrip
primitivetypeLineList
// Type prefix always present
Why Inline Functions Instead of Direct Imports?
Type Safety:
// Inline function with proper types
pub inline fn createGPUDevice(debug_mode: bool) ?*GPUDevice {
return @ptrCast(c.SDL_CreateGPUDevice(debug_mode));
}
// vs direct C import
c.SDL_CreateGPUDevice(debug_mode) // Returns opaque C type
Zero Overhead:
inlinekeyword ensures no runtime cost- Compiler optimizes away the wrapper
- Identical performance to direct C call
Better Error Messages:
- Zig type names in errors
- Clear parameter names
- Type checking at call site
Summary
The SDL3 Parser naming system:
- Uses first underscore rule for enum values
- Strips SDL prefix from type names (keeps GPU)
- Lowercases first character of function names
- Converts SCREAMING_SNAKE to camelCase
- Preserves type prefixes in enum values for safety
- Prevents invalid identifiers starting with numbers
All conversions are deterministic, tested, and generate valid Zig code.