forked from mirror/vulkan-zig
API definitions and enum declaration rendering
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const xml = @import("xml.zig");
|
const xml = @import("xml.zig");
|
||||||
const Registry = @import("registry.zig").Registry;
|
const Registry = @import("registry.zig").Registry;
|
||||||
|
const vk_render = @import("render.zig").render;
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
if (std.os.argv.len <= 1) {
|
if (std.os.argv.len <= 1) {
|
||||||
@@ -22,8 +23,11 @@ pub fn main() !void {
|
|||||||
|
|
||||||
const registry = Registry.fromXml(std.heap.page_allocator, spec.root);
|
const registry = Registry.fromXml(std.heap.page_allocator, spec.root);
|
||||||
defer registry.deinit();
|
defer registry.deinit();
|
||||||
|
// registry.dump();
|
||||||
|
|
||||||
registry.dump();
|
const stdout_file = std.io.getStdOut();
|
||||||
|
var stdout = stdout_file.outStream();
|
||||||
|
try vk_render(&stdout.stream, registry);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "main" {
|
test "main" {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ pub const Registry = struct {
|
|||||||
declarations: SegmentedList(Declaration, 0),
|
declarations: SegmentedList(Declaration, 0),
|
||||||
declarations_by_name: StringHashMap(*Declaration),
|
declarations_by_name: StringHashMap(*Declaration),
|
||||||
api_constants: SegmentedList(ApiConstant, 0),
|
api_constants: SegmentedList(ApiConstant, 0),
|
||||||
|
tags: SegmentedList(TagInfo, 0),
|
||||||
extensions: SegmentedList(ExtensionInfo, 0),
|
extensions: SegmentedList(ExtensionInfo, 0),
|
||||||
|
|
||||||
fn init(allocator: *Allocator) !*Registry {
|
fn init(allocator: *Allocator) !*Registry {
|
||||||
@@ -25,6 +26,7 @@ pub const Registry = struct {
|
|||||||
.declarations = undefined,
|
.declarations = undefined,
|
||||||
.declarations_by_name = StringHashMap(*Declaration).init(allocator),
|
.declarations_by_name = StringHashMap(*Declaration).init(allocator),
|
||||||
.api_constants = undefined,
|
.api_constants = undefined,
|
||||||
|
.tags = undefined,
|
||||||
.extensions = undefined
|
.extensions = undefined
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -33,6 +35,7 @@ pub const Registry = struct {
|
|||||||
|
|
||||||
registry.declarations = SegmentedList(Declaration, 0).init(®istry.arena.allocator);
|
registry.declarations = SegmentedList(Declaration, 0).init(®istry.arena.allocator);
|
||||||
registry.api_constants = SegmentedList(ApiConstant, 0).init(®istry.arena.allocator);
|
registry.api_constants = SegmentedList(ApiConstant, 0).init(®istry.arena.allocator);
|
||||||
|
registry.tags = SegmentedList(TagInfo, 0).init(®istry.arena.allocator);
|
||||||
registry.extensions = SegmentedList(ExtensionInfo, 0).init(®istry.arena.allocator);
|
registry.extensions = SegmentedList(ExtensionInfo, 0).init(®istry.arena.allocator);
|
||||||
|
|
||||||
return registry;
|
return registry;
|
||||||
@@ -47,6 +50,7 @@ pub const Registry = struct {
|
|||||||
processEnums(registry, root);
|
processEnums(registry, root);
|
||||||
processCommands(registry, root);
|
processCommands(registry, root);
|
||||||
processFeatures(registry, root);
|
processFeatures(registry, root);
|
||||||
|
processTags(registry, root);
|
||||||
processExtensions(registry, root);
|
processExtensions(registry, root);
|
||||||
|
|
||||||
return registry;
|
return registry;
|
||||||
@@ -77,6 +81,10 @@ pub const Registry = struct {
|
|||||||
self.api_constants.push(.{.name = name, .expr = expr}) catch unreachable;
|
self.api_constants.push(.{.name = name, .expr = expr}) catch unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn addTag(self: *Registry, name: []const u8, author: []const u8) void {
|
||||||
|
self.tags.push(.{.name = name, .author = author}) catch unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
fn findDefinitionByName(self: *Registry, name: []const u8) ?*Definition {
|
fn findDefinitionByName(self: *Registry, name: []const u8) ?*Definition {
|
||||||
if (self.declarations_by_name.get(name)) |kv| {
|
if (self.declarations_by_name.get(name)) |kv| {
|
||||||
return &kv.value.definition;
|
return &kv.value.definition;
|
||||||
@@ -86,11 +94,13 @@ pub const Registry = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn dump(self: *Registry) void {
|
fn dump(self: *Registry) void {
|
||||||
|
const indent = " " ** 4;
|
||||||
|
|
||||||
{
|
{
|
||||||
std.debug.warn("Definitions:\n", .{});
|
std.debug.warn("Definitions:\n", .{});
|
||||||
var it = self.declarations.iterator(0);
|
var it = self.declarations.iterator(0);
|
||||||
while (it.next()) |decl| {
|
while (it.next()) |decl| {
|
||||||
std.debug.warn(" {} ({})\n", .{decl.name, std.meta.tagName(decl.definition)});
|
std.debug.warn(indent ++ "{} ({})\n", .{decl.name, std.meta.tagName(decl.definition)});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,7 +108,15 @@ pub const Registry = struct {
|
|||||||
std.debug.warn("API constants:\n", .{});
|
std.debug.warn("API constants:\n", .{});
|
||||||
var it = self.api_constants.iterator(0);
|
var it = self.api_constants.iterator(0);
|
||||||
while (it.next()) |kv| {
|
while (it.next()) |kv| {
|
||||||
std.debug.warn(" {} = {}\n", .{kv.name, kv.expr});
|
std.debug.warn(indent ++ "{} = {}\n", .{kv.name, kv.expr});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std.debug.warn("Tags:\n", .{});
|
||||||
|
var it = self.tags.iterator(0);
|
||||||
|
while (it.next()) |tag| {
|
||||||
|
std.debug.warn(indent ++ "{} ({})\n", .{tag.name, tag.author});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,29 +124,34 @@ pub const Registry = struct {
|
|||||||
std.debug.warn("Extensions:\n", .{});
|
std.debug.warn("Extensions:\n", .{});
|
||||||
var it = self.extensions.iterator(0);
|
var it = self.extensions.iterator(0);
|
||||||
while (it.next()) |ext| {
|
while (it.next()) |ext| {
|
||||||
std.debug.warn(" {}: {}, version {}\n", .{ext.number, ext.name, ext.version});
|
std.debug.warn(indent ++ "{}: {}, version {}\n", .{ext.number, ext.name, ext.version});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const ApiConstant = struct {
|
pub const ApiConstant = struct {
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
expr: []const u8
|
expr: []const u8
|
||||||
};
|
};
|
||||||
|
|
||||||
const ExtensionInfo = struct {
|
pub const ExtensionInfo = struct {
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
number: u32,
|
number: u32,
|
||||||
version: u32,
|
version: u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Declaration = struct {
|
pub const TagInfo = struct {
|
||||||
|
name: []const u8,
|
||||||
|
author: []const u8
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Declaration = struct {
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
definition: Definition
|
definition: Definition
|
||||||
};
|
};
|
||||||
|
|
||||||
const Definition = union(enum) {
|
pub const Definition = union(enum) {
|
||||||
Struct: StructInfo,
|
Struct: StructInfo,
|
||||||
Enum: EnumInfo,
|
Enum: EnumInfo,
|
||||||
Bitmask: BitmaskInfo,
|
Bitmask: BitmaskInfo,
|
||||||
@@ -139,16 +162,16 @@ const Definition = union(enum) {
|
|||||||
BaseType: TypeInfo
|
BaseType: TypeInfo
|
||||||
};
|
};
|
||||||
|
|
||||||
const HandleInfo = struct {
|
pub const HandleInfo = struct {
|
||||||
dispatchable: bool
|
dispatchable: bool
|
||||||
};
|
};
|
||||||
|
|
||||||
const BitmaskInfo = struct {
|
pub const BitmaskInfo = struct {
|
||||||
bits_enum: ?[]const u8
|
bits_enum: ?[]const u8
|
||||||
};
|
};
|
||||||
|
|
||||||
// Type info of fields, function parameters, and return types.
|
// Type info of fields, function parameters, and return types.
|
||||||
const TypeInfo = struct {
|
pub const TypeInfo = struct {
|
||||||
const PointerSize = enum {
|
const PointerSize = enum {
|
||||||
One,
|
One,
|
||||||
Many, // The length is given by some expression which cannot be expressed in Zig
|
Many, // The length is given by some expression which cannot be expressed in Zig
|
||||||
@@ -334,7 +357,7 @@ const TypeInfo = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const StructInfo = struct {
|
pub const StructInfo = struct {
|
||||||
const Member = struct {
|
const Member = struct {
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
type_info: TypeInfo
|
type_info: TypeInfo
|
||||||
@@ -367,7 +390,7 @@ const StructInfo = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const CommandInfo = struct {
|
pub const CommandInfo = struct {
|
||||||
const Parameter = struct {
|
const Parameter = struct {
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
type_info: TypeInfo
|
type_info: TypeInfo
|
||||||
@@ -453,9 +476,10 @@ const CommandInfo = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const EnumInfo = struct {
|
pub const EnumInfo = struct {
|
||||||
const Value = union(enum) {
|
const Value = union(enum) {
|
||||||
Bitpos: u5, //log2(u32)
|
Bitpos: u5, //log2(u32)
|
||||||
|
HexValue: i32, // Combined flags and such
|
||||||
Value: i32,
|
Value: i32,
|
||||||
Alias: []const u8,
|
Alias: []const u8,
|
||||||
};
|
};
|
||||||
@@ -474,7 +498,16 @@ const EnumInfo = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn addVariant(self: *EnumInfo, name: []const u8, value: Value) void {
|
fn addVariant(self: *EnumInfo, name: []const u8, value: Value) void {
|
||||||
const ptr = self.variants.push(.{.name = name, .value = value}) catch unreachable;
|
// Sometimes a variant is added multiple times with different 'require' parts of an extension
|
||||||
|
// so filter out any duplicates.
|
||||||
|
var it = self.variants.iterator(0);
|
||||||
|
while (it.next()) |variant| {
|
||||||
|
if (mem.eql(u8, variant.name, name)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = self.variants.push(.{.name = name, .value = value}) catch unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn processVariantFromXml(self: *EnumInfo, variant: *xml.Element, ext_nr: ?u32) void {
|
fn processVariantFromXml(self: *EnumInfo, variant: *xml.Element, ext_nr: ?u32) void {
|
||||||
@@ -490,7 +523,11 @@ const EnumInfo = struct {
|
|||||||
// given by the `extnumber` field (in the case of a feature), or given in the parent <extension>
|
// given by the `extnumber` field (in the case of a feature), or given in the parent <extension>
|
||||||
// tag. In the latter case its passed via the `ext_nr` parameter.
|
// tag. In the latter case its passed via the `ext_nr` parameter.
|
||||||
if (variant.getAttribute("value")) |value_str| {
|
if (variant.getAttribute("value")) |value_str| {
|
||||||
break :blk Value{.Value = parseInt(i32, value_str) catch unreachable};
|
if (mem.startsWith(u8, value_str, "0x")) {
|
||||||
|
break :blk Value{.HexValue = std.fmt.parseInt(i32, value_str[2..], 16) catch unreachable};
|
||||||
|
} else {
|
||||||
|
break :blk Value{.Value = std.fmt.parseInt(i32, value_str, 10) catch unreachable};
|
||||||
|
}
|
||||||
} else if (variant.getAttribute("bitpos")) |bitpos_str| {
|
} else if (variant.getAttribute("bitpos")) |bitpos_str| {
|
||||||
break :blk Value{.Bitpos = std.fmt.parseInt(u5, bitpos_str, 10) catch unreachable};
|
break :blk Value{.Bitpos = std.fmt.parseInt(u5, bitpos_str, 10) catch unreachable};
|
||||||
} else if (variant.getAttribute("alias")) |alias| {
|
} else if (variant.getAttribute("alias")) |alias| {
|
||||||
@@ -666,6 +703,16 @@ fn processCommands(registry: *Registry, root: *xml.Element) void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn processTags(registry: *Registry, root: *xml.Element) void {
|
||||||
|
var tags = root.findChildByTag("tags").?;
|
||||||
|
var it = tags.findChildrenByTag("tag");
|
||||||
|
while (it.next()) |tag| {
|
||||||
|
const name = tag.getAttribute("name").?;
|
||||||
|
const author = tag.getAttribute("author").?;
|
||||||
|
registry.addTag(name, author);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn processExtensions(registry: *Registry, root: *xml.Element) void {
|
fn processExtensions(registry: *Registry, root: *xml.Element) void {
|
||||||
var extensions = root.findChildByTag("extensions").?;
|
var extensions = root.findChildByTag("extensions").?;
|
||||||
var ext_it = extensions.findChildrenByTag("extension");
|
var ext_it = extensions.findChildrenByTag("extension");
|
||||||
@@ -736,11 +783,3 @@ fn count(haystack: []const u8, needle: u8) usize {
|
|||||||
|
|
||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an integer in either base-10 or base-16 when prefixed with '0x'.
|
|
||||||
fn parseInt(comptime T: type, source: []const u8) !T {
|
|
||||||
return if (mem.startsWith(u8, source, "0x"))
|
|
||||||
try std.fmt.parseInt(T, source[2..], 16)
|
|
||||||
else
|
|
||||||
try std.fmt.parseInt(T, source, 10);
|
|
||||||
}
|
|
||||||
|
|||||||
207
generator/render.zig
Normal file
207
generator/render.zig
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
pub fn render(out: var, registry: *Registry) !void {
|
||||||
|
try renderApiConstants(out, registry);
|
||||||
|
try out.write("\n");
|
||||||
|
try renderDeclarations(out, registry);
|
||||||
|
try renderTest(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trimNamespace(name: []const u8) []const u8 {
|
||||||
|
if (mem.startsWith(u8, name, "VK_")) {
|
||||||
|
return name["VK_".len ..];
|
||||||
|
} else if (mem.startsWith(u8, name, "vk")) {
|
||||||
|
return name["vk".len ..];
|
||||||
|
} else if (mem.startsWith(u8, name, "Vk")) {
|
||||||
|
return name["Vk".len ..];
|
||||||
|
} else {
|
||||||
|
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], "_");
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn writeIdentifier(out: var, name: []const u8) !void {
|
||||||
|
if (!isValidZigIdentifier(name) or std.zig.Token.getKeyword(name) != null) {
|
||||||
|
try out.print("@\"{}\"", .{name});
|
||||||
|
} else {
|
||||||
|
try out.write(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 renderApiConstantExpr(out: var, constexpr: []const u8) !void {
|
||||||
|
// There are only a few different kinds of tokens in the expressions,
|
||||||
|
// all of which can be tokenized by the Zig tokenizer. The only parts which cannot
|
||||||
|
// be parsed properly are 'f', 'U', and 'ULL' suffixes.
|
||||||
|
// Render the C expression by simply substituting those values
|
||||||
|
|
||||||
|
// omit enclosing parenthesis
|
||||||
|
const expr = if (constexpr[0] == '(' and constexpr[constexpr.len - 1] == ')')
|
||||||
|
constexpr[1 .. constexpr.len - 1]
|
||||||
|
else
|
||||||
|
constexpr;
|
||||||
|
|
||||||
|
var tokenizer = std.zig.Tokenizer.init(expr);
|
||||||
|
var peek_tok: ?std.zig.Token = null;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const tok = peek_tok orelse tokenizer.next();
|
||||||
|
const text = expr[tok.start .. tok.end];
|
||||||
|
peek_tok = null;
|
||||||
|
|
||||||
|
switch (tok.id) {
|
||||||
|
.LParen, .RParen, .Tilde => try out.write(text),
|
||||||
|
.Identifier => try writeIdentifier(out, trimNamespace(text)),
|
||||||
|
.Minus => try out.write(" - "),
|
||||||
|
.FloatLiteral => {
|
||||||
|
try out.print("@as(f32, {})", .{text});
|
||||||
|
|
||||||
|
// Float literal has to be followed by an 'f' identifier.
|
||||||
|
const suffix = tokenizer.next();
|
||||||
|
const suffix_text = expr[suffix.start .. suffix.end];
|
||||||
|
if (suffix.id != .Identifier or !mem.eql(u8, suffix_text, "f")) {
|
||||||
|
return error.ExpectedFloatSuffix;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.IntegerLiteral => {
|
||||||
|
const suffix = tokenizer.next();
|
||||||
|
const suffix_text = expr[suffix.start .. suffix.end];
|
||||||
|
|
||||||
|
if (suffix.id != .Identifier) {
|
||||||
|
// Only need to check here because any identifier following an integer
|
||||||
|
// that is not 'U' or 'ULL' is a syntax error.
|
||||||
|
peek_tok = suffix;
|
||||||
|
try out.write(text);
|
||||||
|
} else if (mem.eql(u8, suffix_text, "U")) {
|
||||||
|
try out.print("@as(u32, {})", .{text});
|
||||||
|
} else if (mem.eql(u8, suffix_text, "ULL")) {
|
||||||
|
try out.print("@as(u64, {})", .{text});
|
||||||
|
} else {
|
||||||
|
return error.InvalidIntSuffix;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.Eof => return,
|
||||||
|
else => return error.UnexpectedToken
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renderApiConstants(out: var, registry: *Registry) !void {
|
||||||
|
var it = registry.api_constants.iterator(0);
|
||||||
|
while (it.next()) |constant| {
|
||||||
|
try out.write("const ");
|
||||||
|
try writeIdentifier(out, trimNamespace(constant.name));
|
||||||
|
try out.write(" = ");
|
||||||
|
try renderApiConstantExpr(out, constant.expr);
|
||||||
|
try out.write(";\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renderDeclarations(out: var, registry: *Registry) !void {
|
||||||
|
var it = registry.declarations.iterator(0);
|
||||||
|
while (it.next()) |decl| {
|
||||||
|
if (decl.definition == .Command) continue; // handled seperately
|
||||||
|
|
||||||
|
switch (decl.definition) {
|
||||||
|
.Enum => |*info| try renderEnum(out, registry, decl.name, info),
|
||||||
|
else => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renderEnum(out: var, registry: *Registry, name: []const u8, enum_info: *reg.EnumInfo) !void {
|
||||||
|
if (enum_info.variants.count() == 0) {
|
||||||
|
return; // Skip empty declarations, which are unused bitflags
|
||||||
|
}
|
||||||
|
|
||||||
|
const trimmed_name = trimNamespace(name);
|
||||||
|
|
||||||
|
try out.write("const ");
|
||||||
|
try writeIdentifier(out, trimmed_name);
|
||||||
|
try out.write(" = extern enum {\n");
|
||||||
|
|
||||||
|
var prefix_len: usize = 0;
|
||||||
|
var snake_prefix_len: usize = 0;
|
||||||
|
var segment_it = mem.separate(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;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var it = enum_info.variants.iterator(0);
|
||||||
|
while (it.next()) |variant| {
|
||||||
|
if (variant.value == .Alias) continue; // Skip aliases
|
||||||
|
|
||||||
|
try out.write(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.write("};\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renderTest(out: var) !void {
|
||||||
|
try out.write(
|
||||||
|
\\test "Semantic analysis" {
|
||||||
|
\\ @import("std").meta.refAllDecls(@This());
|
||||||
|
\\}
|
||||||
|
\\
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -323,7 +323,6 @@ fn parseEqAttrValue(ctx: *ParseContext, alloc: *Allocator) ![]const u8 {
|
|||||||
fn parseNameNoDupe(ctx: *ParseContext) ![]const u8 {
|
fn parseNameNoDupe(ctx: *ParseContext) ![]const u8 {
|
||||||
// XML's spec on names is very long, so to make this easier
|
// XML's spec on names is very long, so to make this easier
|
||||||
// we just take any character that is not special and not whitespace
|
// we just take any character that is not special and not whitespace
|
||||||
|
|
||||||
const begin = ctx.offset;
|
const begin = ctx.offset;
|
||||||
|
|
||||||
while (ctx.peek()) |ch| {
|
while (ctx.peek()) |ch| {
|
||||||
|
|||||||
Reference in New Issue
Block a user