From 743845de9b9eb34ffac0dee0170d63dc563982ec Mon Sep 17 00:00:00 2001 From: Peterino2 Date: Thu, 22 Jan 2026 15:26:05 -0800 Subject: [PATCH] Add JSON output feature plan --- lib/sdl3/parser/docs/JSON_OUTPUT_PLAN.md | 306 +++++++++++++++++++++++ 1 file changed, 306 insertions(+) create mode 100644 lib/sdl3/parser/docs/JSON_OUTPUT_PLAN.md diff --git a/lib/sdl3/parser/docs/JSON_OUTPUT_PLAN.md b/lib/sdl3/parser/docs/JSON_OUTPUT_PLAN.md new file mode 100644 index 0000000..259c419 --- /dev/null +++ b/lib/sdl3/parser/docs/JSON_OUTPUT_PLAN.md @@ -0,0 +1,306 @@ +# 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 +```json +{ + "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 +```bash +# 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=` 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`) + +```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` + +```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 + +```bash +# 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