forked from mirror/vulkan-zig
Compare commits
15 Commits
instructiv
...
zig-0.7.1-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
34d30d0e64 | ||
|
|
efb63a5cac | ||
|
|
fb5ca7cf90 | ||
|
|
272c1160eb | ||
|
|
2064c912aa | ||
|
|
954ca65ed9 | ||
|
|
9321da3426 | ||
|
|
bda8c7213a | ||
|
|
9aae495eab | ||
|
|
01a64c1f9c | ||
|
|
ffb9e9ff3e | ||
|
|
8e48a8aa03 | ||
|
|
50177211cb | ||
|
|
9eac24ee39 | ||
|
|
2cb1fcc354 |
5
.github/workflows/build.yml
vendored
5
.github/workflows/build.yml
vendored
@@ -31,7 +31,10 @@ jobs:
|
||||
sudo apt update
|
||||
sudo apt install shaderc libglfw3 libglfw3-dev
|
||||
|
||||
- name: Build with latest zig & vk.xml
|
||||
- name: Fetch latest vk.xml
|
||||
run: |
|
||||
wget https://raw.githubusercontent.com/KhronosGroup/Vulkan-Docs/main/xml/vk.xml
|
||||
|
||||
- name: Build with latest zig & vk.xml
|
||||
run: |
|
||||
zig build -Dvulkan-registry=./vk.xml
|
||||
|
||||
@@ -8,7 +8,11 @@ A Vulkan binding generator for Zig.
|
||||
|
||||
vulkan-zig attempts to provide a better experience to programming Vulkan applications in Zig, by providing features such as integration of vulkan errors with Zig's error system, function pointer loading, renaming fields to standard Zig style, better bitfield handling, turning out parameters into return values and more.
|
||||
|
||||
vulkan-zig is automatically tested against the latest vk.xml and zig, and supports vk.xml from version 1.x.163.
|
||||
vulkan-zig is automatically tested daily against the latest vk.xml and zig, and supports vk.xml from version 1.x.163.
|
||||
|
||||
### Zig versions
|
||||
|
||||
vulkan-zig aims to be always compatible with the ever-changing Zig master branch (however, development may lag a few days behind). Sometimes, the Zig master branch breaks a bunch of functionality however, which may make the latest version vulkan-zig incompatible with older releases of Zig. Versions compatible with older versions of zig are marked with the tag `zig-<version>`.
|
||||
|
||||
## Features
|
||||
### CLI-interface
|
||||
|
||||
@@ -85,7 +85,7 @@ pub fn build(b: *Builder) void {
|
||||
triangle_exe.setTarget(target);
|
||||
triangle_exe.setBuildMode(mode);
|
||||
triangle_exe.install();
|
||||
triangle_exe.linkSystemLibrary("c");
|
||||
triangle_exe.linkLibC();
|
||||
triangle_exe.linkSystemLibrary("glfw");
|
||||
|
||||
const vk_xml_path = b.option([]const u8, "vulkan-registry", "Override the to the Vulkan registry") orelse "examples/vk.xml";
|
||||
|
||||
@@ -107,9 +107,9 @@ pub const GraphicsContext = struct {
|
||||
|
||||
const app_info = vk.ApplicationInfo{
|
||||
.p_application_name = app_name,
|
||||
.application_version = vk.makeVersion(0, 0, 0),
|
||||
.application_version = vk.makeApiVersion(0, 0, 0, 0),
|
||||
.p_engine_name = app_name,
|
||||
.engine_version = vk.makeVersion(0, 0, 0),
|
||||
.engine_version = vk.makeApiVersion(0, 0, 0, 0),
|
||||
.api_version = vk.API_VERSION_1_2,
|
||||
};
|
||||
|
||||
|
||||
663
examples/vk.xml
663
examples/vk.xml
File diff suppressed because it is too large
Load Diff
@@ -510,7 +510,7 @@ fn parseArrayDeclarator(xctok: *XmlCTokenizer) !?ArraySize {
|
||||
return size;
|
||||
}
|
||||
|
||||
pub fn parseVersion(xctok: *XmlCTokenizer) ![3][]const u8 {
|
||||
pub fn parseVersion(xctok: *XmlCTokenizer) ![4][]const u8 {
|
||||
_ = try xctok.expect(.hash);
|
||||
const define = try xctok.expect(.id);
|
||||
if (!mem.eql(u8, define.text, "define")) {
|
||||
@@ -519,12 +519,12 @@ pub fn parseVersion(xctok: *XmlCTokenizer) ![3][]const u8 {
|
||||
|
||||
const name = try xctok.expect(.name);
|
||||
const vk_make_version = try xctok.expect(.type_name);
|
||||
if (!mem.eql(u8, vk_make_version.text, "VK_MAKE_VERSION")) {
|
||||
if (!mem.eql(u8, vk_make_version.text, "VK_MAKE_API_VERSION")) {
|
||||
return error.NotVersion;
|
||||
}
|
||||
|
||||
_ = try xctok.expect(.lparen);
|
||||
var version: [3][]const u8 = undefined;
|
||||
var version: [4][]const u8 = undefined;
|
||||
for (version) |*part, i| {
|
||||
if (i != 0) {
|
||||
_ = try xctok.expect(.comma);
|
||||
|
||||
@@ -105,128 +105,6 @@ const EnumFieldMerger = struct {
|
||||
}
|
||||
};
|
||||
|
||||
const TagFixerUpper = struct {
|
||||
allocator: *Allocator,
|
||||
registry: *reg.Registry,
|
||||
names: std.StringHashMap(void),
|
||||
id_renderer: *const IdRenderer,
|
||||
|
||||
fn init(allocator: *Allocator, registry: *reg.Registry, id_renderer: *const IdRenderer) TagFixerUpper {
|
||||
return .{
|
||||
.allocator = allocator,
|
||||
.registry = registry,
|
||||
.names = std.StringHashMap(void).init(allocator),
|
||||
.id_renderer = id_renderer,
|
||||
};
|
||||
}
|
||||
|
||||
fn deinit(self: *TagFixerUpper) void {
|
||||
self.names.deinit();
|
||||
}
|
||||
|
||||
fn insertName(self: *TagFixerUpper, name: []const u8) !void {
|
||||
const tagless = self.id_renderer.stripAuthorTag(name);
|
||||
const result = try self.names.getOrPut(name);
|
||||
|
||||
if (result.found_existing) {
|
||||
return error.DuplicateDefinition;
|
||||
}
|
||||
}
|
||||
|
||||
fn extractNames(self: *TagFixerUpper) !void {
|
||||
for (self.registry.decls) |decl| {
|
||||
try self.insertName(decl.name);
|
||||
|
||||
switch (decl.decl_type) {
|
||||
.enumeration => |enumeration| {
|
||||
for (enumeration.fields) |field| {
|
||||
try self.insertName(field.name);
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fixAlias(self: *TagFixerUpper, name: *[]const u8) !void {
|
||||
if (self.names.contains(name.*)) {
|
||||
// The alias exists, everything is fine
|
||||
return;
|
||||
}
|
||||
|
||||
// The alias does not exist, check if the tagless version exists
|
||||
const tagless = self.id_renderer.stripAuthorTag(name.*);
|
||||
if (self.names.contains(tagless)) {
|
||||
// Fix up the name to the tagless version
|
||||
name.* = tagless;
|
||||
return;
|
||||
}
|
||||
|
||||
// Neither original nor tagless version exists
|
||||
return error.InvalidRegistry;
|
||||
}
|
||||
|
||||
fn fixCommand(self: *TagFixerUpper, command: *reg.Command) !void {
|
||||
for (command.params) |*param| {
|
||||
try self.fixTypeInfo(¶m.param_type);
|
||||
}
|
||||
|
||||
try self.fixTypeInfo(command.return_type);
|
||||
for (command.success_codes) |*code| {
|
||||
try self.fixAlias(code);
|
||||
}
|
||||
|
||||
for (command.error_codes) |*code| {
|
||||
try self.fixAlias(code);
|
||||
}
|
||||
}
|
||||
|
||||
fn fixTypeInfo(self: *TagFixerUpper, type_info: *reg.TypeInfo) error{InvalidRegistry}!void {
|
||||
switch (type_info.*) {
|
||||
.name => |*name| try self.fixAlias(name),
|
||||
.command_ptr => |*command| try self.fixCommand(command),
|
||||
.pointer => |ptr| try self.fixTypeInfo(ptr.child),
|
||||
.array => |arr| try self.fixTypeInfo(arr.child),
|
||||
}
|
||||
}
|
||||
|
||||
fn fixNames(self: *TagFixerUpper) !void {
|
||||
for (self.registry.decls) |*decl| {
|
||||
switch (decl.decl_type) {
|
||||
.container => |*container| {
|
||||
for (container.fields) |*field| {
|
||||
try self.fixTypeInfo(&field.field_type);
|
||||
}
|
||||
},
|
||||
.enumeration => |*enumeration| {
|
||||
for (enumeration.fields) |*field| {
|
||||
if (field.value == .alias) {
|
||||
try self.fixAlias(&field.value.alias.name);
|
||||
}
|
||||
}
|
||||
},
|
||||
.bitmask => |*bitmask| {
|
||||
if (bitmask.bits_enum) |*bits| {
|
||||
try self.fixAlias(bits);
|
||||
}
|
||||
},
|
||||
.command => |*command| try self.fixCommand(command),
|
||||
.alias => |*alias| try self.fixAlias(&alias.name),
|
||||
.typedef => |*type_info| try self.fixTypeInfo(type_info),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fixup(self: *TagFixerUpper) !void {
|
||||
// Extract all non-aliases
|
||||
try self.extractNames();
|
||||
|
||||
// Fix aliases
|
||||
try self.fixNames();
|
||||
}
|
||||
};
|
||||
|
||||
pub const Generator = struct {
|
||||
gpa: *Allocator,
|
||||
reg_arena: std.heap.ArenaAllocator,
|
||||
@@ -252,17 +130,6 @@ pub const Generator = struct {
|
||||
self.reg_arena.deinit();
|
||||
}
|
||||
|
||||
fn removePromotedExtensions(self: *Generator) void {
|
||||
var write_index: usize = 0;
|
||||
for (self.registry.extensions) |ext| {
|
||||
if (ext.promoted_to == .none) {
|
||||
self.registry.extensions[write_index] = ext;
|
||||
write_index += 1;
|
||||
}
|
||||
}
|
||||
self.registry.extensions.len = write_index;
|
||||
}
|
||||
|
||||
fn stripFlagBits(self: Generator, name: []const u8) []const u8 {
|
||||
const tagless = self.id_renderer.stripAuthorTag(name);
|
||||
return tagless[0 .. tagless.len - "FlagBits".len];
|
||||
@@ -273,34 +140,6 @@ pub const Generator = struct {
|
||||
return tagless[0 .. tagless.len - "Flags".len];
|
||||
}
|
||||
|
||||
fn fixupBitmasks(self: *Generator) !void {
|
||||
var bits = std.StringHashMap([]const u8).init(self.gpa);
|
||||
defer bits.deinit();
|
||||
|
||||
for (self.registry.decls) |decl| {
|
||||
if (decl.decl_type == .enumeration and decl.decl_type.enumeration.is_bitmask) {
|
||||
try bits.put(self.stripFlagBits(decl.name), decl.name);
|
||||
}
|
||||
}
|
||||
|
||||
for (self.registry.decls) |*decl| {
|
||||
switch (decl.decl_type) {
|
||||
.bitmask => |*bitmask| {
|
||||
const base_name = self.stripFlags(decl.name);
|
||||
|
||||
if (bitmask.bits_enum) |bits_enum| {
|
||||
if (bits.get(base_name) == null) {
|
||||
bitmask.bits_enum = null;
|
||||
}
|
||||
} else if (bits.get(base_name)) |bits_enum| {
|
||||
bitmask.bits_enum = bits_enum;
|
||||
}
|
||||
},
|
||||
else => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Solve `registry.declarations` according to `registry.extensions` and `registry.features`.
|
||||
fn mergeEnumFields(self: *Generator) !void {
|
||||
var merger = EnumFieldMerger.init(self.gpa, &self.reg_arena.allocator, &self.registry);
|
||||
@@ -330,9 +169,6 @@ pub fn generate(allocator: *Allocator, spec_xml: []const u8, writer: anytype) !v
|
||||
var gen = try Generator.init(allocator, spec.root);
|
||||
defer gen.deinit();
|
||||
|
||||
gen.removePromotedExtensions();
|
||||
try gen.mergeEnumFields();
|
||||
try gen.fixupBitmasks();
|
||||
try gen.fixupTags();
|
||||
try gen.render(writer);
|
||||
}
|
||||
|
||||
@@ -107,9 +107,21 @@ fn parseBitmaskType(ty: *xml.Element) !registry.Declaration {
|
||||
.decl_type = .{.alias = .{.name = alias, .target = .other_type}},
|
||||
};
|
||||
} else {
|
||||
const flags_type = ty.getCharData("type") orelse return error.InvalidRegistry;
|
||||
|
||||
const bitwidth: u8 = if (mem.eql(u8, flags_type, "VkFlags"))
|
||||
32
|
||||
else if (mem.eql(u8, flags_type, "VkFlags64"))
|
||||
64
|
||||
else
|
||||
return error.InvalidRegistry;
|
||||
|
||||
return registry.Declaration{
|
||||
.name = ty.getCharData("name") orelse return error.InvalidRegistry,
|
||||
.decl_type = .{.bitmask = .{.bits_enum = ty.getAttribute("requires")}},
|
||||
.decl_type = .{.bitmask = .{
|
||||
.bits_enum = ty.getAttribute("requires") orelse ty.getAttribute("bitvalues"), // Who knows why these are different fields
|
||||
.bitwidth = bitwidth,
|
||||
}},
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -317,6 +329,11 @@ fn parseEnumFields(allocator: *Allocator, elem: *xml.Element) !registry.Enum {
|
||||
return error.InvalidRegistry;
|
||||
}
|
||||
|
||||
const bitwidth = if (elem.getAttribute("bitwidth")) |bitwidth|
|
||||
try std.fmt.parseInt(u8, bitwidth, 10)
|
||||
else
|
||||
32;
|
||||
|
||||
const fields = try allocator.alloc(registry.Enum.Field, elem.children.items.len);
|
||||
|
||||
var i: usize = 0;
|
||||
@@ -328,6 +345,7 @@ fn parseEnumFields(allocator: *Allocator, elem: *xml.Element) !registry.Enum {
|
||||
|
||||
return registry.Enum{
|
||||
.fields = allocator.shrink(fields, i),
|
||||
.bitwidth = bitwidth,
|
||||
.is_bitmask = is_bitmask,
|
||||
};
|
||||
}
|
||||
@@ -356,7 +374,7 @@ fn parseEnumField(field: *xml.Element) !registry.Enum.Field {
|
||||
break :blk .{.int = try std.fmt.parseInt(i32, value, 10)};
|
||||
}
|
||||
} else if (field.getAttribute("bitpos")) |bitpos| {
|
||||
break :blk .{.bitpos = try std.fmt.parseInt(u5, bitpos, 10)};
|
||||
break :blk .{.bitpos = try std.fmt.parseInt(u6, bitpos, 10)};
|
||||
} else if (field.getAttribute("alias")) |alias| {
|
||||
break :blk .{.alias = .{.name = alias, .is_compat_alias = is_compat_alias}};
|
||||
} else {
|
||||
|
||||
@@ -37,7 +37,7 @@ pub const Alias = struct {
|
||||
pub const ApiConstant = struct {
|
||||
pub const Value = union(enum) {
|
||||
expr: []const u8,
|
||||
version: [3][]const u8,
|
||||
version: [4][]const u8,
|
||||
};
|
||||
|
||||
name: []const u8,
|
||||
@@ -71,7 +71,7 @@ pub const Container = struct {
|
||||
|
||||
pub const Enum = struct {
|
||||
pub const Value = union(enum) {
|
||||
bitpos: u5, // 1 << bitpos
|
||||
bitpos: u6, // 1 << bitpos
|
||||
bit_vector: i32, // Combined flags & some vendor IDs
|
||||
int: i32,
|
||||
alias: struct {
|
||||
@@ -86,11 +86,13 @@ pub const Enum = struct {
|
||||
};
|
||||
|
||||
fields: []Field,
|
||||
bitwidth: u8,
|
||||
is_bitmask: bool,
|
||||
};
|
||||
|
||||
pub const Bitmask = struct {
|
||||
bits_enum: ?[]const u8,
|
||||
bitwidth: u8,
|
||||
};
|
||||
|
||||
pub const Handle = struct {
|
||||
|
||||
@@ -24,9 +24,9 @@ const preamble =
|
||||
\\ .AAPCSVFP
|
||||
\\ else
|
||||
\\ .C;
|
||||
\\pub fn FlagsMixin(comptime FlagsType: type) type {
|
||||
\\pub fn FlagsMixin(comptime FlagsType: type, comptime Int: type) type {
|
||||
\\ return struct {
|
||||
\\ pub const IntType = Flags;
|
||||
\\ pub const IntType = Int;
|
||||
\\ pub fn toInt(self: FlagsType) IntType {
|
||||
\\ return @bitCast(IntType, self);
|
||||
\\ }
|
||||
@@ -50,16 +50,19 @@ const preamble =
|
||||
\\ }
|
||||
\\ };
|
||||
\\}
|
||||
\\pub fn makeVersion(major: u10, minor: u10, patch: u12) u32 {
|
||||
\\ return (@as(u32, major) << 22) | (@as(u32, minor) << 12) | patch;
|
||||
\\pub fn makeApiVersion(variant: u3, major: u7, minor: u10, patch: u12) u32 {
|
||||
\\ return (@as(u32, variant) << 29) | (@as(u32, major) << 22) | (@as(u32, minor) << 12) | patch;
|
||||
\\}
|
||||
\\pub fn versionMajor(version: u32) u10 {
|
||||
\\ return @truncate(u10, version >> 22);
|
||||
\\pub fn apiVersionVariant(version: u32) u3 {
|
||||
\\ return @truncate(u3, version >> 29);
|
||||
\\}
|
||||
\\pub fn versionMinor(version: u32) u10 {
|
||||
\\pub fn apiVersionMajor(version: u32) u7 {
|
||||
\\ return @truncate(u7, version >> 22);
|
||||
\\}
|
||||
\\pub fn apiVersionMinor(version: u32) u10 {
|
||||
\\ return @truncate(u10, version >> 12);
|
||||
\\}
|
||||
\\pub fn versionPatch(version: u32) u12 {
|
||||
\\pub fn apiVersionPatch(version: u32) u12 {
|
||||
\\ return @truncate(u12, version);
|
||||
\\}
|
||||
\\
|
||||
@@ -74,6 +77,8 @@ const builtin_types = std.ComptimeStringMap([]const u8, .{
|
||||
.{"uint16_t", @typeName(u16)},
|
||||
.{"uint32_t", @typeName(u32)},
|
||||
.{"uint64_t", @typeName(u64)},
|
||||
.{"int8_t", @typeName(i8)},
|
||||
.{"int16_t", @typeName(i16)},
|
||||
.{"int32_t", @typeName(i32)},
|
||||
.{"int64_t", @typeName(i64)},
|
||||
.{"size_t", @typeName(usize)},
|
||||
@@ -98,6 +103,8 @@ const foreign_types = std.ComptimeStringMap([]const u8, .{
|
||||
.{"xcb_visualid_t", @typeName(u32)},
|
||||
.{"xcb_window_t", @typeName(u32)},
|
||||
.{"zx_handle_t", @typeName(u32)},
|
||||
.{"_screen_context", "opaque {}"},
|
||||
.{"_screen_window", "opaque {}"},
|
||||
});
|
||||
|
||||
fn eqlIgnoreCase(lhs: []const u8, rhs: []const u8) bool {
|
||||
@@ -129,15 +136,16 @@ fn Renderer(comptime WriterType: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
const WriteError = WriterType.Error;
|
||||
const RenderTypeInfoError = WriteError || error {
|
||||
OutOfMemory,
|
||||
};
|
||||
const RenderTypeInfoError = WriteError || std.fmt.ParseIntError || error { OutOfMemory, InvalidRegistry };
|
||||
|
||||
const BitflagName = struct {
|
||||
/// Name without FlagBits, so VkSurfaceTransformFlagBitsKHR
|
||||
/// becomes VkSurfaceTransform
|
||||
base_name: []const u8,
|
||||
|
||||
/// Optional flag bits revision, used in places like VkAccessFlagBits2KHR
|
||||
revision: ?[]const u8,
|
||||
|
||||
/// Optional tag of the flag
|
||||
tag: ?[]const u8,
|
||||
};
|
||||
@@ -225,8 +233,8 @@ fn Renderer(comptime WriterType: type) type {
|
||||
|
||||
while (true) {
|
||||
const rest = field_it.rest();
|
||||
const field_segment = field_it.next() orelse return error.InvalidRegistry;
|
||||
const enum_segment = enum_it.next() orelse return rest;
|
||||
const field_segment = field_it.next() orelse return error.FieldNameEqualsEnumName;
|
||||
|
||||
if (!eqlIgnoreCase(enum_segment, field_segment)) {
|
||||
return rest;
|
||||
@@ -234,16 +242,48 @@ fn Renderer(comptime WriterType: type) type {
|
||||
}
|
||||
}
|
||||
|
||||
fn extractBitflagName(self: Self, name: []const u8) ?BitflagName {
|
||||
const tag = self.id_renderer.getAuthorTag(name);
|
||||
const base_name = if (tag) |tag_name| name[0 .. name.len - tag_name.len] else name;
|
||||
fn extractBitflagFieldName(self: Self, bitflag_name: BitflagName, field_name: []const u8) ![]const u8 {
|
||||
var flag_it = id_render.SegmentIterator.init(bitflag_name.base_name);
|
||||
var field_it = id_render.SegmentIterator.init(field_name);
|
||||
|
||||
if (!mem.endsWith(u8, base_name, "FlagBits")) {
|
||||
while (true) {
|
||||
const rest = field_it.rest();
|
||||
const field_segment = field_it.next() orelse return error.InvalidRegistry;
|
||||
const flag_segment = flag_it.next() orelse {
|
||||
if (bitflag_name.revision) |revision| {
|
||||
if (mem.eql(u8, revision, field_segment))
|
||||
return field_it.rest();
|
||||
}
|
||||
|
||||
return rest;
|
||||
};
|
||||
|
||||
if (!eqlIgnoreCase(flag_segment, field_segment)) {
|
||||
return rest;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn extractBitflagName(self: Self, name: []const u8) !?BitflagName {
|
||||
const tag = self.id_renderer.getAuthorTag(name);
|
||||
const tagless_name = if (tag) |tag_name| name[0 .. name.len - tag_name.len] else name;
|
||||
|
||||
// Strip out the "version" number of a bitflag, like VkAccessFlagBits2KHR.
|
||||
const base_name = std.mem.trimRight(u8, tagless_name, "0123456789");
|
||||
|
||||
const maybe_flag_bits_index = mem.lastIndexOf(u8, base_name, "FlagBits");
|
||||
if (maybe_flag_bits_index == null) {
|
||||
return null;
|
||||
} else if (maybe_flag_bits_index != base_name.len - "FlagBits".len) {
|
||||
// It is unlikely that a type that is not a flag bit would contain FlagBits,
|
||||
// and more likely that we have missed something if FlagBits isn't the last
|
||||
// part of base_name
|
||||
return error.InvalidRegistry;
|
||||
}
|
||||
|
||||
return BitflagName{
|
||||
.base_name = base_name[0 .. base_name.len - "FlagBits".len],
|
||||
.revision = if (base_name.len != tagless_name.len) tagless_name[base_name.len..] else null,
|
||||
.tag = tag,
|
||||
};
|
||||
}
|
||||
@@ -333,7 +373,7 @@ fn Renderer(comptime WriterType: type) type {
|
||||
}
|
||||
},
|
||||
.name => |name| {
|
||||
if (self.extractBitflagName(param.param_type.name) != null or self.isFlags(param.param_type.name)) {
|
||||
if ((try self.extractBitflagName(param.param_type.name)) != null or self.isFlags(param.param_type.name)) {
|
||||
return .bitflags;
|
||||
}
|
||||
},
|
||||
@@ -411,7 +451,7 @@ fn Renderer(comptime WriterType: type) type {
|
||||
switch (api_constant.value) {
|
||||
.expr => |expr| try self.renderApiConstantExpr(expr),
|
||||
.version => |version| {
|
||||
try self.writer.writeAll("makeVersion(");
|
||||
try self.writer.writeAll("makeApiVersion(");
|
||||
for (version) |part, i| {
|
||||
if (i != 0) {
|
||||
try self.writer.writeAll(", ");
|
||||
@@ -470,7 +510,7 @@ fn Renderer(comptime WriterType: type) type {
|
||||
try self.writer.print("@as(f32, {s}.{s})", .{tok.text, decimal.text});
|
||||
|
||||
const f = (try tokenizer.next()) orelse return error.InvalidConstantExpr;
|
||||
if (f.kind != .id or !mem.eql(u8, f.text, "f")) {
|
||||
if (f.kind != .id or f.text.len != 1 or (f.text[0] != 'f' and f.text[0] != 'F')) {
|
||||
return error.InvalidApiConstant;
|
||||
}
|
||||
},
|
||||
@@ -495,9 +535,10 @@ fn Renderer(comptime WriterType: type) type {
|
||||
if (builtin_types.get(name)) |zig_name| {
|
||||
try self.writer.writeAll(zig_name);
|
||||
return;
|
||||
} else if (self.extractBitflagName(name)) |bitflag_name| {
|
||||
try self.writeIdentifierFmt("{s}Flags{s}", .{
|
||||
} else if (try self.extractBitflagName(name)) |bitflag_name| {
|
||||
try self.writeIdentifierFmt("{s}Flags{s}{s}", .{
|
||||
trimVkNamespace(bitflag_name.base_name),
|
||||
@as([]const u8, if (bitflag_name.revision) |revision| revision else ""),
|
||||
@as([]const u8, if (bitflag_name.tag) |tag| tag else "")
|
||||
});
|
||||
return;
|
||||
@@ -534,9 +575,10 @@ fn Renderer(comptime WriterType: type) type {
|
||||
|
||||
blk: {
|
||||
if (param.param_type == .name) {
|
||||
if (self.extractBitflagName(param.param_type.name)) |bitflag_name| {
|
||||
try self.writeIdentifierFmt("{s}Flags{s}", .{
|
||||
if (try self.extractBitflagName(param.param_type.name)) |bitflag_name| {
|
||||
try self.writeIdentifierFmt("{s}Flags{s}{s}", .{
|
||||
trimVkNamespace(bitflag_name.base_name),
|
||||
@as([]const u8, if (bitflag_name.revision) |revision| revision else ""),
|
||||
@as([]const u8, if (bitflag_name.tag) |tag| tag else "")
|
||||
});
|
||||
try self.writer.writeAll(".IntType");
|
||||
@@ -668,6 +710,37 @@ fn Renderer(comptime WriterType: type) type {
|
||||
try self.writeIdentifierWithCase(.snake, try self.extractEnumFieldName(name, field_name));
|
||||
}
|
||||
|
||||
fn renderEnumerationValue(self: *Self, enum_name: []const u8, enumeration: reg.Enum, value: reg.Enum.Value) !void {
|
||||
var current_value = value;
|
||||
var maybe_alias_of: ?[]const u8 = null;
|
||||
|
||||
while (true) {
|
||||
switch (current_value) {
|
||||
.int => |int| try self.writer.print(" = {}, ", .{int}),
|
||||
.bitpos => |pos| try self.writer.print(" = 1 << {}, ", .{pos}),
|
||||
.bit_vector => |bv| try self.writer.print("= 0x{X}, ", .{bv}),
|
||||
.alias => |alias| {
|
||||
// Find the alias
|
||||
current_value = for (enumeration.fields) |field| {
|
||||
if (mem.eql(u8, field.name, alias.name)) {
|
||||
maybe_alias_of = field.name;
|
||||
break field.value;
|
||||
}
|
||||
} else return error.InvalidRegistry; // There is no alias
|
||||
continue;
|
||||
},
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (maybe_alias_of) |alias_of| {
|
||||
try self.writer.writeAll("// alias of ");
|
||||
try self.renderEnumFieldName(enum_name, alias_of);
|
||||
try self.writer.writeByte('\n');
|
||||
}
|
||||
}
|
||||
|
||||
fn renderEnumeration(self: *Self, name: []const u8, enumeration: reg.Enum) !void {
|
||||
if (enumeration.is_bitmask) {
|
||||
try self.renderBitmaskBits(name, enumeration);
|
||||
@@ -676,36 +749,37 @@ fn Renderer(comptime WriterType: type) type {
|
||||
|
||||
try self.writer.writeAll("pub const ");
|
||||
try self.renderName(name);
|
||||
try self.writer.writeAll(" = extern enum {");
|
||||
try self.writer.writeAll(" = extern enum(i32) {");
|
||||
|
||||
for (enumeration.fields) |field| {
|
||||
if (field.value == .alias) {
|
||||
if (field.value == .alias and field.value.alias.is_compat_alias)
|
||||
continue;
|
||||
}
|
||||
|
||||
try self.renderEnumFieldName(name, field.name);
|
||||
|
||||
switch (field.value) {
|
||||
.int => |int| try self.writer.print(" = {}, ", .{int}),
|
||||
.bitpos => |pos| try self.writer.print(" = 1 << {}, ", .{pos}),
|
||||
.bit_vector => |value| try self.writer.print(" = 0x{X}, ", .{value}),
|
||||
.alias => unreachable,
|
||||
}
|
||||
try self.renderEnumerationValue(name, enumeration, field.value);
|
||||
}
|
||||
|
||||
for (enumeration.fields) |field| {
|
||||
if (field.value != .alias or field.value.alias.is_compat_alias) {
|
||||
continue;
|
||||
try self.writer.writeAll("_,};\n");
|
||||
}
|
||||
|
||||
try self.writer.writeAll("pub const ");
|
||||
try self.renderEnumFieldName(name, field.name);
|
||||
try self.writer.writeAll(" = .");
|
||||
try self.renderEnumFieldName(name, field.value.alias.name);
|
||||
try self.writer.writeAll(";");
|
||||
fn bitmaskFlagsType(bitwidth: u8) ![]const u8 {
|
||||
return switch (bitwidth) {
|
||||
32 => "Flags",
|
||||
64 => "Flags64",
|
||||
else => return error.InvalidRegistry,
|
||||
};
|
||||
}
|
||||
|
||||
try self.writer.writeAll("};\n");
|
||||
fn renderUsingFlagsMixin(self: *Self, name: []const u8, bitwidth: u8) !void {
|
||||
const flags_type = switch (bitwidth) {
|
||||
32 => "Flags",
|
||||
64 => "Flags64",
|
||||
else => return error.InvalidRegistry,
|
||||
};
|
||||
|
||||
try self.writer.writeAll("pub usingnamespace FlagsMixin(");
|
||||
try self.renderName(name);
|
||||
try self.writer.print(", {s});\n", .{ flags_type });
|
||||
}
|
||||
|
||||
fn renderBitmaskBits(self: *Self, name: []const u8, bits: reg.Enum) !void {
|
||||
@@ -713,19 +787,23 @@ fn Renderer(comptime WriterType: type) type {
|
||||
try self.renderName(name);
|
||||
try self.writer.writeAll(" = packed struct {");
|
||||
|
||||
const bitflag_name = (try self.extractBitflagName(name)) orelse return error.InvalidRegistry;
|
||||
const flags_type = try bitmaskFlagsType(bits.bitwidth);
|
||||
|
||||
if (bits.fields.len == 0) {
|
||||
try self.writer.writeAll("_reserved_bits: Flags = 0,");
|
||||
try self.writer.print("_reserved_bits: {s} = 0,", .{ flags_type });
|
||||
} else {
|
||||
var flags_by_bitpos = [_]?[]const u8{null} ** 32;
|
||||
var flags_by_bitpos = [_]?[]const u8{null} ** 64;
|
||||
for (bits.fields) |field| {
|
||||
if (field.value == .bitpos) {
|
||||
flags_by_bitpos[field.value.bitpos] = field.name;
|
||||
}
|
||||
}
|
||||
|
||||
for (flags_by_bitpos) |opt_flag_name, bitpos| {
|
||||
if (opt_flag_name) |flag_name| {
|
||||
try self.renderEnumFieldName(name, flag_name);
|
||||
for (flags_by_bitpos[0.. bits.bitwidth]) |maybe_flag_name, bitpos| {
|
||||
if (maybe_flag_name) |flag_name| {
|
||||
const field_name = try self.extractBitflagFieldName(bitflag_name, flag_name);
|
||||
try self.writeIdentifierWithCase(.snake, field_name);
|
||||
} else {
|
||||
try self.writer.print("_reserved_bit_{}", .{bitpos});
|
||||
}
|
||||
@@ -739,7 +817,7 @@ fn Renderer(comptime WriterType: type) type {
|
||||
}
|
||||
try self.writer.writeAll("pub usingnamespace FlagsMixin(");
|
||||
try self.renderName(name);
|
||||
try self.writer.writeAll(");\n};\n");
|
||||
try self.writer.print(", {s});\n}};\n", .{ flags_type });
|
||||
}
|
||||
|
||||
fn renderBitmask(self: *Self, name: []const u8, bitmask: reg.Bitmask) !void {
|
||||
@@ -747,18 +825,22 @@ fn Renderer(comptime WriterType: type) type {
|
||||
// The bits structure is generated by renderBitmaskBits, but that wont
|
||||
// output flags with no associated bits type.
|
||||
|
||||
const flags_type = try bitmaskFlagsType(bitmask.bitwidth);
|
||||
|
||||
try self.writer.writeAll("pub const ");
|
||||
try self.renderName(name);
|
||||
try self.writer.writeAll(
|
||||
\\ = packed struct {
|
||||
\\_reserved_bits: Flags = 0,
|
||||
try self.writer.print(
|
||||
\\ = packed struct {{
|
||||
\\_reserved_bits: {s} = 0,
|
||||
\\pub usingnamespace FlagsMixin(
|
||||
, .{ flags_type }
|
||||
);
|
||||
try self.renderName(name);
|
||||
try self.writer.writeAll(
|
||||
\\);
|
||||
\\};
|
||||
try self.writer.print(
|
||||
\\, {s});
|
||||
\\}};
|
||||
\\
|
||||
, .{ flags_type }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -774,7 +856,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.extractBitflagName(name) != null) {
|
||||
} else if ((try self.extractBitflagName(name)) != null) {
|
||||
// Don't make aliases of the bitflag names, as those are replaced by just the flags type
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -245,7 +245,7 @@ const ParseContext = struct {
|
||||
|
||||
fn currentLine(self: ParseContext) []const u8 {
|
||||
var begin: usize = 0;
|
||||
if (mem.indexOfScalarPos(u8, self.source[0 .. self.offset], '\n')) |prev_nl| {
|
||||
if (mem.lastIndexOfScalar(u8, self.source[0 .. self.offset], '\n')) |prev_nl| {
|
||||
begin = prev_nl + 1;
|
||||
}
|
||||
|
||||
@@ -328,10 +328,15 @@ fn parseDocument(ctx: *ParseContext, backing_allocator: *Allocator) !Document {
|
||||
|
||||
errdefer doc.deinit();
|
||||
|
||||
try trySkipComments(ctx, &doc.arena.allocator);
|
||||
|
||||
doc.xml_decl = try tryParseProlog(ctx, &doc.arena.allocator);
|
||||
_ = ctx.eatWs();
|
||||
try trySkipComments(ctx, &doc.arena.allocator);
|
||||
|
||||
doc.root = (try tryParseElement(ctx, &doc.arena.allocator)) orelse return error.InvalidDocument;
|
||||
_ = ctx.eatWs();
|
||||
try trySkipComments(ctx, &doc.arena.allocator);
|
||||
|
||||
if (ctx.peek() != null) return error.InvalidDocument;
|
||||
|
||||
@@ -386,7 +391,7 @@ fn tryParseCharData(ctx: *ParseContext, alloc: *Allocator) !?[]const u8 {
|
||||
|
||||
while (ctx.peek()) |ch| {
|
||||
switch (ch) {
|
||||
'<', '>' => break,
|
||||
'<' => break,
|
||||
else => _ = ctx.consumeNoEof()
|
||||
}
|
||||
}
|
||||
@@ -581,6 +586,12 @@ test "tryParseProlog" {
|
||||
}
|
||||
}
|
||||
|
||||
fn trySkipComments(ctx: *ParseContext, alloc: *Allocator) !void {
|
||||
while (try tryParseComment(ctx, alloc)) |_| {
|
||||
_ = ctx.eatWs();
|
||||
}
|
||||
}
|
||||
|
||||
fn tryParseComment(ctx: *ParseContext, alloc: *Allocator) !?[]const u8 {
|
||||
if (!ctx.eatStr("<!--")) return null;
|
||||
|
||||
@@ -645,3 +656,12 @@ test "dupeAndUnescape" {
|
||||
testing.expectError(error.InvalidEntity, dupeAndUnescape(alloc, "python&test;"));
|
||||
testing.expectError(error.InvalidEntity, dupeAndUnescape(alloc, "python&boa"));
|
||||
}
|
||||
|
||||
test "Top level comments" {
|
||||
var arena = std.heap.ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
var alloc = &arena.allocator;
|
||||
|
||||
const doc = try parse(alloc, "<?xml version='aa'?><!--comment--><python color='green'/><!--another comment-->");
|
||||
testing.expectEqualSlices(u8, "python", doc.root.tag);
|
||||
}
|
||||
Reference in New Issue
Block a user