370 lines
8.4 KiB
Markdown
370 lines
8.4 KiB
Markdown
# 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`:
|
|
|
|
1. **Strip SDL prefix**: `PRIMITIVETYPE_TRIANGLELIST`
|
|
2. **Find first underscore**: Position 13 (after `PRIMITIVETYPE`)
|
|
3. **Split into parts**:
|
|
- Type part: `PRIMITIVETYPE`
|
|
- Value part: `TRIANGLELIST`
|
|
4. **Convert casing**:
|
|
- Type → lowercase: `primitivetype`
|
|
- Value → TitleCase: `Trianglelist`
|
|
5. **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**:
|
|
```zig
|
|
// Strip SDL_ or SDL_GPU_ prefix
|
|
typeNameToZig("SDL_GPUDevice") → "GPUDevice"
|
|
typeNameToZig("SDL_Window") → "Window"
|
|
```
|
|
|
|
## Enum Value Conversion
|
|
|
|
### Standard Pattern
|
|
|
|
**C Enum**:
|
|
```c
|
|
typedef enum SDL_GPUPrimitiveType {
|
|
SDL_GPU_PRIMITIVETYPE_TRIANGLELIST,
|
|
SDL_GPU_PRIMITIVETYPE_TRIANGLESTRIP,
|
|
} SDL_GPUPrimitiveType;
|
|
```
|
|
|
|
**Zig Enum**:
|
|
```zig
|
|
pub const GPUPrimitiveType = enum(c_int) {
|
|
primitivetypeTrianglelist,
|
|
primitivetypeTrianglestrip,
|
|
};
|
|
```
|
|
|
|
### Numeric Suffixes
|
|
|
|
**C Enum**:
|
|
```c
|
|
typedef enum SDL_GPUSampleCount {
|
|
SDL_GPU_SAMPLECOUNT_1,
|
|
SDL_GPU_SAMPLECOUNT_2,
|
|
SDL_GPU_SAMPLECOUNT_4,
|
|
} SDL_GPUSampleCount;
|
|
```
|
|
|
|
**Zig Enum**:
|
|
```zig
|
|
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**:
|
|
```c
|
|
typedef enum SDL_GPUTextureType {
|
|
SDL_GPU_TEXTURETYPE_2D,
|
|
SDL_GPU_TEXTURETYPE_2D_ARRAY,
|
|
SDL_GPU_TEXTURETYPE_3D,
|
|
} SDL_GPUTextureType;
|
|
```
|
|
|
|
**Zig Enum**:
|
|
```zig
|
|
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
|
|
|
|
```c
|
|
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
|
|
|
|
```zig
|
|
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**:
|
|
```c
|
|
extern SDL_DECLSPEC SDL_GPUDevice* SDLCALL SDL_CreateGPUDevice(bool debug_mode);
|
|
```
|
|
|
|
**Zig Function**:
|
|
```zig
|
|
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
|
|
|
|
```zig
|
|
detectCommonPrefix(["SDL_GPU_PRIMITIVETYPE_TRIANGLELIST",
|
|
"SDL_GPU_PRIMITIVETYPE_TRIANGLESTRIP"])
|
|
→ "SDL_GPU_"
|
|
|
|
detectCommonPrefix(["SDL_WINDOW_FULLSCREEN",
|
|
"SDL_WINDOW_RESIZABLE"])
|
|
→ "SDL_"
|
|
```
|
|
|
|
**Implementation**:
|
|
1. Check if first name starts with `SDL_GPU_` → return `"SDL_GPU_"`
|
|
2. Otherwise check if it starts with `SDL_` → return `"SDL_"`
|
|
3. 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**:
|
|
```c
|
|
SDL_GPU_INVALID
|
|
```
|
|
|
|
**Zig**:
|
|
```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**:
|
|
```c
|
|
SDL_GPU_SOME__VALUE // Double underscore
|
|
```
|
|
|
|
**Zig**:
|
|
```zig
|
|
someValue // Underscores treated as word separators
|
|
```
|
|
|
|
## Casing Helpers
|
|
|
|
### screaminToLowerCamel
|
|
|
|
Converts `SCREAMING_SNAKE_CASE` to `lowerCamelCase`:
|
|
|
|
```zig
|
|
screaminToLowerCamel("TRIANGLE_LIST") → "triangleList"
|
|
screaminToLowerCamel("INVALID") → "invalid"
|
|
```
|
|
|
|
**Algorithm**:
|
|
1. First word: all lowercase
|
|
2. Subsequent words: capitalize first letter
|
|
3. Underscores removed
|
|
|
|
### screaminToTitleCamel
|
|
|
|
Converts `SCREAMING_SNAKE_CASE` to `TitleCamelCase`:
|
|
|
|
```zig
|
|
screaminToTitleCamel("TRIANGLE_LIST") → "TriangleList"
|
|
screaminToTitleCamel("2D_ARRAY") → "2dArray"
|
|
```
|
|
|
|
**Algorithm**:
|
|
1. Every word: capitalize first letter, lowercase rest
|
|
2. Underscores removed
|
|
3. Numbers preserved
|
|
|
|
## Testing Strategy
|
|
|
|
The naming.zig module includes comprehensive tests for:
|
|
|
|
1. **Prefix detection**: Verify `SDL_GPU_` vs `SDL_` detection
|
|
2. **Enum value conversion**: Test first underscore rule
|
|
3. **Numeric prefixes**: Ensure no invalid identifiers
|
|
4. **Multi-word values**: Test underscore handling
|
|
5. **Type name conversion**: Verify SDL prefix stripping
|
|
6. **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**
|
|
```zig
|
|
// With type prefix
|
|
indexelementsize16bit ✓ Valid
|
|
|
|
// Without type prefix
|
|
16bit ✗ Invalid
|
|
```
|
|
|
|
**Benefit 2: Namespace Clarity**
|
|
```zig
|
|
// With type prefix - clear which type
|
|
primitivetypeTrianglelist
|
|
texturetypeTrianglelist
|
|
|
|
// Without - ambiguous
|
|
trianglelist // Which type?
|
|
```
|
|
|
|
**Benefit 3: Consistent Pattern**
|
|
```zig
|
|
// All enum values follow same pattern
|
|
primitivetypeTrianglelist
|
|
primitivetypeTrianglestrip
|
|
primitivetypeLineList
|
|
// Type prefix always present
|
|
```
|
|
|
|
### Why Inline Functions Instead of Direct Imports?
|
|
|
|
**Type Safety**:
|
|
```zig
|
|
// 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**:
|
|
- `inline` keyword 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:
|
|
|
|
1. Uses **first underscore rule** for enum values
|
|
2. Strips **SDL prefix** from type names (keeps GPU)
|
|
3. **Lowercases first character** of function names
|
|
4. Converts **SCREAMING_SNAKE** to **camelCase**
|
|
5. **Preserves type prefixes** in enum values for safety
|
|
6. **Prevents invalid identifiers** starting with numbers
|
|
|
|
All conversions are deterministic, tested, and generate valid Zig code.
|