From c6054d26650fa404ba02f36a2f040d20a3bbc721 Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Tue, 30 Jun 2020 16:48:58 +0200 Subject: [PATCH] API Version parsing & rendering --- generator/generator.zig | 1 - generator/main.zig | 4 +- generator/registry.zig | 2 +- generator/registry/c-parse.zig | 90 +++++++++++++++++++++++++++------- generator/registry/parse.zig | 55 +++++++++++++++++++-- generator/render.zig | 79 ++++++++++++++++++----------- 6 files changed, 174 insertions(+), 57 deletions(-) diff --git a/generator/generator.zig b/generator/generator.zig index 93ba68a..99e7025 100644 --- a/generator/generator.zig +++ b/generator/generator.zig @@ -204,7 +204,6 @@ pub fn generate(allocator: *Allocator, xml_reader: var, writer: var) !void { defer gen.deinit(); gen.removePromotedExtensions(); - try gen.resolveDeclarations(); try gen.render(writer); } diff --git a/generator/main.zig b/generator/main.zig index 54e1efb..3929500 100644 --- a/generator/main.zig +++ b/generator/main.zig @@ -110,7 +110,7 @@ const ProfilingAllocator = struct { pub fn main() !void { if (std.os.argv.len <= 1) { - std.debug.warn("Usage: vulkan-zig-gen \n", .{}); + std.debug.print("Usage: vulkan-zig-gen \n", .{}); return; } @@ -123,7 +123,7 @@ pub fn main() !void { const stdout = std.io.getStdOut().writer(); try vkgen.generate(&prof_alloc.allocator, file.reader(), stdout); - std.debug.warn("Total memory usage: {} KiB\n", .{@divTrunc(prof_alloc.max_usage, 1024)}); + std.debug.print("Total memory usage: {} KiB\n", .{@divTrunc(prof_alloc.max_usage, 1024)}); } test "main" { diff --git a/generator/registry.zig b/generator/registry.zig index 0b11900..6da5a37 100644 --- a/generator/registry.zig +++ b/generator/registry.zig @@ -36,7 +36,7 @@ pub const Alias = struct { pub const ApiConstant = struct { pub const Value = union(enum) { expr: []const u8, - alias: []const u8, // Alias of another API constant + version: [3][]const u8, }; name: []const u8, diff --git a/generator/registry/c-parse.zig b/generator/registry/c-parse.zig index c01a01b..e63528c 100644 --- a/generator/registry/c-parse.zig +++ b/generator/registry/c-parse.zig @@ -24,6 +24,7 @@ pub const Token = struct { minus, tilde, dot, + hash, lparen, rparen, lbracket, @@ -38,6 +39,7 @@ pub const Token = struct { pub const CTokenizer = struct { source: []const u8, offset: usize = 0, + in_comment: bool = false, fn peek(self: CTokenizer) ?u8 { return if (self.offset < self.source.len) self.source[self.offset] else null; @@ -88,7 +90,6 @@ pub const CTokenizer = struct { const start = self.offset; _ = self.consumeNoEof(); - // TODO: 123ABC is now legal while (true) { const c = self.peek() orelse break; switch (c) { @@ -103,15 +104,29 @@ pub const CTokenizer = struct { }; } - pub fn next(self: *CTokenizer) !?Token { + fn skipws(self: *CTokenizer) void { while (true) { - switch (self.peek() orelse return null) { + switch (self.peek() orelse break) { ' ', '\t', '\n', '\r' => _ = self.consumeNoEof(), else => break, } } + } - const c = self.peek().?; + pub fn next(self: *CTokenizer) !?Token { + self.skipws(); + if (mem.startsWith(u8, self.source[self.offset ..], "//") or self.in_comment) { + const end = mem.indexOfScalarPos(u8, self.source, self.offset, '\n') orelse { + self.offset = self.source.len; + self.in_comment = true; + return null; + }; + self.in_comment = false; + self.offset = end + 1; + } + self.skipws(); + + const c = self.peek() orelse return null; var id: Token.Id = undefined; switch (c) { 'A'...'Z', 'a'...'z', '_' => return self.keyword(), @@ -123,6 +138,7 @@ pub const CTokenizer = struct { '-' => id = .minus, '~' => id = .tilde, '.' => id = .dot, + '#' => id = .hash, '[' => id = .lbracket, ']' => id = .rbracket, '(' => id = .lparen, @@ -176,20 +192,23 @@ pub const XmlCTokenizer = struct { return token; } + var in_comment: bool = false; + while (true) { if (self.ctok) |*ctok| { if (try ctok.next()) |tok| { return tok; } + in_comment = ctok.in_comment; } self.ctok = null; if (self.it.next()) |child| { switch (child.*) { - .CharData => |cdata| self.ctok = CTokenizer{.source = cdata}, - .Comment => {}, - .Element => |elem| if (try elemToToken(elem)) |tok| return tok, + .CharData => |cdata| self.ctok = CTokenizer{.source = cdata, .in_comment = in_comment}, + .Comment => {}, // xml comment + .Element => |elem| if (!in_comment) if (try elemToToken(elem)) |tok| return tok, } } else { return null; @@ -198,7 +217,7 @@ pub const XmlCTokenizer = struct { } fn nextNoEof(self: *XmlCTokenizer) !Token { - return (try self.next()) orelse return error.InvalidSyntax; + return (try self.next()) orelse return error.UnexpectedEof; } fn peek(self: *XmlCTokenizer) !?Token { @@ -211,7 +230,7 @@ pub const XmlCTokenizer = struct { } fn peekNoEof(self: *XmlCTokenizer) !Token { - return (try self.peek()) orelse return error.InvalidSyntax; + return (try self.peek()) orelse return error.UnexpectedEof; } fn expect(self: *XmlCTokenizer, id: Token.Id) !Token { @@ -491,6 +510,36 @@ fn parseArrayDeclarator(xctok: *XmlCTokenizer) !?ArraySize { return size; } +pub fn parseVersion(xctok: *XmlCTokenizer) ![3][]const u8 { + _ = try xctok.expect(.hash); + const define = try xctok.expect(.id); + if (!mem.eql(u8, define.text, "define")) { + return error.InvalidVersion; + } + + 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")) { + return error.NotVersion; + } + + _ = try xctok.expect(.lparen); + var version: [3][]const u8 = undefined; + for (version) |*part, i| { + if (i != 0) { + _ = try xctok.expect(.comma); + } + + const tok = try xctok.nextNoEof(); + switch (tok.id) { + .id, .int => part.* = tok.text, + else => return error.UnexpectedToken, + } + } + _ = try xctok.expect(.rparen); + return version; +} + fn testTokenizer(tokenizer: var, expected_tokens: []const Token) void { for (expected_tokens) |expected| { const tok = (tokenizer.next() catch unreachable).?; @@ -503,7 +552,7 @@ fn testTokenizer(tokenizer: var, expected_tokens: []const Token) void { test "CTokenizer" { var ctok = CTokenizer { - .source = "typedef ([const)]** VKAPI_PTR 123,;aaaa" + .source = \\typedef ([const)]** VKAPI_PTR 123,;aaaa }; testTokenizer( @@ -529,7 +578,9 @@ test "CTokenizer" { test "XmlCTokenizer" { const document = try xml.parse( testing.allocator, - "typedef void (VKAPI_PTR *PFN_vkVoidFunction)(void);" + \\// comment commented name commented type trailing + \\ typedef void (VKAPI_PTR *PFN_vkVoidFunction)(void); + \\ ); defer document.deinit(); @@ -556,7 +607,11 @@ test "XmlCTokenizer" { test "parseTypedef" { const document = try xml.parse( testing.allocator, - "typedef const struct Python* pythons[4];" + \\ // comment commented name trailing + \\ typedef const struct Python* pythons[4]; + \\ // more comments + \\ + \\ ); defer document.deinit(); @@ -567,12 +622,9 @@ test "parseTypedef" { const decl = try parseTypedef(&arena.allocator, &xctok); testing.expectEqualSlices(u8, "pythons", decl.name); - testing.expectEqual(TypeInfo.array, decl.decl_type); - testing.expectEqual(ArraySize{.int = 4}, decl.decl_type.array.size); - const array_child = decl.decl_type.array.child.*; - testing.expectEqual(TypeInfo.pointer, array_child); - const ptr = array_child.pointer; + const array = decl.decl_type.typedef.array; + testing.expectEqual(ArraySize{.int = 4}, array.size); + const ptr = array.child.pointer; testing.expectEqual(true, ptr.is_const); - testing.expectEqual(TypeInfo.alias, ptr.child.*); - testing.expectEqualSlices(u8, "Python", ptr.child.alias); + testing.expectEqualSlices(u8, "Python", ptr.child.name); } diff --git a/generator/registry/parse.zig b/generator/registry/parse.zig index 37f0362..c000b7f 100644 --- a/generator/registry/parse.zig +++ b/generator/registry/parse.zig @@ -462,29 +462,74 @@ fn parseApiConstants(allocator: *Allocator, root: *xml.Element) ![]registry.ApiC return error.InvalidRegistry; }; - const constants = try allocator.alloc(registry.ApiConstant, enums.children.count()); + var types = root.findChildByTag("types") orelse return error.InvalidRegistry; + const n_defines = blk: { + var n_defines: usize = 0; + var it = types.findChildrenByTag("type"); + while (it.next()) |ty| { + if (ty.getAttribute("category")) |category| { + if (mem.eql(u8, category, "define")) { + n_defines += 1; + } + } + } + break :blk n_defines; + }; + + const constants = try allocator.alloc(registry.ApiConstant, enums.children.count() + n_defines); var i: usize = 0; var it = enums.findChildrenByTag("enum"); while (it.next()) |constant| { - const value = if (constant.getAttribute("value")) |expr| - registry.ApiConstant.Value{.expr = expr} + const expr = if (constant.getAttribute("value")) |expr| + expr else if (constant.getAttribute("alias")) |alias| - registry.ApiConstant.Value{.alias = alias} + alias else return error.InvalidRegistry; constants[i] = .{ .name = constant.getAttribute("name") orelse return error.InvalidRegistry, - .value = value, + .value = .{.expr = expr}, }; i += 1; } + i += try parseDefines(types, constants[i..]); return allocator.shrink(constants, i); } +fn parseDefines(types: *xml.Element, out: []registry.ApiConstant) !usize { + var i: usize = 0; + var it = types.findChildrenByTag("type"); + while (it.next()) |ty| { + const category = ty.getAttribute("category") orelse continue; + if (!mem.eql(u8, category, "define")) { + continue; + } + + const name = ty.getCharData("name") orelse continue; + if (mem.eql(u8, name, "VK_HEADER_VERSION")) { + out[i] = .{ + .name = name, + .value = .{.expr = mem.trim(u8, ty.children.at(2).CharData, " ")}, + }; + } else { + var xctok = cparse.XmlCTokenizer.init(ty); + out[i] = .{ + .name = name, + .value = .{ + .version = cparse.parseVersion(&xctok) catch continue + }, + }; + } + i += 1; + } + + return i; +} + fn parseTags(allocator: *Allocator, root: *xml.Element) ![]registry.Tag { var tags_elem = root.findChildByTag("tags") orelse return error.InvalidRegistry; const tags = try allocator.alloc(registry.Tag, tags_elem.children.count()); diff --git a/generator/render.zig b/generator/render.zig index 840b1e3..dd40518 100644 --- a/generator/render.zig +++ b/generator/render.zig @@ -25,32 +25,38 @@ const preamble = \\ pub fn toInt(self: FlagsType) IntType { \\ return @bitCast(IntType, self); \\ } - \\ \\ pub fn fromInt(flags: IntType) FlagsType { \\ return @bitCast(FlagsType, flags); \\ } - \\ \\ pub fn merge(lhs: FlagsType, rhs: FlagsType) FlagsType { \\ return fromInt(toInt(lhs) | toInt(rhs)); \\ } - \\ \\ pub fn intersect(lhs: FlagsType, rhs: FlagsType) FlagsType { \\ return fromInt(toInt(lhs) & toInt(rhs)); \\ } - \\ \\ pub fn complement(self: FlagsType) FlagsType { \\ return fromInt(~toInt(lhs)); \\ } - \\ \\ pub fn subtract(lhs: FlagsType, rhs: FlagsType) FlagsType { \\ return fromInt(toInt(lhs) & toInt(rhs.complement())); \\ } - \\ \\ pub fn contains(lhs: FlagsType, rhs: FlagsType) FlagsType { \\ return toInt(merge(lhs, rhs)) == toInt(rhs); \\ } \\ }; \\} + \\pub fn makeVersion(major: u10, minor: u10, patch: u12) u32 { + \\ return (@as(u32, major) << 22) | (@as(u32, minor) << 12) | patch; + \\} + \\pub fn versionMajor(version: u32) u10 { + \\ return @truncate(u10, version >> 22); + \\} + \\pub fn versionMinor(version: u32) u10 { + \\ return @truncate(u10, version >> 12); + \\} + \\pub fn versionPatch(version: u32) u12 { + \\ return @truncate(u12, version); + \\} \\ ; @@ -260,16 +266,27 @@ fn Renderer(comptime WriterType: type) type { fn renderApiConstant(self: *Self, api_constant: reg.ApiConstant) !void { try self.writer.writeAll("const "); - try self.writeIdentifier(util.trimVkNamespace(api_constant.name)); + try self.renderName(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; + switch (api_constant.value) { + .expr => |expr| try self.renderApiConstantExpr(expr), + .version => |version| { + try self.writer.writeAll("makeVersion("); + for (version) |part, i| { + if (i != 0) { + try self.writer.writeAll(", "); + } + try self.renderApiConstantExpr(part); + } + try self.writer.writeAll(")"); + }, } - const expr = api_constant.value.expr; + try self.writer.writeAll(";\n"); + } + + fn renderApiConstantExpr(self: *Self, expr: []const u8) !void { const adjusted_expr = if (expr.len > 2 and expr[0] == '(' and expr[expr.len - 1] == ')') expr[1 .. expr.len - 1] else @@ -287,7 +304,7 @@ fn Renderer(comptime WriterType: type) type { continue; }, .id => { - try self.writeIdentifier(util.trimVkNamespace(api_constant.value.alias)); + try self.renderName(tok.text); continue; }, .int => {}, @@ -324,20 +341,18 @@ fn Renderer(comptime WriterType: type) type { }, } } - - try self.writer.writeAll(";\n"); } fn renderTypeInfo(self: *Self, type_info: reg.TypeInfo) RenderTypeInfoError!void { switch (type_info) { - .name => |name| try self.renderTypeName(name), + .name => |name| try self.renderName(name), .command_ptr => |command_ptr| try self.renderCommandPtr(command_ptr, true), .pointer => |pointer| try self.renderPointer(pointer), .array => |array| try self.renderArray(array), } } - fn renderTypeName(self: *Self, name: []const u8) !void { + fn renderName(self: *Self, name: []const u8) !void { if (builtin_types.get(name)) |zig_name| { try self.writer.writeAll(zig_name); return; @@ -359,6 +374,10 @@ fn Renderer(comptime WriterType: type) type { // Function pointer type, render using same name for now try self.writeIdentifier(name); return; + } else if (mem.startsWith(u8, name, "VK_")) { + // Constants + try self.writeIdentifier(name[3..]); + return; } try self.writeIdentifier(name); @@ -423,7 +442,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(util.trimVkNamespace(alias)), + .alias => |alias| try self.renderName(alias), } try self.writer.writeByte(']'); try self.renderTypeInfo(array.child.*); @@ -445,7 +464,7 @@ fn Renderer(comptime WriterType: type) type { fn renderContainer(self: *Self, name: []const u8, container: reg.Container) !void { try self.writer.writeAll("const "); - try self.renderTypeName(name); + try self.renderName(name); try self.writer.writeAll(" = "); for (container.fields) |field| { @@ -503,7 +522,7 @@ fn Renderer(comptime WriterType: type) type { } try self.writer.writeAll("const "); - try self.renderTypeName(name); + try self.renderName(name); try self.writer.writeAll(" = extern enum {"); for (enumeration.fields) |field| { @@ -538,7 +557,7 @@ fn Renderer(comptime WriterType: type) type { fn renderBitmaskBits(self: *Self, name: []const u8, bits: reg.Enum) !void { try self.writer.writeAll("const "); - try self.renderTypeName(name); + try self.renderName(name); try self.writer.writeAll(" = packed struct {"); if (bits.fields.len == 0) { @@ -566,7 +585,7 @@ fn Renderer(comptime WriterType: type) type { } } try self.writer.writeAll("pub usingnamespace FlagsMixin("); - try self.renderTypeName(name); + try self.renderName(name); try self.writer.writeAll(");\n};\n"); } @@ -576,13 +595,13 @@ fn Renderer(comptime WriterType: type) type { // output flags with no associated bits type. try self.writer.writeAll("const "); - try self.renderTypeName(name); + try self.renderName(name); try self.writer.writeAll( \\ = packed struct { \\_reserved_bits: Flags = 0, \\pub usingnamespace FlagsMixin( ); - try self.renderTypeName(name); + try self.renderName(name); try self.writer.writeAll( \\); \\}; @@ -595,7 +614,7 @@ fn Renderer(comptime WriterType: type) type { const backing_type: []const u8 = if (handle.is_dispatchable) "usize" else "u64"; try self.writer.writeAll("const "); - try self.renderTypeName(name); + try self.renderName(name); try self.writer.print(" = extern enum({}) {{null_handle = 0, _}};\n", .{backing_type}); } @@ -608,15 +627,15 @@ fn Renderer(comptime WriterType: type) type { } try self.writer.writeAll("const "); - try self.renderTypeName(name); + try self.renderName(name); try self.writer.writeAll(" = "); - try self.renderTypeName(alias.name); + try self.renderName(alias.name); try self.writer.writeAll(";\n"); } fn renderOpaque(self: *Self, name: []const u8) !void { try self.writer.writeAll("const "); - try self.renderTypeName(name); + try self.renderName(name); try self.writer.writeAll(" = @Type(.Opaque);\n"); } @@ -641,7 +660,7 @@ fn Renderer(comptime WriterType: type) type { fn renderTypedef(self: *Self, name: []const u8, type_info: reg.TypeInfo) !void { try self.writer.writeAll("const "); - try self.renderTypeName(name); + try self.renderName(name); try self.writer.writeAll(" = "); try self.renderTypeInfo(type_info); try self.writer.writeAll(";\n"); @@ -875,6 +894,8 @@ fn Renderer(comptime WriterType: type) type { if (returns.len == 1) { try self.writer.writeAll("var "); try self.writeIdentifierWithCase(.snake, returns[0].name); + try self.writer.writeAll(": "); + try self.renderTypeInfo(returns[0].return_value_type); try self.writer.writeAll(" = undefined;\n"); } else if (returns.len > 1) { try self.writer.writeAll("var return_values: ");