diff --git a/generator/registry.zig b/generator/registry.zig index 4ab5a80..eddf29a 100644 --- a/generator/registry.zig +++ b/generator/registry.zig @@ -60,6 +60,7 @@ pub const Container = struct { name: []const u8, field_type: TypeInfo, bits: ?usize, + is_buffer_len: bool, }; fields: []Field, @@ -99,6 +100,7 @@ pub const Command = struct { pub const Param = struct { name: []const u8, param_type: TypeInfo, + is_buffer_len: bool, }; params: []Param, @@ -108,9 +110,10 @@ pub const Command = struct { }; pub const Pointer = struct { - pub const PointerSize = enum { + pub const PointerSize = union(enum) { one, - many, // The length is given by some expression + many, // The length is given by some complex expression, possibly involving another field + other_field: []const u8, // The length is given by some other field or parameter zero_terminated }; diff --git a/generator/registry/c-parse.zig b/generator/registry/c-parse.zig index 5bdb84e..c01a01b 100644 --- a/generator/registry/c-parse.zig +++ b/generator/registry/c-parse.zig @@ -246,6 +246,7 @@ pub fn parseMember(allocator: *Allocator, xctok: *XmlCTokenizer) !registry.Conta .name = decl.name orelse return error.MissingTypeIdentifier, .field_type = decl.decl_type, .bits = null, + .is_buffer_len = false, }; if (try xctok.peek()) |tok| { @@ -404,6 +405,7 @@ fn parseFnPtrSuffix(allocator: *Allocator, xctok: *XmlCTokenizer, return_type: T try params.append(.{ .name = first_param.name.?, .param_type = first_param.decl_type, + .is_buffer_len = false, }); while (true) { @@ -417,6 +419,7 @@ fn parseFnPtrSuffix(allocator: *Allocator, xctok: *XmlCTokenizer, return_type: T try params.append(.{ .name = decl.name orelse return error.MissingTypeIdentifier, .param_type = decl.decl_type, + .is_buffer_len = false, }); } @@ -455,7 +458,7 @@ fn parsePointers(allocator: *Allocator, xctok: *XmlCTokenizer, inner_const: bool type_info = .{ .pointer = .{ .is_const = is_const or first_const, - .is_optional = true, // set elsewhere + .is_optional = false, // set elsewhere .size = .one, // set elsewhere .child = child, }, diff --git a/generator/registry/parse.zig b/generator/registry/parse.zig index 0be66c4..907e71e 100644 --- a/generator/registry/parse.zig +++ b/generator/registry/parse.zig @@ -173,18 +173,31 @@ fn parseContainer(allocator: *Allocator, ty: *xml.Element, is_union: bool) !regi while (it.next()) |member| { var xctok = cparse.XmlCTokenizer.init(member); members[i] = try cparse.parseMember(allocator, &xctok); - try parsePointerMeta(&members[i].field_type, member); i += 1; } + members = allocator.shrink(members, i); + const decl_type = registry.DeclarationType{ + .container = .{ + .fields = members, + .is_union = is_union, + } + }; + + it = ty.findChildrenByTag("member"); + for (members) |*member| { + const member_elem = it.next().?; + try parsePointerMeta(decl_type, &member.field_type, member_elem); + + // pNext isn't properly marked as optional, so just manually override it, + if (mem.eql(u8, member.name, "pNext")) { + member.field_type.pointer.is_optional = true; + } + } + return registry.Declaration{ .name = name, - .decl_type = .{ - .container = .{ - .fields = allocator.shrink(members, i), - .is_union = is_union, - } - }, + .decl_type = decl_type, }; } @@ -193,7 +206,27 @@ fn parseFuncPointer(allocator: *Allocator, ty: *xml.Element) !registry.Declarati return try cparse.parseTypedef(allocator, &xctok); } -fn lenToPointerSize(len: []const u8) registry.Pointer.PointerSize { +fn lenToPointerSize(decl_type: registry.DeclarationType, len: []const u8) registry.Pointer.PointerSize { + switch (decl_type) { + .command => |command| { + for (command.params) |*param| { + if (mem.eql(u8, param.name, len)) { + param.is_buffer_len = true; + return .{.other_field = param.name}; + } + } + }, + .container => |container| { + for (container.fields) |*field| { + if (mem.eql(u8, field.name, len)) { + field.is_buffer_len = true; + return .{.other_field = field.name}; + } + } + }, + else => {}, + } + if (mem.eql(u8, len, "null-terminated")) { return .zero_terminated; } else { @@ -201,12 +234,13 @@ fn lenToPointerSize(len: []const u8) registry.Pointer.PointerSize { } } -fn parsePointerMeta(type_info: *registry.TypeInfo, elem: *xml.Element) !void { +fn parsePointerMeta(decl_type: registry.DeclarationType, type_info: *registry.TypeInfo, elem: *xml.Element) !void { if (elem.getAttribute("len")) |lens| { var it = mem.split(lens, ","); var current_type_info = type_info; while (current_type_info.* == .pointer) { - const size = if (it.next()) |len_str| lenToPointerSize(len_str) else .one; + // TODO: Check altlen + const size = if (it.next()) |len_str| lenToPointerSize(decl_type, len_str) else .one; current_type_info.pointer.size = size; current_type_info = current_type_info.pointer.child; } @@ -372,8 +406,11 @@ fn parseCommand(allocator: *Allocator, elem: *xml.Element) !registry.Declaration while (it.next()) |param| { var xctok = cparse.XmlCTokenizer.init(param); const decl = try cparse.parseParamOrProto(allocator, &xctok); - params[i] = .{.name = decl.name, .param_type = decl.decl_type.typedef}; - try parsePointerMeta(¶ms[i].param_type, param); + params[i] = .{ + .name = decl.name, + .param_type = decl.decl_type.typedef, + .is_buffer_len = false, + }; i += 1; } @@ -390,16 +427,25 @@ fn parseCommand(allocator: *Allocator, elem: *xml.Element) !registry.Declaration else &[_][]const u8{}; + params = allocator.shrink(params, i); + var decl_type = registry.DeclarationType{ + .command = .{ + .params = params, + .return_type = return_type, + .success_codes = success_codes, + .error_codes = error_codes, + } + }; + + it = elem.findChildrenByTag("param"); + for (params) |*param| { + const param_elem = it.next().?; + try parsePointerMeta(decl_type, ¶m.param_type, param_elem); + } + return registry.Declaration{ .name = command_decl.name, - .decl_type = .{ - .command = .{ - .params = allocator.shrink(params, i), - .return_type = return_type, - .success_codes = success_codes, - .error_codes = error_codes, - } - } + .decl_type = decl_type, }; } diff --git a/generator/render.zig b/generator/render.zig index 02fbac4..7dc8cd1 100644 --- a/generator/render.zig +++ b/generator/render.zig @@ -88,6 +88,15 @@ fn Renderer(comptime WriterType: type) type { tag: ?[]const u8, }; + const ParamType = enum { + in_pointer, + out_pointer, + bitflags, + mut_buffer_len, + buffer_len, + other, + }; + writer: WriterType, allocator: *Allocator, registry: *const reg.Registry, @@ -131,7 +140,7 @@ fn Renderer(comptime WriterType: type) type { } } - fn exctractBitflagName(self: Self, name: []const u8) ?BitflagName { + fn extractBitflagName(self: Self, name: []const u8) ?BitflagName { const tag = util.getAuthorTag(name, self.registry.tags); const base_name = if (tag) |tag_name| name[0 .. name.len - tag_name.len] else name; @@ -145,6 +154,41 @@ fn Renderer(comptime WriterType: type) type { }; } + fn classifyParam(self: Self, param: reg.Command.Param) !ParamType { + switch (param.param_type) { + .pointer => |ptr| { + if (param.is_buffer_len) { + if (ptr.is_const or ptr.is_optional) { + return error.InvalidRegistry; + } + + return .mut_buffer_len; + } + + const child_is_void = ptr.child.* == .name and mem.eql(u8, ptr.child.name, "void"); + if (!child_is_void and ptr.size == .one and !ptr.is_optional) { + if (ptr.is_const) { + return .in_pointer; + } else { + return .out_pointer; + } + } + }, + .name => |name| { + if (self.extractBitflagName(param.param_type.name) != null) { + return .bitflags; + } + }, + else => {}, + } + + if (param.is_buffer_len) { + return .buffer_len; + } + + return .other; + } + fn render(self: *Self) !void { try self.writer.writeAll(preamble); @@ -157,6 +201,7 @@ fn Renderer(comptime WriterType: type) type { } try self.renderCommandPtrs(); + try self.renderWrappers(); } fn renderApiConstant(self: *Self, api_constant: reg.ApiConstant) !void { @@ -238,7 +283,7 @@ fn Renderer(comptime WriterType: type) type { if (builtin_types.get(name)) |zig_name| { try self.writer.writeAll(zig_name); return; - } else if (self.exctractBitflagName(name)) |bitflag_name| { + } else if (self.extractBitflagName(name)) |bitflag_name| { try self.writer.print("{}Flags{}", .{ util.trimVkNamespace(bitflag_name.base_name), @as([]const u8, if (bitflag_name.tag) |tag| tag else "") @@ -286,7 +331,7 @@ fn Renderer(comptime WriterType: type) type { const size = if (child_is_void) .one else pointer.size; switch (size) { .one => try self.writer.writeByte('*'), - .many => try self.writer.writeAll("[*]"), + .many, .other_field => try self.writer.writeAll("[*]"), .zero_terminated => try self.writer.writeAll("[*:0]"), } @@ -412,6 +457,38 @@ fn Renderer(comptime WriterType: type) type { } try self.writer.writeAll("};\n"); + + if (mem.eql(u8, name, "VkResult")) { + try self.renderResultErrorSet(enumeration); + } + } + + fn renderResultAsError(self: *Self, name: []const u8) !void { + const error_prefix = "VK_ERROR_"; + if (!mem.startsWith(u8, name, error_prefix)) { + return error.NotErrorResult; + } + + try self.writeIdentifierWithCase(.title, name[error_prefix.len ..]); + } + + fn renderResultErrorSet(self: *Self, enumeration: reg.Enum) !void { + try self.writer.writeAll("pub const Error = error{\n"); + const error_prefix = "VK_ERROR_"; + for (enumeration.fields) |field| { + if (field.value != .int) { + continue; + } + + self.renderResultAsError(field.name) catch |err| switch (err) { + error.NotErrorResult => continue, + else => |narrowed| return narrowed + }; + + try self.writer.writeAll(",\n"); + } + + try self.writer.writeAll("};\n"); } fn renderBitmaskBits(self: *Self, name: []const u8, bits: reg.Enum) !void { @@ -470,7 +547,7 @@ fn Renderer(comptime WriterType: type) type { fn renderAlias(self: *Self, name: []const u8, alias: reg.Alias) !void { if (alias.target == .other_command) { return; - } else if (self.exctractBitflagName(name) != null) { + } else if (self.extractBitflagName(name) != null) { // Don't make aliases of the bitflag names, as those are replaced by just the flags type return; } @@ -526,28 +603,188 @@ fn Renderer(comptime WriterType: type) type { try self.writer.writeAll(";\n"); } + // try self.writer.writeAll( + // \\const commands = struct { + // \\ const CommandInfo = struct { + // \\ Pfn: type, + // \\ link_name: [:0]const u8, + // \\ }; + // \\ + // ); + // for (self.registry.decls) |decl| { + // if (decl.decl_type != .command) { + // continue; + // } + + // try self.writer.writeAll("const "); + // try self.renderTypeName(decl.name); + // try self.writer.print( + // " = CommandInfo{{ .Pfn = PFN_{}, .link_name = \"{}\" }};\n", + // .{decl.name, decl.name} + // ); + // } + + // try self.writer.writeAll("};\n"); + } + + fn renderWrappers(self: *Self) !void { try self.writer.writeAll( - \\const commands = struct { - \\ const CommandInfo = struct { - \\ Pfn: type, - \\ link_name: [:0]const u8, - \\ }; + \\pub fn Wrapper(comptime Self: type) type { + \\ return struct { \\ ); - for (self.registry.decls) |decl| { - if (decl.decl_type != .command) { - continue; - } - try self.writer.writeAll("const "); - try self.renderTypeName(decl.name); - try self.writer.print( - " = CommandInfo{{ .Pfn = PFN_{}, .link_name = \"{}\" }};\n", - .{decl.name, decl.name} - ); + for (self.registry.decls) |decl| { + if (decl.decl_type == .command) { + try self.renderWrapper(decl.name, decl.decl_type.command); + } } - try self.writer.writeAll("};\n"); + try self.writer.writeAll("};}\n"); + } + + fn inPointerAdjustedName(name: []const u8) []const u8 { + var it = util.SegmentIterator.init(name); + return if (mem.eql(u8, it.next().?, "p")) + name[1..] + else + name; + } + + fn renderWrapperPrototype(self: *Self, name: []const u8, command: reg.Command) !void { + try self.writer.writeAll("pub fn "); + try self.writeIdentifierWithCase(.camel, util.trimVkNamespace(name)); + try self.writer.writeAll("(self: Self, "); + + for (command.params) |param| { + switch (try self.classifyParam(param)) { + .in_pointer => { + // Remove one pointer level + try self.writeIdentifierWithCase(.snake, inPointerAdjustedName(param.name)); + try self.writer.writeAll(": "); + try self.renderTypeInfo(param.param_type.pointer.child.*); + }, + .out_pointer => continue, // Return vaPlue + .bitflags, // Special stuff handled in renderWrapperCall + .buffer_len, + .mut_buffer_len, + .other => { + try self.writeIdentifierWithCase(.snake, param.name); + try self.writer.writeAll(": "); + try self.renderTypeInfo(param.param_type); + }, + } + + try self.writer.writeAll(", "); + } + + try self.writer.writeAll(") "); + } + + fn renderWrapperCall(self: *Self, name: []const u8, command: reg.Command) !void { + try self.writer.writeAll("return self."); + try self.writeIdentifier(name); + try self.writer.writeAll("("); + + for (command.params) |param| { + switch (try self.classifyParam(param)) { + .in_pointer => { + try self.writer.writeByte('&'); + try self.writeIdentifierWithCase(.snake, inPointerAdjustedName(param.name)); + }, + .out_pointer => { + try self.writer.writeByte('&'); + try self.writeIdentifierWithCase(.snake, param.name); + }, + .bitflags => { + try self.writeIdentifierWithCase(.snake, param.name); + try self.writer.writeAll(".toInt()"); // TODO: Generate wrapper + }, + .buffer_len, .mut_buffer_len, .other => { + try self.writeIdentifierWithCase(.snake, param.name); + }, + } + + try self.writer.writeAll(", "); + } + } + + fn extractReturns(self: *Self, command: reg.Command) !reg.Container { + var returns = std.ArrayList(reg.Container.Field).init(self.allocator); + + // if (command.return_type.* == .name) { + // const return_name = command.return_type.name; + // if (!mem.eql(u8, return_name, "void") and !mem.eql(u8, command.return_type.name)) + // } + + if (command.success_codes.len > 0) { + if (command.return_type.* != .name or !mem.eql(u8, command.return_type.name, "VkResult")) { + return error.InvalidRegistry; + } + + try returns.append(.{ + .name = "result", + .field_type = command.return_type.*, + .bits = null, + .is_buffer_len = false, + }); + } + + for (command.params) |param| { + if ((try self.classifyParam(param)) == .out_pointer) { + try returns.append(.{ + .name = param.name, + .field_type = param.param_type, + .bits = null, + .is_buffer_len = param.is_buffer_len, + }); + } + } + + return reg.Container{ + .fields = returns.toOwnedSlice(), + .is_union = false, + }; + } + + fn renderWrapper(self: *Self, name: []const u8, command: reg.Command) !void { + // const returns = try self.extractReturns(command); + // const return_struct_name = if (returns.fields.len > 1) + // try std.fmt.allocPrint(self.allocator, "{}Result", .{name}) + // else + // null; + // defer if (return_struct_name) |rsn| self.sfa.allocator.free(rsn); + + + // if (self.returns.items.len > 1) { + + // } + + try self.renderWrapperPrototype(name, command); + + // if (command.error_codes.len != 0) { + // try self.renderErrorSet(command.error_codes); + // try self.writer.writeByte('!'); + // } + + try self.renderTypeInfo(command.return_type.*); + try self.writer.writeAll("{\n"); + try self.renderWrapperCall(name, command); + try self.writer.writeAll(");\n}\n"); + } + + fn renderErrorSet(self: *Self, errors: []const []const u8) !void { + try self.writer.writeAll("error{"); + for (errors) |name| { + self.renderResultAsError(name) catch |err| switch (err) { + // Apparently some commands return a non-error code as error code + error.NotErrorResult => continue, + else => |narrowed| return narrowed + }; + + try self.writer.writeAll(", "); + } + try self.writer.writeAll("}"); } }; } @@ -555,6 +792,5 @@ fn Renderer(comptime WriterType: type) type { pub fn render(writer: var, allocator: *Allocator, registry: *const reg.Registry) !void { var renderer = Renderer(@TypeOf(writer)).init(writer, allocator, registry); defer renderer.deinit(); - try renderer.render(); }