Add JSON output feature plan
This commit is contained in:
parent
a2ab0f0f21
commit
743845de9b
|
|
@ -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=<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`)
|
||||
|
||||
```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
|
||||
Loading…
Reference in New Issue