forked from mirror/vulkan-zig
DeclarationResolver
This commit is contained in:
174
generator/generator.zig
Normal file
174
generator/generator.zig
Normal file
@@ -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();
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user