Backlog/lib/sdl3/parser/docs/naming.md

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:

  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:

// 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: TEXTURETYPEtexturetype
  • Value: 2D_ARRAY2dArray
  • 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:

  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:

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:

  1. First word: all lowercase
  2. Subsequent words: capitalize first letter
  3. Underscores removed

screaminToTitleCamel

Converts SCREAMING_SNAKE_CASE to TitleCamelCase:

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

// 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:

  • 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.