diff --git a/generator/generator.zig b/generator/generator.zig index 26ae81f..6c5d2dd 100644 --- a/generator/generator.zig +++ b/generator/generator.zig @@ -139,7 +139,9 @@ const DeclarationResolver = struct { var write_index: usize = 0; while (read_index < self.registry.decls.len) { const decl = self.registry.decls[read_index]; - if (decl.decl_type == .foreign or self.declarations.contains(decl.name)) { + const is_required = self.declarations.contains(decl.name); + const is_empty_enum = decl.decl_type == .enumeration and decl.decl_type.enumeration.fields.len == 0; + if (decl.decl_type == .foreign or (is_required and !is_empty_enum)) { self.registry.decls[write_index] = decl; write_index += 1; } diff --git a/generator/main.zig b/generator/main.zig index 4c7ce9e..13741c5 100644 --- a/generator/main.zig +++ b/generator/main.zig @@ -138,5 +138,3 @@ test "main" { _ = @import("xml.zig"); _ = @import("registry/c-parse.zig"); } - -// TODO: Fix not all struct fields being marked as optional properly. diff --git a/generator/registry/c-parse.zig b/generator/registry/c-parse.zig index 7f00c32..5bdb84e 100644 --- a/generator/registry/c-parse.zig +++ b/generator/registry/c-parse.zig @@ -7,7 +7,7 @@ const testing = std.testing; const ArraySize = registry.Array.ArraySize; const TypeInfo = registry.TypeInfo; -const Token = struct { +pub const Token = struct { id: Id, text: []const u8, @@ -21,6 +21,9 @@ const Token = struct { comma, semicolon, colon, + minus, + tilde, + dot, lparen, rparen, lbracket, @@ -32,7 +35,7 @@ const Token = struct { }; }; -const CTokenizer = struct { +pub const CTokenizer = struct { source: []const u8, offset: usize = 0, @@ -100,7 +103,7 @@ const CTokenizer = struct { }; } - fn next(self: *CTokenizer) !?Token { + pub fn next(self: *CTokenizer) !?Token { while (true) { switch (self.peek() orelse return null) { ' ', '\t', '\n', '\r' => _ = self.consumeNoEof(), @@ -117,6 +120,9 @@ const CTokenizer = struct { ',' => id = .comma, ';' => id = .semicolon, ':' => id = .colon, + '-' => id = .minus, + '~' => id = .tilde, + '.' => id = .dot, '[' => id = .lbracket, ']' => id = .rbracket, '(' => id = .lparen, diff --git a/generator/registry/parse.zig b/generator/registry/parse.zig index 6421a50..0be66c4 100644 --- a/generator/registry/parse.zig +++ b/generator/registry/parse.zig @@ -1,7 +1,7 @@ const std = @import("std"); const registry = @import("../registry.zig"); const xml = @import("../xml.zig"); -const xmlc = @import("c-parse.zig"); +const cparse = @import("c-parse.zig"); const mem = std.mem; const Allocator = mem.Allocator; const ArenaAllocator = std.heap.ArenaAllocator; @@ -144,8 +144,8 @@ fn parseHandleType(ty: *xml.Element) !registry.Declaration { fn parseBaseType(allocator: *Allocator, ty: *xml.Element) !registry.Declaration { const name = ty.getCharData("name") orelse return error.InvalidRegistry; if (ty.getCharData("type")) |_| { - var tok = xmlc.XmlCTokenizer.init(ty); - return try xmlc.parseTypedef(allocator, &tok); + var tok = cparse.XmlCTokenizer.init(ty); + return try cparse.parseTypedef(allocator, &tok); } else { // Either ANativeWindow, AHardwareBuffer or CAMetalLayer. The latter has a lot of // macros, which is why this part is not built into the xml/c parser. @@ -171,8 +171,8 @@ fn parseContainer(allocator: *Allocator, ty: *xml.Element, is_union: bool) !regi var i: usize = 0; var it = ty.findChildrenByTag("member"); while (it.next()) |member| { - var xctok = xmlc.XmlCTokenizer.init(member); - members[i] = try xmlc.parseMember(allocator, &xctok); + var xctok = cparse.XmlCTokenizer.init(member); + members[i] = try cparse.parseMember(allocator, &xctok); try parsePointerMeta(&members[i].field_type, member); i += 1; } @@ -189,8 +189,8 @@ fn parseContainer(allocator: *Allocator, ty: *xml.Element, is_union: bool) !regi } fn parseFuncPointer(allocator: *Allocator, ty: *xml.Element) !registry.Declaration { - var xctok = xmlc.XmlCTokenizer.init(ty); - return try xmlc.parseTypedef(allocator, &xctok); + var xctok = cparse.XmlCTokenizer.init(ty); + return try cparse.parseTypedef(allocator, &xctok); } fn lenToPointerSize(len: []const u8) registry.Pointer.PointerSize { @@ -362,16 +362,16 @@ fn parseCommand(allocator: *Allocator, elem: *xml.Element) !registry.Declaration } const proto = elem.findChildByTag("proto") orelse return error.InvalidRegistry; - var proto_xctok = xmlc.XmlCTokenizer.init(proto); - const command_decl = try xmlc.parseParamOrProto(allocator, &proto_xctok); + var proto_xctok = cparse.XmlCTokenizer.init(proto); + const command_decl = try cparse.parseParamOrProto(allocator, &proto_xctok); var params = try allocator.alloc(registry.Command.Param, elem.children.count()); var i: usize = 0; var it = elem.findChildrenByTag("param"); while (it.next()) |param| { - var xctok = xmlc.XmlCTokenizer.init(param); - const decl = try xmlc.parseParamOrProto(allocator, &xctok); + 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); i += 1; diff --git a/generator/render.zig b/generator/render.zig index 599062f..7a04126 100644 --- a/generator/render.zig +++ b/generator/render.zig @@ -116,11 +116,81 @@ fn Renderer(comptime WriterType: type) type { fn render(self: *Self) !void { try self.writer.writeAll(preamble); + for (self.registry.api_constants) |api_constant| { + try self.renderApiConstant(api_constant); + } + for (self.registry.decls) |decl| { try self.renderDecl(decl); } } + fn renderApiConstant(self: *Self, api_constant: reg.ApiConstant) !void { + try self.writer.writeAll("const "); + try self.writeIdentifier(util.trimVkNamespace(api_constant.name)); + try self.writer.writeAll(" = "); + + if (api_constant.value == .alias) { + try self.writeIdentifier(util.trimVkNamespace(api_constant.value.alias)); + try self.writer.writeAll(";\n"); + return; + } + + const expr = api_constant.value.expr; + const adjusted_expr = if (expr.len > 2 and expr[0] == '(' and expr[expr.len - 1] == ')') + expr[1 .. expr.len - 1] + else + expr; + + var tokenizer = cparse.CTokenizer{.source = adjusted_expr}; + var peeked: ?cparse.Token = null; + while (true) { + const tok = peeked orelse (try tokenizer.next()) orelse break; + peeked = null; + + switch (tok.id) { + .lparen, .rparen, .tilde, .minus, .id => { + try self.writer.writeAll(tok.text); + continue; + }, + .int => {}, + else => return error.InvalidApiConstant, + } + + const suffix = (try tokenizer.next()) orelse { + try self.writer.writeAll(tok.text); + break; + }; + + switch (suffix.id) { + .id => { + if (mem.eql(u8, suffix.text, "ULL")) { + try self.writer.print("@as(u64, {})", .{tok.text}); + } else if (mem.eql(u8, suffix.text, "U")) { + try self.writer.print("@as(u32, {})", .{tok.text}); + } else { + return error.InvalidApiConstant; + } + }, + .dot => { + const decimal = (try tokenizer.next()) orelse return error.InvalidConstantExpr; + + try self.writer.print("@as(f32, {}.{})", .{tok.text, decimal.text}); + const f = (try tokenizer.next()) orelse return error.InvalidConstantExpr; + if (f.id != .id or !mem.eql(u8, f.text, "f")) { + return error.InvalidApiConstant; + } + }, + else => { + try self.writer.writeAll(tok.text); + peeked = suffix; + }, + } + } + + try self.writer.writeAll(";\n"); + } + fn renderTypeInfo(self: *Self, type_info: reg.TypeInfo) RenderTypeInfoError!void { switch (type_info) { .name => |name| try self.renderTypeName(name), @@ -192,7 +262,7 @@ fn Renderer(comptime WriterType: type) type { try self.writer.writeByte('['); switch (array.size) { .int => |size| try self.writer.print("{}", .{size}), - .alias => |alias| try self.writeIdentifier(alias[3..]), //TODO: Check proper VK_ prefix + .alias => |alias| try self.writeIdentifier(util.trimVkNamespace(alias)), } try self.writer.writeByte(']'); try self.renderTypeInfo(array.child.*);