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

9.1 KiB

JSON Output Implementation Plan

Goal

Add a --generate-json flag to the parser that outputs all parsed declarations (types, enums, functions, etc.) as a structured JSON file for external tooling and analysis.

Design Decisions

1. JSON Schema Design

{
  "header": "SDL_gpu.h",
  "parsed_at": "2026-01-22T23:23:35Z",
  "declarations": {
    "opaque_types": [
      {
        "name": "SDL_GPUDevice",
        "doc_comment": "/**\n * Opaque handle to a GPU device\n */"
      }
    ],
    "typedefs": [
      {
        "name": "SDL_PropertiesID",
        "underlying_type": "Uint32",
        "doc_comment": "..."
      }
    ],
    "function_pointers": [
      {
        "name": "SDL_TimerCallback",
        "return_type": "Uint32",
        "params": [
          {"name": "userdata", "type": "void *"},
          {"name": "timerID", "type": "SDL_TimerID"}
        ],
        "doc_comment": "..."
      }
    ],
    "enums": [
      {
        "name": "SDL_GPUPrimitiveType",
        "values": [
          {"name": "SDL_GPU_PRIMITIVETYPE_TRIANGLELIST", "value": "0", "comment": "..."},
          {"name": "SDL_GPU_PRIMITIVETYPE_TRIANGLESTRIP", "value": null, "comment": "..."}
        ],
        "doc_comment": "..."
      }
    ],
    "structs": [
      {
        "name": "SDL_GPUViewport",
        "fields": [
          {"name": "x", "type": "float", "comment": "..."},
          {"name": "y", "type": "float", "comment": "..."}
        ],
        "doc_comment": "..."
      }
    ],
    "unions": [
      {
        "name": "SDL_Event",
        "fields": [
          {"name": "type", "type": "Uint32", "comment": "..."}
        ],
        "doc_comment": "..."
      }
    ],
    "flags": [
      {
        "name": "SDL_GPUTextureUsageFlags",
        "underlying_type": "Uint32",
        "flags": [
          {"name": "SDL_GPU_TEXTUREUSAGE_SAMPLER", "value": "(1u << 0)", "comment": "..."}
        ],
        "doc_comment": "..."
      }
    ],
    "functions": [
      {
        "name": "SDL_CreateGPUDevice",
        "return_type": "SDL_GPUDevice *",
        "params": [
          {"name": "format_flags", "type": "SDL_GPUShaderFormat"}
        ],
        "doc_comment": "..."
      }
    ]
  },
  "statistics": {
    "total_declarations": 150,
    "opaque_types": 5,
    "typedefs": 10,
    "function_pointers": 3,
    "enums": 15,
    "structs": 25,
    "unions": 2,
    "flags": 10,
    "functions": 80
  }
}

2. Command Line Interface

# Output JSON to stdout
parser SDL_gpu.h --generate-json

# Output JSON to file
parser SDL_gpu.h --generate-json=output.json

# Combine with other outputs
parser SDL_gpu.h --output=gpu.zig --generate-json=gpu.json

3. Implementation Strategy

Phase 1: Create JSON Serializer Module

  • Create src/json_output.zig
  • Implement serialization functions for each declaration type
  • Handle proper escaping of strings (especially doc comments with quotes/newlines)
  • Use std.json.stringify for structured output

Phase 2: Update Command Line Parsing

  • Add --generate-json and --generate-json=<file> flag parsing in parser.zig
  • Store flag in configuration structure

Phase 3: Integrate with Main Parser Flow

  • After scanner.scan() and dependency resolution
  • Before or after Zig code generation
  • Call JSON serializer with full declaration list

Phase 4: Testing

  • Test with multiple SDL headers
  • Verify JSON is valid and well-formed
  • Test edge cases: empty comments, special characters, null values
  • Validate against JSON schema

Implementation Details

Module Structure (src/json_output.zig)

const std = @import("std");
const patterns = @import("patterns.zig");
const Allocator = std.mem.Allocator;

pub fn writeJson(
    allocator: Allocator,
    writer: anytype,
    header_name: []const u8,
    decls: []const patterns.Declaration,
) !void {
    // Write JSON structure
}

fn writeOpaqueType(writer: anytype, opaque: patterns.OpaqueType) !void;
fn writeTypedef(writer: anytype, typedef: patterns.TypedefDecl) !void;
fn writeFunctionPointer(writer: anytype, func_ptr: patterns.FunctionPointerDecl) !void;
fn writeEnum(writer: anytype, enum_decl: patterns.EnumDecl) !void;
fn writeStruct(writer: anytype, struct_decl: patterns.StructDecl) !void;
fn writeUnion(writer: anytype, union_decl: patterns.UnionDecl) !void;
fn writeFlags(writer: anytype, flags: patterns.FlagDecl) !void;
fn writeFunction(writer: anytype, func: patterns.FunctionDecl) !void;

