diff --git a/generator/generator.zig b/generator/generator.zig new file mode 100644 index 0000000..7bd7ec0 --- /dev/null +++ b/generator/generator.zig @@ -0,0 +1,174 @@ +const std = @import("std"); +const reg = @import("registry.zig"); +const xml = @import("xml.zig"); +const parseXml = @import("registry/parse.zig").parseXml; +const Allocator = std.mem.Allocator; +const FeatureLevel = reg.FeatureLevel; + +fn cmpFeatureLevels(a: FeatureLevel, b: FeatureLevel) std.math.Order { + if (a.major > b.major) { + return .gt; + } if (a.major < b.major) { + return .lt; + } + + if (a.minor > b.minor) { + return .gt; + } else if (a.minor < b.minor) { + return .lt; + } + + return .eq; +} + +const DeclarationResolver = struct { + const DeclarationSet = std.StringHashMap(void); + const EnumExtensionMap = std.StringHashMap(std.ArrayList(reg.Enum.Field)); + + allocator: *Allocator, + reg_arena: *Allocator, + registry: *reg.Registry, + declarations: DeclarationSet, + enum_extensions: EnumExtensionMap, + + fn init(allocator: *Allocator, reg_arena: *Allocator, registry: *reg.Registry) DeclarationResolver { + return .{ + .allocator = allocator, + .reg_arena = reg_arena, + .registry = registry, + .declarations = DeclarationSet.init(allocator), + .enum_extensions = EnumExtensionMap.init(allocator), + }; + } + + fn deinit(self: DeclarationResolver) void { + var it = self.enum_extensions.iterator(); + while (it.next()) |kv| { + kv.value.deinit(); + } + + self.enum_extensions.deinit(); + self.declarations.deinit(); + } + + fn putEnumExtension(self: *DeclarationResolver, enum_name: []const u8, field: reg.Enum.Field) !void { + const res = try self.enum_extensions.getOrPut(enum_name); + if (!res.found_existing) { + res.kv.value = std.ArrayList(reg.Enum.Field).init(self.allocator); + } + + try res.kv.value.append(field); + } + + fn addRequire(self: *DeclarationResolver, req: reg.Require) !void { + for (req.types) |type_name| { + _ = try self.declarations.put(type_name, {}); + } + + for (req.commands) |command| { + _ = try self.declarations.put(command, {}); + } + + for (req.extends) |enum_ext| { + try self.putEnumExtension(enum_ext.extends, enum_ext.field); + } + } + + fn mergeEnumFields(self: *DeclarationResolver, name: []const u8, base_enum: *reg.Enum) !void { + // If there are no extensions for this enum, assume its valid. + const extensions = self.enum_extensions.get(name) orelse return; + + // Todo: cache this in DeclarationResolver + var field_map = std.StringHashMap(reg.Enum.Value).init(self.allocator); + defer field_map.deinit(); + + for (base_enum.fields) |field| { + _ = try field_map.put(field.name, field.value); + } + + // Assume that if a field name clobbers, the value is the same + for (extensions.value.items) |field| { + _ = try field_map.put(field.name, field.value); + } + + const new_fields = try self.reg_arena.alloc(reg.Enum.Field, field_map.count()); + + var it = field_map.iterator(); + for (new_fields) |*field| { + const kv = it.next().?; + field.* = .{ + .name = kv.key, + .value = kv.value, + }; + } + + // Existing base_enum.fields was allocatued by `self.reg_arena`, so + // it gets cleaned up whenever that is deinited. + base_enum.fields = new_fields; + } + + fn resolve(self: *DeclarationResolver) !void { + for (self.registry.features) |feature| { + for (feature.requires) |req| { + try self.addRequire(req); + } + } + + for (self.registry.extensions) |ext| { + for (ext.requires) |req| { + try self.addRequire(req); + } + } + + // Merge all the enum fields. + // Assume that all keys of enum_extensions appear in `self.registry.decls` + for (self.registry.decls) |*decl| { + if (decl.decl_type == .enumeration) { + try self.mergeEnumFields(decl.name, &decl.decl_type.enumeration); + } + } + + // Swap-remove all declarations that are not required. + // Some declarations may exist in `self.declarations` that do not exit in + // `self.registry.decls`, these are mostly macros and other stuff not parsed. + var i: usize = 0; + var count = self.registry.decls.len; + while (i < count) { + const decl = self.registry.decls[i]; + if (self.declarations.contains(decl.name)) { + i += 1; + } else { + count -= 1; + self.registry.decls[i] = self.registry.decls[count]; + } + } + + self.registry.decls = self.reg_arena.shrink(self.registry.decls, count); + } +}; + +pub const Generator = struct { + gpa: *Allocator, + registry_arena: std.heap.ArenaAllocator, + registry: reg.Registry, + + pub fn init(allocator: *Allocator, spec: *xml.Element) !Generator { + const result = try parseXml(allocator, spec); + return Generator{ + .gpa = allocator, + .registry_arena = result.arena, + .registry = result.registry, + }; + } + + pub fn deinit(self: Generator) void { + self.registry_arena.deinit(); + } + + // Solve registry.declarations according to registry.extensions and registry.features + pub fn resolveDeclarations(self: *Generator) !void { + var resolver = DeclarationResolver.init(self.gpa, &self.registry_arena.allocator, &self.registry); + defer resolver.deinit(); + try resolver.resolve(); + } +}; diff --git a/generator/main.zig b/generator/main.zig index bbae7f2..20d5f39 100644 --- a/generator/main.zig +++ b/generator/main.zig @@ -1,9 +1,11 @@ const std = @import("std"); const xml = @import("xml.zig"); const parseXml = @import("registry/parse.zig").parseXml; -const registry = @import("registry.zig"); +const reg = @import("registry.zig"); +const vkgen = @import("generator.zig"); +const Allocator = std.mem.Allocator; -fn renderType(type_info: registry.TypeInfo) void { +fn renderType(type_info: reg.TypeInfo) void { switch (type_info) { .container => |c| { if (c.is_union) { @@ -45,12 +47,12 @@ fn renderType(type_info: registry.TypeInfo) void { } } -fn dumpRegistry(reg: registry.Registry) void { - for (reg.tags) |tag| { +fn dumpRegistry(registry: reg.Registry) void { + for (registry.tags) |tag| { std.debug.warn("tag: name = {}, author = {}\n", .{tag.name, tag.author}); } - for (reg.api_constants) |api_constant| { + for (registry.api_constants) |api_constant| { std.debug.warn("constant: name = {}, ", .{api_constant.name}); switch (api_constant.value) { .expr => |expr| std.debug.warn("expr = {}\n", .{expr}), @@ -58,35 +60,80 @@ fn dumpRegistry(reg: registry.Registry) void { } } - for (reg.decls) |decl| { + for (registry.decls) |decl| { std.debug.warn("decl: name = {}; ", .{decl.name}); renderType(decl.decl_type); std.debug.warn("\n", .{}); } } +const ProfilingAllocator = struct { + allocator: Allocator, + parent_allocator: *Allocator, + max_usage: usize, + current_usage: usize, + + fn init(parent_allocator: *Allocator) ProfilingAllocator { + return ProfilingAllocator{ + .allocator = Allocator{ + .reallocFn = realloc, + .shrinkFn = shrink, + }, + .parent_allocator = parent_allocator, + .max_usage = 0, + .current_usage = 0, + }; + } + + fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { + const self = @fieldParentPtr(ProfilingAllocator, "allocator", allocator); + self.current_usage = self.current_usage - old_mem.len + new_size; + if (self.current_usage > self.max_usage) { + self.max_usage = self.current_usage; + } + + return self.parent_allocator.reallocFn(self.parent_allocator, old_mem, old_align, new_size, new_align); + } + + fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 { + const self = @fieldParentPtr(ProfilingAllocator, "allocator", allocator); + return self.parent_allocator.shrinkFn(self.parent_allocator, old_mem, old_align, new_size, new_align); + } +}; + pub fn main() !void { - // if (std.os.argv.len <= 1) { - // std.debug.warn("Usage: vulkan-zig-gen \n", .{}); - // return; - // } + if (std.os.argv.len <= 1) { + std.debug.warn("Usage: vulkan-zig-gen \n", .{}); + return; + } + + var prof_alloc = ProfilingAllocator.init(std.heap.page_allocator); + const allocator = &prof_alloc.allocator; const file = try std.fs.cwd().openFileZ(std.os.argv[1], .{}); defer file.close(); const size = try file.seekableStream().getEndPos(); - const source = try std.heap.page_allocator.alloc(u8, size); - defer std.heap.page_allocator.free(source); + const source = try allocator.alloc(u8, size); + defer allocator.free(source); _ = try file.inStream().read(source); - const spec = try xml.parse(std.heap.page_allocator, source); + const spec = try xml.parse(allocator, source); defer spec.deinit(); - const result = try parseXml(std.heap.page_allocator, spec.root); - defer result.deinit(); + // const result = try parseXml(std.heap.page_allocator, spec.root); + // defer result.deinit(); - dumpRegistry(result.registry); + // dumpRegistry(result.registry); + + var gen = try vkgen.Generator.init(allocator, spec.root); + defer gen.deinit(); + + try gen.resolveDeclarations(); + + std.debug.warn("Total declarations: {}\n", .{gen.registry.decls.len}); + std.debug.warn("Total memory usage: {} KiB\n", .{@divTrunc(prof_alloc.max_usage, 1024)}); } test "main" { diff --git a/generator/registry/parse.zig b/generator/registry/parse.zig index d19b688..a3a2a8c 100644 --- a/generator/registry/parse.zig +++ b/generator/registry/parse.zig @@ -579,7 +579,7 @@ fn parseRequire(allocator: *Allocator, require: *xml.Element, extnumber: ?u31) ! }; return registry.Require{ - .extends = extends, + .extends = allocator.shrink(extends, i_extends), .types = types, .commands = commands, .required_feature_level = required_feature_level, @@ -594,10 +594,15 @@ fn parseExtensions(allocator: *Allocator, root: *xml.Element) ![]registry.Extens var i: usize = 0; var it = extensions_elem.findChildrenByTag("extension"); while (it.next()) |extension| { - if (try parseExtension(allocator, extension)) |ext| { - extensions[i] = ext; - i += 1; + // Some extensions (in particular 94) are disabled, so just skip them + if (extension.getAttribute("supported")) |supported| { + if (mem.eql(u8, supported, "disabled")) { + continue; + } } + + extensions[i] = try parseExtension(allocator, extension); + i += 1; } return allocator.shrink(extensions, i); @@ -619,12 +624,7 @@ fn findExtVersion(extension: *xml.Element) !u32 { return error.InvalidRegistry; } -fn parseExtension(allocator: *Allocator, extension: *xml.Element) !?registry.Extension { - // Some extensions (in particular 94) are disabled, so just skip them - if (extension.getAttribute("supported")) |supported| { - if (mem.eql(u8, supported, "disabled")) return null; - } - +fn parseExtension(allocator: *Allocator, extension: *xml.Element) !registry.Extension { const name = extension.getAttribute("name") orelse return error.InvalidRegistry; const platform = extension.getAttribute("platform"); const version = try findExtVersion(extension); diff --git a/generator/render.zig b/generator/render.zig index 5d0fe33..4e45d62 100644 --- a/generator/render.zig +++ b/generator/render.zig @@ -1,515 +1,3 @@ const std = @import("std"); -const ast = std.zig.ast; -const mem = std.mem; -const Allocator = mem.Allocator; -const reg = @import("registry.zig"); -const Registry = reg.Registry; - -const base_indent = " " ** 4; - -const ForeignType = struct { - name: []const u8, - expr: []const u8 -}; - -const foreign_types = [_]ForeignType{ - .{.name = "Display", .expr = "@Type(.Opaque)"}, - .{.name = "VisualID", .expr = @typeName(c_uint)}, - .{.name = "Window", .expr = @typeName(c_ulong)}, - .{.name = "RROutput", .expr = @typeName(c_ulong)}, - .{.name = "wl_display", .expr = "@Type(.Opaque)"}, - .{.name = "wl_surface", .expr = "@Type(.Opaque)"}, - .{.name = "HINSTANCE", .expr = "u32; //std.os.HINSTANCE"}, - .{.name = "HWND", .expr = "*@Type(.Opaque)"}, - .{.name = "HMONITOR", .expr = "*@Type(.Opaque)"}, - .{.name = "HANDLE", .expr = "u32; //std.os.HANDLE"}, - .{.name = "SECURITY_ATTRIBUTES", .expr = "u32; //std.os.SECURITY_ATTRIBUTES"}, - .{.name = "DWORD", .expr = "u32; //std.os.DWORD"}, - .{.name = "LPCWSTR", .expr = "u32; //std.os.LPCWSTR"}, - .{.name = "xcb_connection_t", .expr = "@Type(.Opaque)"}, - .{.name = "xcb_visualid_t", .expr = @typeName(u32)}, - .{.name = "xcb_window_t", .expr = @typeName(u32)}, - .{.name = "zx_handle_t", .expr = @typeName(u32)}, - .{.name = "GgpStreamDescriptor", .expr = @typeName(u32)}, // TODO: Remove GGP-related code - .{.name = "GgpFrameToken", .expr = @typeName(u32)}, - .{.name = "ANativeWindow", .expr = "@Type(.Opaque)"}, - .{.name = "AHardwareBuffer", .expr = "@Type(.Opaque)"}, - .{.name = "CAMetalLayer", .expr = "@Type(.Opaque)"}, -}; - -const foreign_types_namespace = "foreign"; - -const BuiltinType = struct { - c_name: []const u8, - zig_name: []const u8 -}; - -const builtin_types = [_]BuiltinType{ - .{.c_name = "void", .zig_name = @typeName(void)}, - .{.c_name = "char", .zig_name = @typeName(u8)}, - .{.c_name = "float", .zig_name = @typeName(f32)}, - .{.c_name = "double", .zig_name = @typeName(f64)}, - .{.c_name = "uint8_t", .zig_name = @typeName(u8)}, - .{.c_name = "uint16_t", .zig_name = @typeName(u16)}, - .{.c_name = "uint32_t", .zig_name = @typeName(u32)}, - .{.c_name = "uint64_t", .zig_name = @typeName(u64)}, - .{.c_name = "int32_t", .zig_name = @typeName(i32)}, - .{.c_name = "int64_t", .zig_name = @typeName(i64)}, - .{.c_name = "size_t", .zig_name = @typeName(usize)}, - .{.c_name = "int", .zig_name = @typeName(c_int)}, -}; - -pub fn render(out: var, registry: *Registry) !void { - try out.writeAll("const std = @import(\"std\");\n"); - try renderApiConstants(out, registry); - try renderForeignTypes(out); - try out.writeAll("\n"); - try renderDeclarations(out, registry); - try renderTest(out); -} - -fn trimNamespace(name: []const u8) []const u8 { - const prefixes = [_][]const u8{"VK_", "vk", "Vk", "PFN_vk"}; - for (prefixes) |prefix| { - if (mem.startsWith(u8, name, prefix)) { - return name[prefix.len..]; - } - } - - unreachable; -} - -fn getAuthorTag(registry: *Registry, name: []const u8) ?[]const u8 { - var it = registry.tags.iterator(0); - while (it.next()) |tag| { - if (mem.endsWith(u8, name, tag.name)) { - return tag; - } - } - - return null; -} - -fn trimTag(registry: *Registry, name: []const u8) []const u8 { - const tag = getAuthorTag(name) orelse return name; - return mem.trimRight(u8, name[0 .. name.len - tag.name.len], "_"); -} - -// Lifted from src-self-hosted/translate_c.zig -fn isValidZigIdentifier(name: []const u8) bool { - for (name) |c, i| { - switch (c) { - '_', 'a'...'z', 'A'...'Z' => {}, - '0' ... '9' => if (i == 0) return false, - else => return false - } - } - - return true; -} - -// Lifted from src-self-hosted/translate_c.zig -fn isZigReservedIdentifier(name: []const u8) bool { - if (name.len > 1 and (name[0] == 'u' or name[0] == 'i')) { - for (name[1..]) |c| { - switch (c) { - '0'...'9' => {}, - else => return false, - } - } - return true; - } - - // void is invalid in c so it doesn't need to be checked. - const reserved_names = [_][]const u8 { - "comptime_float", "comptime_int", "bool", "isize", - "usize", "f16", "f32", "f64", "f128", "c_longdouble", - "noreturn", "type", "anyerror", "c_short", "c_ushort", - "c_int", "c_uint", "c_long", "c_ulong", "c_longlong", "c_ulonglong" - }; - - for (reserved_names) |reserved| { - if (mem.eql(u8, reserved, name)) { - return true; - } - } - - return false; -} - -fn writeIdentifier(out: var, name: []const u8) !void { - if (!isValidZigIdentifier(name) or isZigReservedIdentifier(name) or std.zig.Token.getKeyword(name) != null) { - try out.print("@\"{}\"", .{name}); - } else { - try out.writeAll(name); - } -} - -const Identifier = struct { - name: []const u8, - - fn init(name: []const u8) Identifier { - return .{.name = name}; - } - - fn needEscape(self: Identifier) bool { - return !isValidZigIdentifier(self.name) - or isZigReservedIdentifier(self.name) - or std.zig.Token.getKeyword(self.name) != null; - } - - pub fn format( - self: Identifier, - fmt: []const u8, - options: std.fmt.FormatOptions, - out_stream: var - ) @TypeOf(out_stream).Error!void { - if (self.needEscape()){ - try out_stream.print("@\"{}\"", .{self.name}); - } else { - try out_stream.writeAll(self.name); - } - } -}; - -fn writeConstAssignmemt(out: var, name: []const u8) !void { - try out.print("pub const {} = ", .{Identifier.init(name)}); -} - -fn eqlIgnoreCase(lhs: []const u8, rhs: []const u8) bool { - if (lhs.len != rhs.len) { - return false; - } - - for (lhs) |c, i| { - if (std.ascii.toLower(c) != std.ascii.toLower(rhs[i])) { - return false; - } - } - - return true; -} - -fn renderTypeInfo(out: var, registry: *Registry, type_info: reg.TypeInfo) !void { - // If the type is a `void*`, it needs to be translated to `*c_void`. - // If its not a pointer, its a return type, so `void` should be emitted. - const is_void_ptr = mem.eql(u8, type_info.name, "void") and type_info.pointers.len > 0; - - if (type_info.array_size) |array_size| { - try out.print("[{}]", .{trimNamespace(array_size)}); - } - - for (type_info.pointers) |ptr| { - // Apparently Vulkan optional-ness information is not correct, so every pointer - // is considered optional - switch (ptr.size) { - .One => try out.writeAll("?*"), - .Many => if (is_void_ptr) - try out.writeAll("?*") - else - try out.writeAll("?[*]"), - .ZeroTerminated => try out.writeAll("?[*:0]") - } - - if (ptr.is_const) { - try out.writeAll("const "); - } - } - - if (is_void_ptr) { - try out.writeAll("c_void"); - return; - } - - // If the type is foreign, add the appropriate namespace specifier - for (foreign_types) |fty| { - if (mem.eql(u8, type_info.name, fty.name)) { - try out.print(foreign_types_namespace ++ ".{}", .{type_info.name}); - return; - } - } - - // Some types can be mapped directly to a built-in type - for (builtin_types) |bty| { - if (mem.eql(u8, type_info.name, bty.c_name)) { - try out.writeAll(bty.zig_name); - return; - } - } - - // Make sure the type is defined by Vulkan otherwise - if (registry.findDefinitionByName(type_info.name)) |def| { - if (def.* == .FnPtr) { - try out.writeAll("PFN_"); - } - - try writeIdentifier(out, trimNamespace(type_info.name)); - } else { - return error.InvalidType; - } -} - -fn renderApiConstantExpr(out: var, constant_expr: []const u8) !void { - // omit enclosing parenthesis - const expr = if (constant_expr[0] == '(' and constant_expr[constant_expr.len - 1] == ')') - constant_expr[1 .. constant_expr.len - 1] - else - constant_expr; - - var i: usize = 0; - while (i < expr.len) { - switch (expr[i]) { - '(', ')', '~' => try out.writeByte(expr[i]), - '-' => try out.writeAll(" - "), - 'a'...'z', 'A'...'Z', '_' => { - var j = i; - while (j < expr.len) : (j += 1) { - switch (expr[j]) { - 'a'...'z', 'A'...'Z', '_', '0'...'9' => {}, - else => break - } - } - - try writeIdentifier(out, trimNamespace(expr[i .. j])); - i = j; - continue; - }, - '0'...'9' => { - var j = i; - while (j < expr.len) : (j += 1) { - switch (expr[j]) { - '0'...'9', '.' => {}, - else => break - } - } - - if (mem.startsWith(u8, expr[j..], "f")) { - try out.print("@as(f32, {})", .{expr[i .. j]}); - j += "f".len; - } else if (mem.startsWith(u8, expr[j..], "ULL")) { - try out.print("@as(u32, {})", .{expr[i .. j]}); - j += "ULL".len; - } else if (mem.startsWith(u8, expr[j..], "U")) { - try out.print("@as(u64, {})", .{expr[i .. j]}); - j += "U".len; - } else { - try out.writeAll(expr[i .. j]); - } - - i = j; - continue; - }, - ' ' => {}, - else => return error.InvalidConstantExpr - } - - i += 1; - } -} - -fn renderApiConstants(out: var, registry: *Registry) !void { - var it = registry.api_constants.iterator(0); - while (it.next()) |constant| { - try writeConstAssignmemt(out, trimNamespace(constant.name)); - try renderApiConstantExpr(out, constant.expr); - try out.writeAll(";\n"); - } - - try out.writeAll("\n"); -} - -fn renderForeignTypes(out: var) !void { - try writeConstAssignmemt(out, foreign_types_namespace); - try out.writeAll("struct {\n"); - - for (foreign_types) |fty| { - try out.writeAll(base_indent); - try writeConstAssignmemt(out, fty.name); - try out.print("{};\n", .{fty.expr}); - } - - try out.writeAll("};\n"); -} - -fn renderDeclarations(out: var, registry: *Registry) !void { - var it = registry.declarations.iterator(0); - while (it.next()) |decl| { - switch (decl.definition) { - .Struct => |*info| try renderContainer(out, registry, .Struct, decl.name, info), - .Union => |*info| try renderContainer(out, registry, .Union, decl.name, info), - .Enum => |*info| try renderEnum(out, decl.name, info), - .Bitmask => |*info| try renderBitmask(out, decl.name, info), - .Handle => |*info| try renderHandle(out, decl.name, info), - .FnPtr => |*info| try renderFnPtr(out, registry, decl.name, info), - .Command => |*info| try renderCommand(out, registry, decl.name, info), - .Alias => |alias| try renderAlias(out, registry, decl.name, alias), - .BaseType => |type_info| { - try writeConstAssignmemt(out, trimNamespace(decl.name)); - try renderTypeInfo(out, registry, type_info); - try out.writeAll(";\n"); - }, - - } - } -} - -fn renderCommand(out: var, registry: *Registry, name: []const u8, info: *reg.CommandInfo) !void { - try out.writeAll("extern fn "); - try writeIdentifier(out, name); - try out.writeAll("("); - - if (info.parameters.count() > 0) { - try out.writeAll("\n"); - var it = info.parameters.iterator(0); - while (it.next()) |param| { - try out.writeAll(base_indent); - try writeIdentifier(out, param.name); - try out.writeAll(": "); - try renderTypeInfo(out, registry, param.type_info); - try out.writeAll(",\n"); - } - } - - try out.writeAll(") callconv(.C) "); - try renderTypeInfo(out, registry, info.return_type_info); - try out.writeAll(";\n\n"); - - // var buf: [256]u8 = undefined; - // const trimmed = trimNamespace(name); - // mem.copy(u8, &buf, trimNamespace(name)); - // buf[0] = std.ascii.toLower(buf[0]); - - // try writeConstAssignmemt(out, buf[0 .. trimmed.len]); - // try writeIdentifier(out, name); - // try out.writeAll(";\n"); -} - -fn renderHandle(out: var, name: []const u8, info: *const reg.HandleInfo) !void { - try writeConstAssignmemt(out, trimNamespace(name)); - // Todo: (non)-dispatchable - try out.writeAll("*@Type(.Opaque);\n"); -} - -fn renderBitmask(out: var, name: []const u8, info: *const reg.BitmaskInfo) !void { - try writeConstAssignmemt(out, trimNamespace(name)); - try out.writeAll("Flags;\n"); -} - -fn shouldSkipEnum(enum_info: *reg.EnumInfo) bool { - // Skip empty declarations (which are unused bitflags) - return enum_info.variants.count() == 0; -} - -fn renderEnum(out: var, name: []const u8, enum_info: *reg.EnumInfo) !void { - if (shouldSkipEnum(enum_info)) { - return; - } - - const trimmed_name = trimNamespace(name); - - try writeConstAssignmemt(out, trimmed_name); - try out.writeAll("extern enum {\n"); - - // Calculate the length of the enum namespace, by iterating through the segments - // of the variant (in screaming snake case) and comparing it to the name of the enum, - // until the two differ. - var prefix_len: usize = 0; - var snake_prefix_len: usize = 0; - var segment_it = mem.split(enum_info.variants.at(0).name, "_"); - while (segment_it.next()) |segment| { - if (prefix_len + segment.len <= name.len and eqlIgnoreCase(segment, name[prefix_len .. prefix_len + segment.len])) { - prefix_len += segment.len; - snake_prefix_len += segment.len + 1; // Add one for the underscore - } else { - break; - } - } - - var it = enum_info.variants.iterator(0); - while (it.next()) |variant| { - if (variant.value == .Alias) continue; // Skip aliases - - try out.writeAll(base_indent); - try writeIdentifier(out, variant.name[snake_prefix_len ..]); - - switch (variant.value) { - .Value => |value| try out.print(" = {},\n", .{value}), - .HexValue => |value| try out.print(" = 0x{X},\n", .{value}), - .Bitpos => |value| try out.print(" = 1 << {},\n", .{value}), - .Alias => unreachable - } - } - - try out.writeAll("};\n"); -} - -fn renderAlias(out: var, registry: *Registry, name: []const u8, alias: []const u8) !void { - // An declaration may be aliased to a bit flag enum which has no members, in which case - // the alias also has to be skipped. - var def = registry.findDefinitionByName(alias).?; - while (def.* == .Alias) { - def = registry.findDefinitionByName(def.Alias).?; - } - - if ((def.* == .Enum and shouldSkipEnum(&def.Enum)) or def.* == .Command) { - return; - } - - try writeConstAssignmemt(out, trimNamespace(name)); - try writeIdentifier(out, trimNamespace(alias)); - try out.writeAll(";\n"); -} - -fn renderFnPtr(out: var, registry: *Registry, name: []const u8, info: *reg.CommandInfo) !void { - try out.writeAll("pub const PFN_"); - try writeIdentifier(out, name); - try out.writeAll(" = "); - - try out.writeAll("?fn("); - - if (info.parameters.count() > 0) { - try out.writeAll("\n"); - var it = info.parameters.iterator(0); - while (it.next()) |param| { - try out.writeAll(base_indent); - try writeIdentifier(out, param.name); - try out.writeAll(": "); - try renderTypeInfo(out, registry, param.type_info); - try out.writeAll(",\n"); - } - } - - try out.writeAll(") callconv(.C) "); - try renderTypeInfo(out, registry, info.return_type_info); - try out.writeAll(";\n"); -} - -fn renderContainer(out: var, registry: *Registry, kind: enum{Struct, Union}, name: []const u8, info: *reg.ContainerInfo) !void { - try writeConstAssignmemt(out, trimNamespace(name)); - - switch (kind) { - .Struct => try out.writeAll("extern struct {\n"), - .Union => try out.writeAll("extern union {\n") - } - - var it = info.members.iterator(0); - while (it.next()) |member| { - try out.writeAll(base_indent); - try writeIdentifier(out, member.name); - try out.writeAll(": "); - try renderTypeInfo(out, registry, member.type_info); - try out.writeAll(",\n"); - } - - try out.writeAll("};\n"); -} - -fn renderTest(out: var) !void { - try out.writeAll( - \\test "Semantic analysis" { - \\ @setEvalBranchQuota(2000); - \\ std.meta.refAllDecls(@This()); - \\} - \\ - ); -} +const registry = @import("registry.zig"); +const Allocator = std.mem.Allocator, diff --git a/generator/xml.zig b/generator/xml.zig index 2415e0c..96783c9 100644 --- a/generator/xml.zig +++ b/generator/xml.zig @@ -17,7 +17,7 @@ pub const Content = union(enum) { }; pub const Element = struct { - pub const AttributeList = SegmentedList(*Attribute, 0); + pub const AttributeList = SegmentedList(*Attribute, 1); pub const ContentList = SegmentedList(Content, 0); tag: []const u8,