diff --git a/generator/registry-new.zig b/generator/registry-new.zig index 8113eda..1d54a49 100644 --- a/generator/registry-new.zig +++ b/generator/registry-new.zig @@ -12,7 +12,7 @@ pub const Declaration = struct { pub const ApiConstant = struct { pub const Value = union(enum) { expr: []const u8, - alias: []const u8, + alias: []const u8, // Alias of another API constant }; name: []const u8, @@ -50,9 +50,12 @@ pub const Container = struct { pub const Enum = struct { pub const Value = union(enum) { bitpos: u5, // 1 << bitpos - bitvector: u32, // Combined flags + bit_vector: i32, // Combined flags & some vendor IDs int: i32, - alias: []const u8, + alias: struct { + alias_name: []const u8, + is_compat_alias: bool, + } }; pub const Field = struct { @@ -61,7 +64,7 @@ pub const Enum = struct { }; fields: []Field, - is_bitmask_bits: bool, + is_bitmask: bool, }; pub const Bitmask = struct { diff --git a/generator/spec-parse.zig b/generator/spec-parse.zig index 710b4d6..68d852e 100644 --- a/generator/spec-parse.zig +++ b/generator/spec-parse.zig @@ -5,6 +5,8 @@ const mem = std.mem; const Allocator = mem.Allocator; const ArenaAllocator = std.heap.ArenaAllocator; +const api_constants_name = "API Constants"; + pub const ParseResult = struct { arena: ArenaAllocator, registry: registry.Registry, @@ -14,18 +16,17 @@ pub const ParseResult = struct { } }; -pub fn parseXml(allocator: *Allocator, root: *xml.Element) !ParseResult { - var arena = ArenaAllocator.init(allocator); +pub fn parseXml(backing_allocator: *Allocator, root: *xml.Element) !ParseResult { + var arena = ArenaAllocator.init(backing_allocator); errdefer arena.deinit(); - var reg = registry.Registry{ - .decls = &[_]registry.Declaration{}, - .api_constants = &[_]registry.ApiConstant{}, - .tags = &[_]registry.Tag{}, - }; + const allocator = &arena.allocator; - reg.api_constants = try parseApiConstants(&arena.allocator, root); - reg.tags = try parseTags(&arena.allocator, root); + var reg = registry.Registry{ + .decls = try parseDeclarations(allocator, root), + .api_constants = try parseApiConstants(allocator, root), + .tags = try parseTags(allocator, root), + }; return ParseResult{ .arena = arena, @@ -33,12 +34,117 @@ pub fn parseXml(allocator: *Allocator, root: *xml.Element) !ParseResult { }; } +fn parseDeclarations(allocator: *Allocator, root: *xml.Element) ![]registry.Declaration { + var types_elem = root.findChildByTag("types") orelse return error.InvalidRegistry; + var commands_elem = root.findChildByTag("commands") orelse return error.InvalidRegistry; + + const decl_upper_bound = types_elem.children.count() + commands_elem.children.count(); + const decls = try allocator.alloc(registry.Declaration, decl_upper_bound); + errdefer allocator.free(decls); + + var count: usize = 0; + count += try parseTypes(allocator, decls, types_elem); + count += try parseEnums(allocator, decls[count..], root); + count += try parseCommands(allocator, decls[count..], commands_elem); + return allocator.shrink(decls, count); +} + +fn parseTypes(allocator: *Allocator, out: []registry.Declaration, types_elem: *xml.Element) !usize { + return 0; +} + +fn parseEnums(allocator: *Allocator, out: []registry.Declaration, root: *xml.Element) !usize { + var i: usize = 0; + var it = root.findChildrenByTag("enums"); + while (it.next()) |enums| { + const name = enums.getAttribute("name") orelse return error.InvalidRegistry; + if (mem.eql(u8, name, api_constants_name)) { + continue; + } + + out[i] = .{ + .name = name, + .decl_type = .{.Enum = try parseEnumFields(allocator, enums)}, + }; + i += 1; + } + + return i; +} + +fn parseEnumFields(allocator: *Allocator, elem: *xml.Element) !registry.Enum { + // TODO: `type` was added recently, fall back to checking endswith FlagBits for older versions? + const enum_type = elem.getAttribute("type") orelse return error.InvalidRegistry; + const is_bitmask = mem.eql(u8, enum_type, "bitmask"); + if (!is_bitmask and !mem.eql(u8, enum_type, "enum")) { + return error.InvalidRegistry; + } + + const fields = try allocator.alloc(registry.Enum.Field, elem.children.count()); + errdefer allocator.free(fields); + + var i: usize = 0; + var it = elem.findChildrenByTag("enum"); + while (it.next()) |field| { + fields[i] = try parseEnumField(field); + i += 1; + } + + return registry.Enum{ + .fields = allocator.shrink(fields, i), + .is_bitmask = is_bitmask, + }; +} + +fn parseEnumField(field: *xml.Element) !registry.Enum.Field { + const is_compat_alias = if (field.getAttribute("comment")) |comment| + mem.eql(u8, comment, "Backwards-compatible alias containing a typo") or + mem.eql(u8, comment, "Deprecated name for backwards compatibility") + else + false; + + const name = field.getAttribute("name") orelse return error.InvalidRegistry; + const value: registry.Enum.Value = blk: { + // An enum variant's value could be defined by any of the following attributes: + // - value: Straight up value of the enum variant, in either base 10 or 16 (prefixed with 0x). + // - bitpos: Used for bitmasks, and can also be set in extensions. + // - alias: The field is an alias of another variant within the same enum. + // - offset: Used with features and extensions, where a non-bitpos value is added to an enum. + // The value is given by `1e9 + (extr_nr - 1) * 1e3 + offset`, where `ext_nr` is either + // given by the `extnumber` field (in the case of a feature), or given in the parent + // tag. In the latter case its passed via the `ext_nr` parameter. + // TODO: Handle `offset` elsewhere + if (field.getAttribute("value")) |value| { + if (mem.startsWith(u8, value, "0x")) { + break :blk .{.bit_vector = try std.fmt.parseInt(i32, value[2..], 16)}; + } else { + 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)}; + } else if (field.getAttribute("alias")) |alias| { + break :blk .{.alias = .{.alias_name = alias, .is_compat_alias = is_compat_alias}}; + } else { + return error.InvalidRegistry; + } + }; + + return registry.Enum.Field{ + .name = name, + .value = value, + }; +} + +fn parseCommands(allocator: *Allocator, out: []registry.Declaration, commmands_elem: *xml.Element) !usize { + return 0; +} + fn parseApiConstants(allocator: *Allocator, root: *xml.Element) ![]registry.ApiConstant { var enums = blk: { - var it = root.elements(); + var it = root.findChildrenByTag("enums"); while (it.next()) |child| { const name = child.getAttribute("name") orelse continue; - if (mem.eql(u8, name, "API Constants")) { + if (mem.eql(u8, name, api_constants_name)) { break :blk child; } } @@ -46,7 +152,7 @@ fn parseApiConstants(allocator: *Allocator, root: *xml.Element) ![]registry.ApiC return error.InvalidRegistry; }; - var constants = try allocator.alloc(registry.ApiConstant, enums.children.count()); + const constants = try allocator.alloc(registry.ApiConstant, enums.children.count()); errdefer allocator.free(constants); var i: usize = 0; @@ -72,7 +178,7 @@ fn parseApiConstants(allocator: *Allocator, root: *xml.Element) ![]registry.ApiC fn parseTags(allocator: *Allocator, root: *xml.Element) ![]registry.Tag { var tags_elem = root.findChildByTag("tags") orelse return error.InvalidRegistry; - var tags = try allocator.alloc(registry.Tag, tags_elem.children.count()); + const tags = try allocator.alloc(registry.Tag, tags_elem.children.count()); errdefer allocator.free(tags); var i: usize = 0;