sdlparser-scrap/src/dependency_resolver.zig

469 lines
16 KiB
Zig

const std = @import("std");
const Allocator = std.mem.Allocator;
const patterns = @import("patterns.zig");
const Declaration = patterns.Declaration;
pub const TypeReference = struct {
name: []const u8,
source_location: []const u8,
};
pub const DependencyResolver = struct {
allocator: Allocator,
referenced_types: std.StringHashMap(void),
defined_types: std.StringHashMap(void),
pub fn init(allocator: Allocator) DependencyResolver {
return .{
.allocator = allocator,
.referenced_types = std.StringHashMap(void).init(allocator),
.defined_types = std.StringHashMap(void).init(allocator),
};
}
pub fn deinit(self: *DependencyResolver) void {
// Free all owned keys in referenced_types
var it = self.referenced_types.keyIterator();
while (it.next()) |key| {
self.allocator.free(key.*);
}
self.referenced_types.deinit();
self.defined_types.deinit();
}
pub fn analyze(self: *DependencyResolver, decls: []const Declaration) !void {
try self.collectDefinedTypes(decls);
try self.collectReferencedTypes(decls);
}
pub fn getMissingTypes(self: *DependencyResolver, allocator: Allocator) ![][]const u8 {
var missing = std.ArrayList([]const u8){};
var it = self.referenced_types.keyIterator();
while (it.next()) |key| {
if (!self.defined_types.contains(key.*)) {
try missing.append(allocator, try allocator.dupe(u8, key.*));
}
}
return try missing.toOwnedSlice(allocator);
}
fn collectDefinedTypes(self: *DependencyResolver, decls: []const Declaration) !void {
for (decls) |decl| {
const type_name = switch (decl) {
.opaque_type => |o| o.name,
.typedef_decl => |t| t.name,
.function_pointer_decl => |fp| fp.name,
.c_type_alias => |a| a.name,
.enum_decl => |e| e.name,
.struct_decl => |s| s.name,
.union_decl => |u| u.name,
.flag_decl => |f| f.name,
.function_decl => continue,
};
try self.defined_types.put(type_name, {});
}
}
fn collectReferencedTypes(self: *DependencyResolver, decls: []const Declaration) !void {
for (decls) |decl| {
switch (decl) {
.function_decl => |func| {
try self.scanType(func.return_type);
for (func.params) |param| {
try self.scanType(param.type_name);
}
},
.function_pointer_decl => |func_ptr| {
try self.scanType(func_ptr.return_type);
for (func_ptr.params) |param| {
try self.scanType(param.type_name);
}
},
.struct_decl => |struct_decl| {
for (struct_decl.fields) |field| {
try self.scanType(field.type_name);
}
},
.union_decl => |union_decl| {
for (union_decl.fields) |field| {
try self.scanType(field.type_name);
}
},
else => {},
}
}
}
fn scanType(self: *DependencyResolver, type_str: []const u8) !void {
const base_type = extractBaseType(type_str);
if (base_type.len > 0 and isSDLType(base_type)) {
// Only add if not already present (avoids duplicates)
if (!self.referenced_types.contains(base_type)) {
// We need to own the string since base_type is a slice into type_str
// which might not have a stable lifetime
const owned = try self.allocator.dupe(u8, base_type);
try self.referenced_types.put(owned, {});
}
}
}
};
pub fn extractBaseType(type_str: []const u8) []const u8 {
var result = type_str;
// Remove leading qualifiers and pointer markers
while (true) {
// Trim whitespace
result = std.mem.trim(u8, result, " \t");
// Remove "const"
if (std.mem.startsWith(u8, result, "const ")) {
result = result["const ".len..];
continue;
}
// Remove "struct"
if (std.mem.startsWith(u8, result, "struct ")) {
result = result["struct ".len..];
continue;
}
// Remove leading "?" (nullable)
if (std.mem.startsWith(u8, result, "?")) {
result = result[1..];
continue;
}
// Remove leading "*" (pointer)
if (std.mem.startsWith(u8, result, "*")) {
result = result[1..];
continue;
}
break;
}
// Trim again
result = std.mem.trim(u8, result, " \t");
// If it contains [*c], extract the part after
if (std.mem.indexOf(u8, result, "[*c]")) |idx| {
result = result[idx + "[*c]".len ..];
result = std.mem.trim(u8, result, " \t");
// Remove const again if present
if (std.mem.startsWith(u8, result, "const ")) {
result = result["const ".len..];
}
result = std.mem.trim(u8, result, " \t");
}
// Remove trailing pointer markers and const qualifiers
while (true) {
result = std.mem.trim(u8, result, " \t");
// Remove trailing "*const" (common pattern)
if (std.mem.endsWith(u8, result, "*const")) {
result = result[0 .. result.len - "*const".len];
continue;
}
// Remove trailing "*"
if (std.mem.endsWith(u8, result, "*")) {
result = result[0 .. result.len - 1];
continue;
}
// Remove trailing "const"
if (std.mem.endsWith(u8, result, " const")) {
result = result[0 .. result.len - " const".len];
continue;
}
break;
}
// Final trim
result = std.mem.trim(u8, result, " \t");
return result;
}
fn isSDLType(type_str: []const u8) bool {
// Check if it's an SDL type (starts with SDL_ or is a known SDL type)
if (std.mem.startsWith(u8, type_str, "SDL_")) {
return true;
}
// Check for known SDL types that don't have SDL_ prefix in Zig bindings
// These are types that would already be converted from SDL_ to their Zig name
const known_types = [_][]const u8{
"Window",
"Rect",
"FColor",
"Color",
"FPoint",
"FlipMode",
"PropertiesID",
"Surface",
"PixelFormat",
};
for (known_types) |known| {
if (std.mem.eql(u8, type_str, known)) {
return true;
}
}
return false;
}
pub fn parseIncludes(allocator: Allocator, source: []const u8) ![][]const u8 {
var includes = std.ArrayList([]const u8){};
var lines = std.mem.splitScalar(u8, source, '\n');
while (lines.next()) |line| {
// Match: #include <SDL3/SDL_something.h>
const trimmed = std.mem.trim(u8, line, " \t\r");
if (std.mem.startsWith(u8, trimmed, "#include <SDL3/")) {
const after_open = "#include <SDL3/".len;
if (std.mem.indexOf(u8, trimmed[after_open..], ">")) |end| {
const header_name = trimmed[after_open..][0..end];
try includes.append(allocator, try allocator.dupe(u8, header_name));
}
}
}
return try includes.toOwnedSlice(allocator);
}
pub fn extractTypeFromHeader(
allocator: Allocator,
header_source: []const u8,
type_name: []const u8,
) !?Declaration {
var scanner = patterns.Scanner.init(allocator, header_source);
const all_decls = try scanner.scan();
defer {
for (all_decls) |decl| {
freeDeclaration(allocator, decl);
}
allocator.free(all_decls);
}
// Find matching declaration
for (all_decls) |decl| {
const decl_name = switch (decl) {
.opaque_type => |o| o.name,
.typedef_decl => |t| t.name,
.enum_decl => |e| e.name,
.struct_decl => |s| s.name,
.union_decl => |u| u.name,
.flag_decl => |f| f.name,
else => continue,
};
if (std.mem.eql(u8, decl_name, type_name)) {
return try cloneDeclaration(allocator, decl);
}
}
return null;
}
fn cloneDeclaration(allocator: Allocator, decl: Declaration) !Declaration {
return switch (decl) {
.opaque_type => |o| .{
.opaque_type = .{
.name = try allocator.dupe(u8, o.name),
.doc_comment = if (o.doc_comment) |doc| try allocator.dupe(u8, doc) else null,
},
},
.typedef_decl => |t| .{
.typedef_decl = .{
.name = try allocator.dupe(u8, t.name),
.underlying_type = try allocator.dupe(u8, t.underlying_type),
.doc_comment = if (t.doc_comment) |doc| try allocator.dupe(u8, doc) else null,
},
},
.function_pointer_decl => |fp| .{
.function_pointer_decl = .{
.name = try allocator.dupe(u8, fp.name),
.return_type = try allocator.dupe(u8, fp.return_type),
.doc_comment = if (fp.doc_comment) |doc| try allocator.dupe(u8, doc) else null,
.params = try cloneParams(allocator, fp.params),
},
},
.c_type_alias => |a| .{
.c_type_alias = .{
.name = try allocator.dupe(u8, a.name),
.doc_comment = if (a.doc_comment) |doc| try allocator.dupe(u8, doc) else null,
},
},
.enum_decl => |e| .{
.enum_decl = .{
.name = try allocator.dupe(u8, e.name),
.doc_comment = if (e.doc_comment) |doc| try allocator.dupe(u8, doc) else null,
.values = try cloneEnumValues(allocator, e.values),
},
},
.struct_decl => |s| .{
.struct_decl = .{
.name = try allocator.dupe(u8, s.name),
.doc_comment = if (s.doc_comment) |doc| try allocator.dupe(u8, doc) else null,
.fields = try cloneFields(allocator, s.fields),
},
},
.union_decl => |u| .{
.union_decl = .{
.name = try allocator.dupe(u8, u.name),
.doc_comment = if (u.doc_comment) |doc| try allocator.dupe(u8, doc) else null,
.fields = try cloneFields(allocator, u.fields),
},
},
.flag_decl => |f| .{
.flag_decl = .{
.name = try allocator.dupe(u8, f.name),
.underlying_type = try allocator.dupe(u8, f.underlying_type),
.doc_comment = if (f.doc_comment) |doc| try allocator.dupe(u8, doc) else null,
.flags = try cloneFlagValues(allocator, f.flags),
},
},
.function_decl => |func| .{
.function_decl = .{
.name = try allocator.dupe(u8, func.name),
.return_type = try allocator.dupe(u8, func.return_type),
.doc_comment = if (func.doc_comment) |doc| try allocator.dupe(u8, doc) else null,
.params = try cloneParams(allocator, func.params),
},
},
};
}
fn cloneEnumValues(allocator: Allocator, values: []const patterns.EnumValue) ![]patterns.EnumValue {
const cloned = try allocator.alloc(patterns.EnumValue, values.len);
for (values, 0..) |val, i| {
cloned[i] = .{
.name = try allocator.dupe(u8, val.name),
.value = if (val.value) |v| try allocator.dupe(u8, v) else null,
.comment = if (val.comment) |c| try allocator.dupe(u8, c) else null,
};
}
return cloned;
}
fn cloneFields(allocator: Allocator, fields: []const patterns.FieldDecl) ![]patterns.FieldDecl {
const cloned = try allocator.alloc(patterns.FieldDecl, fields.len);
for (fields, 0..) |field, i| {
cloned[i] = .{
.name = try allocator.dupe(u8, field.name),
.type_name = try allocator.dupe(u8, field.type_name),
.comment = if (field.comment) |c| try allocator.dupe(u8, c) else null,
};
}
return cloned;
}
fn cloneFlagValues(allocator: Allocator, flags: []const patterns.FlagValue) ![]patterns.FlagValue {
const cloned = try allocator.alloc(patterns.FlagValue, flags.len);
for (flags, 0..) |flag, i| {
cloned[i] = .{
.name = try allocator.dupe(u8, flag.name),
.value = try allocator.dupe(u8, flag.value),
.comment = if (flag.comment) |c| try allocator.dupe(u8, c) else null,
};
}
return cloned;
}
fn cloneParams(allocator: Allocator, params: []const patterns.ParamDecl) ![]patterns.ParamDecl {
const cloned = try allocator.alloc(patterns.ParamDecl, params.len);
for (params, 0..) |param, i| {
cloned[i] = .{
.name = try allocator.dupe(u8, param.name),
.type_name = try allocator.dupe(u8, param.type_name),
};
}
return cloned;
}
fn freeDeclaration(allocator: Allocator, decl: Declaration) void {
switch (decl) {
.opaque_type => |o| {
allocator.free(o.name);
if (o.doc_comment) |doc| allocator.free(doc);
},
.typedef_decl => |t| {
allocator.free(t.name);
allocator.free(t.underlying_type);
if (t.doc_comment) |doc| allocator.free(doc);
},
.function_pointer_decl => |fp| {
allocator.free(fp.name);
allocator.free(fp.return_type);
if (fp.doc_comment) |doc| allocator.free(doc);
for (fp.params) |param| {
allocator.free(param.name);
allocator.free(param.type_name);
}
allocator.free(fp.params);
},
.c_type_alias => |a| {
allocator.free(a.name);
if (a.doc_comment) |doc| allocator.free(doc);
},
.enum_decl => |e| {
allocator.free(e.name);
if (e.doc_comment) |doc| allocator.free(doc);
for (e.values) |val| {
allocator.free(val.name);
if (val.value) |v| allocator.free(v);
if (val.comment) |c| allocator.free(c);
}
allocator.free(e.values);
},
.struct_decl => |s| {
allocator.free(s.name);
if (s.doc_comment) |doc| allocator.free(doc);
for (s.fields) |field| {
allocator.free(field.name);
allocator.free(field.type_name);
if (field.comment) |c| allocator.free(c);
}
allocator.free(s.fields);
},
.union_decl => |u| {
allocator.free(u.name);
if (u.doc_comment) |doc| allocator.free(doc);
for (u.fields) |field| {
allocator.free(field.name);
allocator.free(field.type_name);
if (field.comment) |c| allocator.free(c);
}
allocator.free(u.fields);
},
.flag_decl => |f| {
allocator.free(f.name);
allocator.free(f.underlying_type);
if (f.doc_comment) |doc| allocator.free(doc);
for (f.flags) |flag| {
allocator.free(flag.name);
allocator.free(flag.value);
if (flag.comment) |c| allocator.free(c);
}
allocator.free(f.flags);
},
.function_decl => |func| {
allocator.free(func.name);
allocator.free(func.return_type);
if (func.doc_comment) |doc| allocator.free(doc);
for (func.params) |param| {
allocator.free(param.name);
allocator.free(param.type_name);
}
allocator.free(func.params);
},
}
}