fn escapeString(allocator: Allocator, str: []const u8) ![]u8;

Updates to parser.zig

// Add after argument parsing
var json_output_file: ?[]const u8 = null;

for (args[2..]) |arg| {
    // ... existing flags ...
    const json_prefix = "--generate-json";
    if (std.mem.eql(u8, arg, json_prefix)) {
        json_output_file = ""; // stdout
    } else if (std.mem.startsWith(u8, arg, json_prefix ++ "=")) {
        json_output_file = arg[(json_prefix.len + 1)..];
    }
}

// Add after dependency resolution
if (json_output_file) |json_file| {
    std.debug.print("Generating JSON output...\n", .{});
    if (json_file.len == 0) {
        // Write to stdout
        const stdout = std.io.getStdOut().writer();
        try json_output.writeJson(allocator, stdout, header_path, decls);
    } else {
        // Write to file
        const file = try std.fs.cwd().createFile(json_file, .{});
        defer file.close();
        const writer = file.writer();
        try json_output.writeJson(allocator, writer, header_path, decls);
        std.debug.print("JSON written to: {s}\n", .{json_file});
    }
}

Edge Cases to Handle

  1. Null/Optional Fields: doc_comment, enum values, field comments
  2. String Escaping: Quotes, newlines, backslashes in doc comments
  3. Special Characters: Unicode in comments or identifiers
  4. Empty Arrays: Structs with no fields, enums with no values
  5. Large Output: Efficient writing without loading entire JSON in memory
  6. Mixed Output: Ensure JSON doesn't interfere with stderr debug output

Success Criteria

  • Can parse any SDL header and output valid JSON
  • JSON validates against standard JSON parsers (jq, Python json module)
  • All declaration types are represented
  • Doc comments are preserved with proper escaping
  • Statistics section is accurate
  • Can output to both stdout and file
  • Works alongside existing --output and --mocks flags
  • No memory leaks in JSON generation path

Testing Plan

# Test basic functionality
./parser ../SDL/include/SDL3/SDL_gpu.h --generate-json | jq .

# Test with file output
./parser ../SDL/include/SDL3/SDL_gpu.h --generate-json=gpu.json
cat gpu.json | jq '.statistics'

# Test combined with Zig output
./parser ../SDL/include/SDL3/SDL_video.h --output=video.zig --generate-json=video.json

# Validate JSON structure
python3 -m json.tool gpu.json > /dev/null && echo "Valid JSON"

# Test edge cases
./parser test_small.h --generate-json | jq '.declarations.functions[0].doc_comment'

Future Enhancements (Not in Scope)

  • JSON Schema file generation
  • Filtering by declaration type (e.g., only functions)
  • Dependency graph in JSON format
  • Diff mode between two JSON outputs
  • Machine-readable error format

Iteration Notes

Iteration 1 Considerations:

  • Should we include dependency information in JSON?
    • Decision: No, keep it simple. Focus on declarations only.
  • Should we include source location (line numbers)?
    • Decision: Future enhancement. Not in initial scope.
  • Should JSON output be pretty-printed or compact?
    • Decision: Pretty-printed with 2-space indentation for readability.
  • Error handling: What if JSON write fails partway through?
    • Decision: Write to temporary file first, rename on success. For stdout, fail fast.

Iteration 2 Review:

Looking at the plan again:

Strengths:

  • Clear JSON schema design
  • Comprehensive edge case handling
  • Good testing plan
  • Realistic scope

Potential Issues:

  • Need to handle timestamp generation (use std.time)
  • Should verify that nested JSON writing doesn't cause stack overflow
  • Consider buffering for large outputs
  • Add validation that string escaping handles all C comment styles

Refinements:

  • Add buffered writer wrapper for performance
  • Use std.json.writeStream if available in Zig 0.14
  • Add --json-pretty flag to control formatting
  • Document that all strings are UTF-8 encoded

Final Confidence Assessment:

High Confidence Areas:

  • JSON schema design is complete and covers all declaration types
  • Integration points are well-defined
  • Testing approach is thorough

⚠️ Medium Confidence Areas:

  • String escaping complexity (especially multi-line doc comments)
  • Performance with very large headers
  • Error recovery during JSON generation

Ready to Implement: The plan is comprehensive and actionable. We should proceed with implementation.

Implementation Order

  1. Create src/json_output.zig with basic structure
  2. Implement individual serialization functions
  3. Add command line flag parsing
  4. Integrate into main parser flow
  5. Test with SDL_gpu.h (known good header)
  6. Test with SDL_video.h (larger header)
  7. Test edge cases and error conditions
  8. Update documentation