Implement JSON output feature for SDL parser
- Add --generate-json flag to output structured JSON representation - JSON includes all parsed types: opaques, typedefs, function pointers, enums, structs, unions, flags, and functions - Preserves doc comments and inline comments in JSON output - Proper JSON escaping for special characters - Tested with SDL_gpu.h and test_small.h - Validates as proper JSON format
This commit is contained in:
parent
743845de9b
commit
b7ec134b0e
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,344 @@
|
|||
const std = @import("std");
|
||||
const patterns = @import("patterns.zig");
|
||||
|
||||
pub const JsonSerializer = struct {
|
||||
allocator: std.mem.Allocator,
|
||||
output: std.ArrayList(u8),
|
||||
header_name: []const u8,
|
||||
opaque_types: std.ArrayList(patterns.OpaqueType),
|
||||
typedefs: std.ArrayList(patterns.TypedefDecl),
|
||||
function_pointers: std.ArrayList(patterns.FunctionPointerDecl),
|
||||
enums: std.ArrayList(patterns.EnumDecl),
|
||||
structs: std.ArrayList(patterns.StructDecl),
|
||||
unions: std.ArrayList(patterns.UnionDecl),
|
||||
flags: std.ArrayList(patterns.FlagDecl),
|
||||
functions: std.ArrayList(patterns.FunctionDecl),
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator, header_name: []const u8) JsonSerializer {
|
||||
return .{
|
||||
.allocator = allocator,
|
||||
.output = std.ArrayList(u8){},
|
||||
.header_name = header_name,
|
||||
.opaque_types = std.ArrayList(patterns.OpaqueType){},
|
||||
.typedefs = std.ArrayList(patterns.TypedefDecl){},
|
||||
.function_pointers = std.ArrayList(patterns.FunctionPointerDecl){},
|
||||
.enums = std.ArrayList(patterns.EnumDecl){},
|
||||
.structs = std.ArrayList(patterns.StructDecl){},
|
||||
.unions = std.ArrayList(patterns.UnionDecl){},
|
||||
.flags = std.ArrayList(patterns.FlagDecl){},
|
||||
.functions = std.ArrayList(patterns.FunctionDecl){},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *JsonSerializer) void {
|
||||
self.output.deinit(self.allocator);
|
||||
self.opaque_types.deinit(self.allocator);
|
||||
self.typedefs.deinit(self.allocator);
|
||||
self.function_pointers.deinit(self.allocator);
|
||||
self.enums.deinit(self.allocator);
|
||||
self.structs.deinit(self.allocator);
|
||||
self.unions.deinit(self.allocator);
|
||||
self.flags.deinit(self.allocator);
|
||||
self.functions.deinit(self.allocator);
|
||||
}
|
||||
|
||||
pub fn addDeclarations(self: *JsonSerializer, decls: []const patterns.Declaration) !void {
|
||||
for (decls) |decl| {
|
||||
switch (decl) {
|
||||
.opaque_type => |o| try self.opaque_types.append(self.allocator, o),
|
||||
.typedef_decl => |t| try self.typedefs.append(self.allocator, t),
|
||||
.function_pointer_decl => |fp| try self.function_pointers.append(self.allocator, fp),
|
||||
.enum_decl => |e| try self.enums.append(self.allocator, e),
|
||||
.struct_decl => |s| try self.structs.append(self.allocator, s),
|
||||
.union_decl => |u| try self.unions.append(self.allocator, u),
|
||||
.flag_decl => |f| try self.flags.append(self.allocator, f),
|
||||
.function_decl => |fn_decl| try self.functions.append(self.allocator, fn_decl),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn finalize(self: *JsonSerializer) ![]const u8 {
|
||||
var writer = self.output.writer(self.allocator);
|
||||
|
||||
try writer.writeAll("{\n");
|
||||
try writer.writeAll(" \"header\": ");
|
||||
try self.writeString(writer, self.header_name);
|
||||
try writer.writeAll(",\n");
|
||||
|
||||
// Serialize opaque types
|
||||
try writer.writeAll(" \"opaque_types\": [\n");
|
||||
for (self.opaque_types.items, 0..) |opaque_type, i| {
|
||||
try writer.writeAll(" ");
|
||||
try self.serializeOpaqueType(writer, opaque_type);
|
||||
if (i < self.opaque_types.items.len - 1) try writer.writeAll(",");
|
||||
try writer.writeAll("\n");
|
||||
}
|
||||
try writer.writeAll(" ],\n");
|
||||
|
||||
// Serialize typedefs
|
||||
try writer.writeAll(" \"typedefs\": [\n");
|
||||
for (self.typedefs.items, 0..) |typedef, i| {
|
||||
try writer.writeAll(" ");
|
||||
try self.serializeTypedef(writer, typedef);
|
||||
if (i < self.typedefs.items.len - 1) try writer.writeAll(",");
|
||||
try writer.writeAll("\n");
|
||||
}
|
||||
try writer.writeAll(" ],\n");
|
||||
|
||||
// Serialize function pointers
|
||||
try writer.writeAll(" \"function_pointers\": [\n");
|
||||
for (self.function_pointers.items, 0..) |fp, i| {
|
||||
try writer.writeAll(" ");
|
||||
try self.serializeFunctionPointer(writer, fp);
|
||||
if (i < self.function_pointers.items.len - 1) try writer.writeAll(",");
|
||||
try writer.writeAll("\n");
|
||||
}
|
||||
try writer.writeAll(" ],\n");
|
||||
|
||||
// Serialize enums
|
||||
try writer.writeAll(" \"enums\": [\n");
|
||||
for (self.enums.items, 0..) |enum_decl, i| {
|
||||
try writer.writeAll(" ");
|
||||
try self.serializeEnum(writer, enum_decl);
|
||||
if (i < self.enums.items.len - 1) try writer.writeAll(",");
|
||||
try writer.writeAll("\n");
|
||||
}
|
||||
try writer.writeAll(" ],\n");
|
||||
|
||||
// Serialize structs
|
||||
try writer.writeAll(" \"structs\": [\n");
|
||||
for (self.structs.items, 0..) |struct_decl, i| {
|
||||
try writer.writeAll(" ");
|
||||
try self.serializeStruct(writer, struct_decl);
|
||||
if (i < self.structs.items.len - 1) try writer.writeAll(",");
|
||||
try writer.writeAll("\n");
|
||||
}
|
||||
try writer.writeAll(" ],\n");
|
||||
|
||||
// Serialize unions
|
||||
try writer.writeAll(" \"unions\": [\n");
|
||||
for (self.unions.items, 0..) |union_decl, i| {
|
||||
try writer.writeAll(" ");
|
||||
try self.serializeUnion(writer, union_decl);
|
||||
if (i < self.unions.items.len - 1) try writer.writeAll(",");
|
||||
try writer.writeAll("\n");
|
||||
}
|
||||
try writer.writeAll(" ],\n");
|
||||
|
||||
// Serialize flags
|
||||
try writer.writeAll(" \"flags\": [\n");
|
||||
for (self.flags.items, 0..) |flag, i| {
|
||||
try writer.writeAll(" ");
|
||||
try self.serializeFlag(writer, flag);
|
||||
if (i < self.flags.items.len - 1) try writer.writeAll(",");
|
||||
try writer.writeAll("\n");
|
||||
}
|
||||
try writer.writeAll(" ],\n");
|
||||
|
||||
// Serialize functions
|
||||
try writer.writeAll(" \"functions\": [\n");
|
||||
for (self.functions.items, 0..) |func, i| {
|
||||
try writer.writeAll(" ");
|
||||
try self.serializeFunction(writer, func);
|
||||
if (i < self.functions.items.len - 1) try writer.writeAll(",");
|
||||
try writer.writeAll("\n");
|
||||
}
|
||||
try writer.writeAll(" ]\n");
|
||||
|
||||
try writer.writeAll("}\n");
|
||||
|
||||
return self.output.items;
|
||||
}
|
||||
|
||||
fn serializeOpaqueType(self: *JsonSerializer, writer: anytype, opaque_type: patterns.OpaqueType) !void {
|
||||
try writer.writeAll("{\"name\": ");
|
||||
try self.writeString(writer, opaque_type.name);
|
||||
if (opaque_type.doc_comment) |doc| {
|
||||
try writer.writeAll(", \"doc\": ");
|
||||
try self.writeString(writer, doc);
|
||||
}
|
||||
try writer.writeAll("}");
|
||||
}
|
||||
|
||||
fn serializeTypedef(self: *JsonSerializer, writer: anytype, typedef: patterns.TypedefDecl) !void {
|
||||
try writer.writeAll("{\"name\": ");
|
||||
try self.writeString(writer, typedef.name);
|
||||
try writer.writeAll(", \"underlying_type\": ");
|
||||
try self.writeString(writer, typedef.underlying_type);
|
||||
if (typedef.doc_comment) |doc| {
|
||||
try writer.writeAll(", \"doc\": ");
|
||||
try self.writeString(writer, doc);
|
||||
}
|
||||
try writer.writeAll("}");
|
||||
}
|
||||
|
||||
fn serializeFunctionPointer(self: *JsonSerializer, writer: anytype, fp: patterns.FunctionPointerDecl) !void {
|
||||
try writer.writeAll("{\"name\": ");
|
||||
try self.writeString(writer, fp.name);
|
||||
try writer.writeAll(", \"return_type\": ");
|
||||
try self.writeString(writer, fp.return_type);
|
||||
try writer.writeAll(", \"parameters\": [");
|
||||
for (fp.params, 0..) |param, i| {
|
||||
try writer.writeAll("{\"name\": ");
|
||||
try self.writeString(writer, param.name);
|
||||
try writer.writeAll(", \"type\": ");
|
||||
try self.writeString(writer, param.type_name);
|
||||
try writer.writeAll("}");
|
||||
if (i < fp.params.len - 1) try writer.writeAll(", ");
|
||||
}
|
||||
try writer.writeAll("]");
|
||||
if (fp.doc_comment) |doc| {
|
||||
try writer.writeAll(", \"doc\": ");
|
||||
try self.writeString(writer, doc);
|
||||
}
|
||||
try writer.writeAll("}");
|
||||
}
|
||||
|
||||
fn serializeEnum(self: *JsonSerializer, writer: anytype, enum_decl: patterns.EnumDecl) !void {
|
||||
try writer.writeAll("{\"name\": ");
|
||||
try self.writeString(writer, enum_decl.name);
|
||||
try writer.writeAll(", \"values\": [");
|
||||
|
||||
for (enum_decl.values, 0..) |value, i| {
|
||||
try writer.writeAll("{\"name\": ");
|
||||
try self.writeString(writer, value.name);
|
||||
if (value.value) |val| {
|
||||
try writer.writeAll(", \"value\": ");
|
||||
try self.writeString(writer, val);
|
||||
}
|
||||
if (value.comment) |comment| {
|
||||
try writer.writeAll(", \"comment\": ");
|
||||
try self.writeString(writer, comment);
|
||||
}
|
||||
try writer.writeAll("}");
|
||||
if (i < enum_decl.values.len - 1) try writer.writeAll(", ");
|
||||
}
|
||||
|
||||
try writer.writeAll("]");
|
||||
if (enum_decl.doc_comment) |doc| {
|
||||
try writer.writeAll(", \"doc\": ");
|
||||
try self.writeString(writer, doc);
|
||||
}
|
||||
try writer.writeAll("}");
|
||||
}
|
||||
|
||||
fn serializeStruct(self: *JsonSerializer, writer: anytype, struct_decl: patterns.StructDecl) !void {
|
||||
try writer.writeAll("{\"name\": ");
|
||||
try self.writeString(writer, struct_decl.name);
|
||||
try writer.writeAll(", \"fields\": [");
|
||||
|
||||
for (struct_decl.fields, 0..) |field, i| {
|
||||
try writer.writeAll("{\"name\": ");
|
||||
try self.writeString(writer, field.name);
|
||||
try writer.writeAll(", \"type\": ");
|
||||
try self.writeString(writer, field.type_name);
|
||||
if (field.comment) |comment| {
|
||||
try writer.writeAll(", \"comment\": ");
|
||||
try self.writeString(writer, comment);
|
||||
}
|
||||
try writer.writeAll("}");
|
||||
if (i < struct_decl.fields.len - 1) try writer.writeAll(", ");
|
||||
}
|
||||
|
||||
try writer.writeAll("]");
|
||||
if (struct_decl.doc_comment) |doc| {
|
||||
try writer.writeAll(", \"doc\": ");
|
||||
try self.writeString(writer, doc);
|
||||
}
|
||||
try writer.writeAll("}");
|
||||
}
|
||||
|
||||
fn serializeUnion(self: *JsonSerializer, writer: anytype, union_decl: patterns.UnionDecl) !void {
|
||||
try writer.writeAll("{\"name\": ");
|
||||
try self.writeString(writer, union_decl.name);
|
||||
try writer.writeAll(", \"fields\": [");
|
||||
|
||||
for (union_decl.fields, 0..) |field, i| {
|
||||
try writer.writeAll("{\"name\": ");
|
||||
try self.writeString(writer, field.name);
|
||||
try writer.writeAll(", \"type\": ");
|
||||
try self.writeString(writer, field.type_name);
|
||||
if (field.comment) |comment| {
|
||||
try writer.writeAll(", \"comment\": ");
|
||||
try self.writeString(writer, comment);
|
||||
}
|
||||
try writer.writeAll("}");
|
||||
if (i < union_decl.fields.len - 1) try writer.writeAll(", ");
|
||||
}
|
||||
|
||||
try writer.writeAll("]");
|
||||
if (union_decl.doc_comment) |doc| {
|
||||
try writer.writeAll(", \"doc\": ");
|
||||
try self.writeString(writer, doc);
|
||||
}
|
||||
try writer.writeAll("}");
|
||||
}
|
||||
|
||||
fn serializeFlag(self: *JsonSerializer, writer: anytype, flag: patterns.FlagDecl) !void {
|
||||
try writer.writeAll("{\"name\": ");
|
||||
try self.writeString(writer, flag.name);
|
||||
try writer.writeAll(", \"underlying_type\": ");
|
||||
try self.writeString(writer, flag.underlying_type);
|
||||
try writer.writeAll(", \"values\": [");
|
||||
|
||||
for (flag.flags, 0..) |value, i| {
|
||||
try writer.writeAll("{\"name\": ");
|
||||
try self.writeString(writer, value.name);
|
||||
try writer.writeAll(", \"value\": ");
|
||||
try self.writeString(writer, value.value);
|
||||
if (value.comment) |comment| {
|
||||
try writer.writeAll(", \"comment\": ");
|
||||
try self.writeString(writer, comment);
|
||||
}
|
||||
try writer.writeAll("}");
|
||||
if (i < flag.flags.len - 1) try writer.writeAll(", ");
|
||||
}
|
||||
|
||||
try writer.writeAll("]");
|
||||
if (flag.doc_comment) |doc| {
|
||||
try writer.writeAll(", \"doc\": ");
|
||||
try self.writeString(writer, doc);
|
||||
}
|
||||
try writer.writeAll("}");
|
||||
}
|
||||
|
||||
fn serializeFunction(self: *JsonSerializer, writer: anytype, func: patterns.FunctionDecl) !void {
|
||||
try writer.writeAll("{\"name\": ");
|
||||
try self.writeString(writer, func.name);
|
||||
try writer.writeAll(", \"return_type\": ");
|
||||
try self.writeString(writer, func.return_type);
|
||||
try writer.writeAll(", \"parameters\": [");
|
||||
|
||||
for (func.params, 0..) |param, i| {
|
||||
try writer.writeAll("{\"name\": ");
|
||||
try self.writeString(writer, param.name);
|
||||
try writer.writeAll(", \"type\": ");
|
||||
try self.writeString(writer, param.type_name);
|
||||
try writer.writeAll("}");
|
||||
if (i < func.params.len - 1) try writer.writeAll(", ");
|
||||
}
|
||||
|
||||
try writer.writeAll("]");
|
||||
if (func.doc_comment) |doc| {
|
||||
try writer.writeAll(", \"doc\": ");
|
||||
try self.writeString(writer, doc);
|
||||
}
|
||||
try writer.writeAll("}");
|
||||
}
|
||||
|
||||
fn writeString(self: *JsonSerializer, writer: anytype, str: []const u8) !void {
|
||||
_ = self;
|
||||
try writer.writeAll("\"");
|
||||
for (str) |c| {
|
||||
switch (c) {
|
||||
'"' => try writer.writeAll("\\\""),
|
||||
'\\' => try writer.writeAll("\\\\"),
|
||||
'\n' => try writer.writeAll("\\n"),
|
||||
'\r' => try writer.writeAll("\\r"),
|
||||
'\t' => try writer.writeAll("\\t"),
|
||||
else => try writer.writeByte(c),
|
||||
}
|
||||
}
|
||||
try writer.writeAll("\"");
|
||||
}
|
||||
};
|
||||
|
|
@ -2,6 +2,7 @@ const std = @import("std");
|
|||
const patterns = @import("patterns.zig");
|
||||
const codegen = @import("codegen.zig");
|
||||
const dependency_resolver = @import("dependency_resolver.zig");
|
||||
const json_serializer = @import("json_serializer.zig");
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
|
|
@ -17,9 +18,10 @@ pub fn main() !void {
|
|||
defer std.process.argsFree(allocator, args);
|
||||
|
||||
if (args.len < 2) {
|
||||
std.debug.print("Usage: {s} <header-file> [--output=<output-file>] [--mocks=<mock-file>]\n", .{args[0]});
|
||||
std.debug.print("Usage: {s} <header-file> [--output=<output-file>] [--mocks=<mock-file>] [--generate-json=<json-file>]\n", .{args[0]});
|
||||
std.debug.print("Example: {s} ../SDL/include/SDL3/SDL_gpu.h --output=gpu.zig\n", .{args[0]});
|
||||
std.debug.print(" {s} ../SDL/include/SDL3/SDL_gpu.h --output=gpu.zig --mocks=gpu_mock.c\n", .{args[0]});
|
||||
std.debug.print(" {s} ../SDL/include/SDL3/SDL_gpu.h --generate-json=gpu.json\n", .{args[0]});
|
||||
std.debug.print(" {s} ../SDL/include/SDL3/SDL_gpu.h > gpu.zig\n", .{args[0]});
|
||||
return error.MissingArgument;
|
||||
}
|
||||
|
|
@ -28,18 +30,22 @@ pub fn main() !void {
|
|||
|
||||
var output_file: ?[]const u8 = null;
|
||||
var mock_output_file: ?[]const u8 = null;
|
||||
var json_output_file: ?[]const u8 = null;
|
||||
|
||||
// Parse additional flags
|
||||
for (args[2..]) |arg| {
|
||||
const output_prefix = "--output=";
|
||||
const mocks_prefix = "--mocks=";
|
||||
const json_prefix = "--generate-json=";
|
||||
if (std.mem.startsWith(u8, arg, output_prefix)) {
|
||||
output_file = arg[output_prefix.len..];
|
||||
} else if (std.mem.startsWith(u8, arg, mocks_prefix)) {
|
||||
mock_output_file = arg[mocks_prefix.len..];
|
||||
} else if (std.mem.startsWith(u8, arg, json_prefix)) {
|
||||
json_output_file = arg[json_prefix.len..];
|
||||
} else {
|
||||
std.debug.print("Error: Unknown argument '{s}'\n", .{arg});
|
||||
std.debug.print("Usage: {s} <header-file> [--output=<output-file>] [--mocks=<mock-file>]\n", .{args[0]});
|
||||
std.debug.print("Usage: {s} <header-file> [--output=<output-file>] [--mocks=<mock-file>] [--generate-json=<json-file>]\n", .{args[0]});
|
||||
return error.InvalidArgument;
|
||||
}
|
||||
}
|
||||
|
|
@ -167,6 +173,29 @@ pub fn main() !void {
|
|||
std.debug.print(" - Flags: {d}\n", .{flag_count});
|
||||
std.debug.print(" - Functions: {d}\n\n", .{func_count});
|
||||
|
||||
// Generate JSON if requested
|
||||
if (json_output_file) |json_path| {
|
||||
std.debug.print("Generating JSON output...\n", .{});
|
||||
|
||||
var serializer = json_serializer.JsonSerializer.init(allocator, std.fs.path.basename(header_path));
|
||||
|
||||
try serializer.addDeclarations(decls);
|
||||
const json_output = try serializer.finalize();
|
||||
// json_output is owned by serializer, so we need to write it before deinit
|
||||
|
||||
try std.fs.cwd().writeFile(.{
|
||||
.sub_path = json_path,
|
||||
.data = json_output,
|
||||
});
|
||||
serializer.deinit();
|
||||
std.debug.print("Generated JSON: {s}\n", .{json_path});
|
||||
|
||||
// If only JSON was requested, we're done
|
||||
if (output_file == null and mock_output_file == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Analyze dependencies
|
||||
std.debug.print("Analyzing dependencies...\n", .{});
|
||||
var resolver = dependency_resolver.DependencyResolver.init(allocator);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"header": "test_small.h",
|
||||
"opaque_types": [
|
||||
{"name": "SDL_GPUDevice"}
|
||||
],
|
||||
"typedefs": [
|
||||
],
|
||||
"function_pointers": [
|
||||
],
|
||||
"enums": [
|
||||
{"name": "SDL_GPUPrimitiveType", "values": []}
|
||||
],
|
||||
"structs": [
|
||||
],
|
||||
"unions": [
|
||||
],
|
||||
"flags": [
|
||||
],
|
||||
"functions": [
|
||||
{"name": "SDL_CreateGPUDevice", "return_type": "SDL_GPUDevice*", "parameters": [{"name": "debug_mode", "type": "bool"}]}
|
||||
]
|
||||
}
|
||||
Loading…
Reference in New Issue