forked from mirror/vulkan-zig
Rendering setup
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const reg = @import("registry.zig");
|
const reg = @import("registry.zig");
|
||||||
const xml = @import("xml.zig");
|
const xml = @import("xml.zig");
|
||||||
|
const renderRegistry = @import("render.zig").render;
|
||||||
const parseXml = @import("registry/parse.zig").parseXml;
|
const parseXml = @import("registry/parse.zig").parseXml;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const FeatureLevel = reg.FeatureLevel;
|
const FeatureLevel = reg.FeatureLevel;
|
||||||
@@ -167,10 +168,14 @@ pub const Generator = struct {
|
|||||||
self.registry_arena.deinit();
|
self.registry_arena.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Solve registry.declarations according to registry.extensions and registry.features
|
// Solve `registry.declarations` according to `registry.extensions` and `registry.features`.
|
||||||
pub fn resolveDeclarations(self: *Generator) !void {
|
pub fn resolveDeclarations(self: *Generator) !void {
|
||||||
var resolver = DeclarationResolver.init(self.gpa, &self.registry_arena.allocator, &self.registry);
|
var resolver = DeclarationResolver.init(self.gpa, &self.registry_arena.allocator, &self.registry);
|
||||||
defer resolver.deinit();
|
defer resolver.deinit();
|
||||||
try resolver.resolve();
|
try resolver.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn render(self: *Generator, out_stream: var) !void {
|
||||||
|
try renderRegistry(out_stream, self.gpa, &self.registry);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -122,16 +122,14 @@ pub fn main() !void {
|
|||||||
const spec = try xml.parse(allocator, source);
|
const spec = try xml.parse(allocator, source);
|
||||||
defer spec.deinit();
|
defer spec.deinit();
|
||||||
|
|
||||||
// const result = try parseXml(std.heap.page_allocator, spec.root);
|
|
||||||
// defer result.deinit();
|
|
||||||
|
|
||||||
// dumpRegistry(result.registry);
|
|
||||||
|
|
||||||
var gen = try vkgen.Generator.init(allocator, spec.root);
|
var gen = try vkgen.Generator.init(allocator, spec.root);
|
||||||
defer gen.deinit();
|
defer gen.deinit();
|
||||||
|
|
||||||
try gen.resolveDeclarations();
|
try gen.resolveDeclarations();
|
||||||
|
|
||||||
|
const stdout = std.io.getStdOut().writer();
|
||||||
|
try gen.render(stdout);
|
||||||
|
|
||||||
std.debug.warn("Total declarations: {}\n", .{gen.registry.decls.len});
|
std.debug.warn("Total declarations: {}\n", .{gen.registry.decls.len});
|
||||||
std.debug.warn("Total memory usage: {} KiB\n", .{@divTrunc(prof_alloc.max_usage, 1024)});
|
std.debug.warn("Total memory usage: {} KiB\n", .{@divTrunc(prof_alloc.max_usage, 1024)});
|
||||||
}
|
}
|
||||||
@@ -140,3 +138,7 @@ test "main" {
|
|||||||
_ = @import("xml.zig");
|
_ = @import("xml.zig");
|
||||||
_ = @import("registry/c-parse.zig");
|
_ = @import("registry/c-parse.zig");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Fix not all enums being parsed.
|
||||||
|
// TODO: Fix not all struct fields being marked as optional properly.
|
||||||
|
// TODO: Sort enum fields.
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ pub const DeclarationType = union(enum) {
|
|||||||
alias: Alias,
|
alias: Alias,
|
||||||
foreign: Foreign,
|
foreign: Foreign,
|
||||||
typedef: TypeInfo,
|
typedef: TypeInfo,
|
||||||
|
opaque,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Alias = struct {
|
pub const Alias = struct {
|
||||||
@@ -52,7 +53,6 @@ pub const TypeInfo = union(enum) {
|
|||||||
command_ptr: Command,
|
command_ptr: Command,
|
||||||
pointer: Pointer,
|
pointer: Pointer,
|
||||||
array: Array,
|
array: Array,
|
||||||
opaque,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Container = struct {
|
pub const Container = struct {
|
||||||
@@ -72,7 +72,7 @@ pub const Enum = struct {
|
|||||||
bit_vector: i32, // Combined flags & some vendor IDs
|
bit_vector: i32, // Combined flags & some vendor IDs
|
||||||
int: i32,
|
int: i32,
|
||||||
alias: struct {
|
alias: struct {
|
||||||
alias_name: []const u8,
|
name: []const u8,
|
||||||
is_compat_alias: bool,
|
is_compat_alias: bool,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ fn parseBaseType(allocator: *Allocator, ty: *xml.Element) !registry.Declaration
|
|||||||
// macros, which is why this part is not built into the xml/c parser.
|
// macros, which is why this part is not built into the xml/c parser.
|
||||||
return registry.Declaration{
|
return registry.Declaration{
|
||||||
.name = name,
|
.name = name,
|
||||||
.decl_type = .{.typedef = .{.opaque = {}}},
|
.decl_type = .{.opaque = {}},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -302,7 +302,7 @@ fn parseEnumField(field: *xml.Element) !registry.Enum.Field {
|
|||||||
} else if (field.getAttribute("bitpos")) |bitpos| {
|
} else if (field.getAttribute("bitpos")) |bitpos| {
|
||||||
break :blk .{.bitpos = try std.fmt.parseInt(u5, bitpos, 10)};
|
break :blk .{.bitpos = try std.fmt.parseInt(u5, bitpos, 10)};
|
||||||
} else if (field.getAttribute("alias")) |alias| {
|
} else if (field.getAttribute("alias")) |alias| {
|
||||||
break :blk .{.alias = .{.alias_name = alias, .is_compat_alias = is_compat_alias}};
|
break :blk .{.alias = .{.name = alias, .is_compat_alias = is_compat_alias}};
|
||||||
} else {
|
} else {
|
||||||
return error.InvalidRegistry;
|
return error.InvalidRegistry;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,298 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const registry = @import("registry.zig");
|
const reg = @import("registry.zig");
|
||||||
const Allocator = std.mem.Allocator,
|
const util = @import("render/util.zig");
|
||||||
|
const mem = std.mem;
|
||||||
|
const Allocator = mem.Allocator;
|
||||||
|
|
||||||
|
const preamble =
|
||||||
|
\\const std = @import("std");
|
||||||
|
\\
|
||||||
|
;
|
||||||
|
|
||||||
|
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)},
|
||||||
|
};
|
||||||
|
|
||||||
|
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 Renderer(comptime WriterType: type) type {
|
||||||
|
return struct {
|
||||||
|
const Self = @This();
|
||||||
|
const WriteError = WriterType.Error;
|
||||||
|
const RenderTypeInfoError = WriteError || error {
|
||||||
|
OutOfMemory,
|
||||||
|
};
|
||||||
|
|
||||||
|
writer: WriterType,
|
||||||
|
allocator: *Allocator,
|
||||||
|
registry: *const reg.Registry,
|
||||||
|
id_renderer: util.IdRenderer,
|
||||||
|
|
||||||
|
fn init(writer: WriterType, allocator: *Allocator, registry: *const reg.Registry) Self {
|
||||||
|
return .{
|
||||||
|
.writer = writer,
|
||||||
|
.allocator = allocator,
|
||||||
|
.registry = registry,
|
||||||
|
.id_renderer = util.IdRenderer.init(allocator, registry.tags),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deinit(self: Self) void {
|
||||||
|
self.id_renderer.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn writeIdentifier(self: Self, id: []const u8) !void {
|
||||||
|
try util.writeIdentifier(self.writer, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn writeIdentifierWithCase(self: *Self, case: util.CaseStyle, id: []const u8) !void {
|
||||||
|
try self.id_renderer.render(self.writer, case, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extractEnumFieldName(self: Self, enum_name: []const u8, field_name: []const u8) ![]const u8 {
|
||||||
|
const tag = util.getAuthorTag(enum_name, self.registry.tags);
|
||||||
|
const adjusted_enum_name = if (tag) |name|
|
||||||
|
enum_name[0 .. enum_name.len - name.len]
|
||||||
|
else
|
||||||
|
enum_name;
|
||||||
|
|
||||||
|
var enum_it = util.SegmentIterator.init(adjusted_enum_name);
|
||||||
|
var field_it = util.SegmentIterator.init(field_name);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const rest = field_it.rest();
|
||||||
|
const enum_segment = enum_it.next() orelse return rest;
|
||||||
|
const field_segment = field_it.next() orelse return error.FieldNameEqualsEnumName;
|
||||||
|
|
||||||
|
if (!eqlIgnoreCase(enum_segment, field_segment)) {
|
||||||
|
return rest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(self: *Self) !void {
|
||||||
|
try self.writer.writeAll(preamble);
|
||||||
|
|
||||||
|
for (self.registry.decls) |decl| {
|
||||||
|
try self.renderDecl(decl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renderTypeInfo(self: *Self, type_info: reg.TypeInfo) RenderTypeInfoError!void {
|
||||||
|
switch (type_info) {
|
||||||
|
.name => |name| try self.renderTypeName(name),
|
||||||
|
.command_ptr => |command_ptr| try self.renderCommandPtr(command_ptr),
|
||||||
|
.pointer => |pointer| try self.renderPointer(pointer),
|
||||||
|
.array => |array| try self.renderArray(array),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renderTypeName(self: *Self, name: []const u8) !void {
|
||||||
|
for (builtin_types) |builtin_type| {
|
||||||
|
if (mem.eql(u8, name, builtin_type.c_name)) {
|
||||||
|
try self.writer.writeAll(builtin_type.zig_name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Handle foreign types
|
||||||
|
|
||||||
|
if (mem.startsWith(u8, name, "vk")) {
|
||||||
|
// Function type, always render with the exact same text for linking purposes.
|
||||||
|
try self.writeIdentifier(name);
|
||||||
|
return;
|
||||||
|
} else if (mem.startsWith(u8, name, "Vk")) {
|
||||||
|
// Type, strip namespace and write, as they are alreay in title case.
|
||||||
|
try self.writeIdentifier(name[2..]);
|
||||||
|
return;
|
||||||
|
} else if (mem.startsWith(u8, name, "PFN_vk")) {
|
||||||
|
// Function pointer type, render using same name for now
|
||||||
|
try self.writeIdentifier(name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try self.writeIdentifier(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renderCommandPtr(self: *Self, command_ptr: reg.Command) !void {
|
||||||
|
try self.writer.writeAll("?fn(");
|
||||||
|
for (command_ptr.params) |param| {
|
||||||
|
try self.writeIdentifierWithCase(.snake, param.name);
|
||||||
|
try self.writer.writeAll(": ");
|
||||||
|
try self.renderTypeInfo(param.param_type);
|
||||||
|
try self.writer.writeAll(", ");
|
||||||
|
}
|
||||||
|
try self.writer.writeAll(") ");
|
||||||
|
try self.renderTypeInfo(command_ptr.return_type.*);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renderPointer(self: *Self, pointer: reg.Pointer) !void {
|
||||||
|
if (pointer.is_optional) {
|
||||||
|
try self.writer.writeByte('?');
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (pointer.size) {
|
||||||
|
.one => try self.writer.writeByte('*'),
|
||||||
|
.many => try self.writer.writeAll("[*]"),
|
||||||
|
.zero_terminated => try self.writer.writeAll("[*:0]"),
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pointer.is_const) {
|
||||||
|
try self.writer.writeAll("const ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special case: void pointers
|
||||||
|
if (pointer.child.* == .name and mem.eql(u8, pointer.child.name, "void")) {
|
||||||
|
try self.writer.writeAll("c_void");
|
||||||
|
} else {
|
||||||
|
try self.renderTypeInfo(pointer.child.*);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renderArray(self: *Self, array: reg.Array) !void {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
try self.writer.writeByte(']');
|
||||||
|
try self.renderTypeInfo(array.child.*);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renderDecl(self: *Self, decl: reg.Declaration) !void {
|
||||||
|
switch (decl.decl_type) {
|
||||||
|
.container => |container| try self.renderContainer(decl.name, container),
|
||||||
|
.enumeration => |enumeration| try self.renderEnumeration(decl.name, enumeration),
|
||||||
|
.alias => |alias| try self.renderAlias(decl.name, alias),
|
||||||
|
.opaque => try self.renderOpaque(decl.name),
|
||||||
|
.typedef => |type_info| try self.renderTypedef(decl.name, type_info),
|
||||||
|
else => {}, // unhandled for now
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renderContainer(self: *Self, name: []const u8, container: reg.Container) !void {
|
||||||
|
try self.writer.writeAll("const ");
|
||||||
|
try self.renderTypeName(name);
|
||||||
|
try self.writer.writeAll(" = ");
|
||||||
|
|
||||||
|
for (container.fields) |field| {
|
||||||
|
if (field.bits != null) {
|
||||||
|
try self.writer.writeAll("packed ");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try self.writer.writeAll("extern ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (container.is_union) {
|
||||||
|
try self.writer.writeAll("union {");
|
||||||
|
} else {
|
||||||
|
try self.writer.writeAll("struct {");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (container.fields) |field| {
|
||||||
|
try self.writeIdentifierWithCase(.snake, field.name);
|
||||||
|
try self.writer.writeAll(": ");
|
||||||
|
try self.renderTypeInfo(field.field_type);
|
||||||
|
// TODO: Generate struct defaults
|
||||||
|
// TODO: Deal with packed structs
|
||||||
|
try self.writer.writeAll(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
try self.writer.writeAll("};\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renderEnumeration(self: *Self, name: []const u8, enumeration: reg.Enum) !void {
|
||||||
|
// TODO: Handle bitmasks
|
||||||
|
try self.writer.writeAll("const ");
|
||||||
|
try self.renderTypeName(name);
|
||||||
|
try self.writer.writeAll(" = extern enum {");
|
||||||
|
|
||||||
|
for (enumeration.fields) |field| {
|
||||||
|
if (field.value == .alias) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try self.writeIdentifierWithCase(.snake, try self.extractEnumFieldName(name, field.name));
|
||||||
|
|
||||||
|
switch (field.value) {
|
||||||
|
.int => |int| try self.writer.print("= {}, ", .{int}),
|
||||||
|
.bitpos => |pos| try self.writer.print(" = 1 << {}, ", .{pos}),
|
||||||
|
.bit_vector => |value| try self.writer.print(" = 0x{X}, ", .{value}),
|
||||||
|
.alias => unreachable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (enumeration.fields) |field| {
|
||||||
|
if (field.value != .alias or field.value.alias.is_compat_alias) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try self.writer.writeAll("pub const ");
|
||||||
|
try self.writeIdentifierWithCase(.snake, try self.extractEnumFieldName(name, field.name));
|
||||||
|
try self.writer.writeAll(" = ");
|
||||||
|
try self.writeIdentifierWithCase(.snake, try self.extractEnumFieldName(name, field.value.alias.name));
|
||||||
|
try self.writer.writeAll(";");
|
||||||
|
}
|
||||||
|
|
||||||
|
try self.writer.writeAll("};\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renderAlias(self: *Self, name: []const u8, alias: reg.Alias) !void {
|
||||||
|
try self.writer.writeAll("const ");
|
||||||
|
try self.renderTypeName(name);
|
||||||
|
try self.writer.writeAll(" = ");
|
||||||
|
try self.renderTypeName(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.writer.writeAll(" = @Type(.Opaque);\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renderTypedef(self: *Self, name: []const u8, type_info: reg.TypeInfo) !void {
|
||||||
|
try self.writer.writeAll("const ");
|
||||||
|
try self.renderTypeName(name);
|
||||||
|
try self.writer.writeAll(" = ");
|
||||||
|
try self.renderTypeInfo(type_info);
|
||||||
|
try self.writer.writeAll(";\n");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(writer: var, allocator: *Allocator, registry: *const reg.Registry) !void {
|
||||||
|
var renderer = Renderer(@TypeOf(writer)).init(writer, allocator, registry);
|
||||||
|
defer renderer.deinit();
|
||||||
|
|
||||||
|
try renderer.render();
|
||||||
|
}
|
||||||
|
|||||||
232
generator/render/util.zig
Normal file
232
generator/render/util.zig
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const reg = @import("../registry.zig");
|
||||||
|
const mem = std.mem;
|
||||||
|
const Allocator = mem.Allocator;
|
||||||
|
|
||||||
|
// Lifted from src-self-hosted/translate_c.zig
|
||||||
|
pub 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
|
||||||
|
pub 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
const reserved_names = [_][]const u8 {
|
||||||
|
"void", "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;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn needZigEscape(name: []const u8) bool {
|
||||||
|
return !isValidZigIdentifier(name)
|
||||||
|
or isZigReservedIdentifier(name)
|
||||||
|
or std.zig.Token.getKeyword(name) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn writeIdentifier(out: var, id: []const u8) !void {
|
||||||
|
if (needZigEscape(id)) {
|
||||||
|
try out.print("@\"{}\"", .{id});
|
||||||
|
} else {
|
||||||
|
try out.writeAll(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const CaseStyle = enum {
|
||||||
|
snake,
|
||||||
|
screaming_snake,
|
||||||
|
title,
|
||||||
|
camel,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn trimVkNamespace(id: []const u8) []const u8 {
|
||||||
|
const prefixes = [_][]const u8{"VK_", "vk", "Vk", "PFN_vk"};
|
||||||
|
for (prefixes) |prefix| {
|
||||||
|
if (mem.startsWith(u8, id, prefix)) {
|
||||||
|
return id[prefix.len..];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getAuthorTag(id: []const u8, tags: []const reg.Tag) ?[]const u8 {
|
||||||
|
for (tags) |tag| {
|
||||||
|
if (mem.endsWith(u8, id, tag.name)) {
|
||||||
|
return tag.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const SegmentIterator = struct {
|
||||||
|
text: []const u8,
|
||||||
|
offset: usize,
|
||||||
|
|
||||||
|
pub fn init(text: []const u8) SegmentIterator {
|
||||||
|
return .{
|
||||||
|
.text = text,
|
||||||
|
.offset = 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nextBoundary(self: SegmentIterator) usize {
|
||||||
|
var i = self.offset + 1;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (i == self.text.len or self.text[i] == '_') {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
const prev_lower = std.ascii.isLower(self.text[i - 1]);
|
||||||
|
const next_lower = std.ascii.isLower(self.text[i]);
|
||||||
|
|
||||||
|
if (prev_lower and !next_lower) {
|
||||||
|
return i;
|
||||||
|
} else if (i != self.offset + 1 and !prev_lower and next_lower) {
|
||||||
|
return i - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next(self: *SegmentIterator) ?[]const u8 {
|
||||||
|
while (self.offset < self.text.len and self.text[self.offset] == '_') {
|
||||||
|
self.offset += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.offset == self.text.len) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const end = self.nextBoundary();
|
||||||
|
const word = self.text[self.offset .. end];
|
||||||
|
self.offset = end;
|
||||||
|
return word;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rest(self: SegmentIterator) []const u8 {
|
||||||
|
if (self.offset >= self.text.len) {
|
||||||
|
return &[_]u8{};
|
||||||
|
} else {
|
||||||
|
return self.text[self.offset..];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const IdRenderer = struct {
|
||||||
|
tags: []const reg.Tag,
|
||||||
|
text_cache: std.ArrayList(u8),
|
||||||
|
|
||||||
|
pub fn init(allocator: *Allocator, tags: []const reg.Tag) IdRenderer {
|
||||||
|
return .{
|
||||||
|
.tags = tags,
|
||||||
|
.text_cache = std.ArrayList(u8).init(allocator),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: IdRenderer) void {
|
||||||
|
self.text_cache.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renderSnake(self: *IdRenderer, screaming: bool, id: []const u8, tag: ?[]const u8) !void {
|
||||||
|
var it = SegmentIterator.init(id);
|
||||||
|
var first = true;
|
||||||
|
const transform = if (screaming) std.ascii.toUpper else std.ascii.toLower;
|
||||||
|
|
||||||
|
while (it.next()) |segment| {
|
||||||
|
if (first) {
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
try self.text_cache.append('_');
|
||||||
|
}
|
||||||
|
|
||||||
|
for (segment) |c| {
|
||||||
|
try self.text_cache.append(transform(c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tag) |name| {
|
||||||
|
try self.text_cache.append('_');
|
||||||
|
|
||||||
|
for (name) |c| {
|
||||||
|
try self.text_cache.append(transform(c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renderCamel(self: *IdRenderer, title: bool, id: []const u8, tag: ?[]const u8) !void {
|
||||||
|
var it = SegmentIterator.init(id);
|
||||||
|
var lower_first = !title;
|
||||||
|
|
||||||
|
while (it.next()) |segment| {
|
||||||
|
var i: usize = 0;
|
||||||
|
while (i < segment.len and std.ascii.isDigit(segment[i])) {
|
||||||
|
try self.text_cache.append(segment[i]);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == segment.len) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 0 and lower_first) {
|
||||||
|
try self.text_cache.append(std.ascii.toLower(segment[i]));
|
||||||
|
} else {
|
||||||
|
try self.text_cache.append(std.ascii.toUpper(segment[i]));
|
||||||
|
}
|
||||||
|
lower_first = false;
|
||||||
|
|
||||||
|
for (segment[i + 1..]) |c| {
|
||||||
|
try self.text_cache.append(std.ascii.toLower(c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tag) |name| {
|
||||||
|
try self.text_cache.appendSlice(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(self: *IdRenderer, out: var, case_style: CaseStyle, id: []const u8) !void {
|
||||||
|
const tag = getAuthorTag(id, self.tags);
|
||||||
|
const adjusted_id = if (tag) |name| id[0 .. id.len - name.len] else id;
|
||||||
|
self.text_cache.items.len = 0;
|
||||||
|
|
||||||
|
switch (case_style) {
|
||||||
|
.snake => try self.renderSnake(false, adjusted_id, tag),
|
||||||
|
.screaming_snake => try self.renderSnake(true, adjusted_id, tag),
|
||||||
|
.title => try self.renderCamel(true, adjusted_id, tag),
|
||||||
|
.camel => try self.renderCamel(false, adjusted_id, tag),
|
||||||
|
}
|
||||||
|
|
||||||
|
try writeIdentifier(out, self.text_cache.items);
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user