9.1 KiB
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.stringifyfor structured output
Phase 2: Update Command Line Parsing
- Add
--generate-jsonand--generate-json=<file>flag parsing inparser.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
- Null/Optional Fields: doc_comment, enum values, field comments
- String Escaping: Quotes, newlines, backslashes in doc comments
- Special Characters: Unicode in comments or identifiers
- Empty Arrays: Structs with no fields, enums with no values
- Large Output: Efficient writing without loading entire JSON in memory
- 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.writeStreamif 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
- Create
src/json_output.zigwith basic structure - Implement individual serialization functions
- Add command line flag parsing
- Integrate into main parser flow
- Test with SDL_gpu.h (known good header)
- Test with SDL_video.h (larger header)
- Test edge cases and error conditions
- Update documentation