625 lines
24 KiB
Zig
625 lines
24 KiB
Zig
const std = @import("std");
|
|
const Allocator = std.mem.Allocator;
|
|
const patterns = @import("patterns.zig");
|
|
const naming = @import("naming.zig");
|
|
const types = @import("types.zig");
|
|
|
|
const Declaration = patterns.Declaration;
|
|
const OpaqueType = patterns.OpaqueType;
|
|
const EnumDecl = patterns.EnumDecl;
|
|
const StructDecl = patterns.StructDecl;
|
|
const FlagDecl = patterns.FlagDecl;
|
|
|
|
pub const CodeGen = struct {
|
|
decls: []Declaration,
|
|
allocator: Allocator,
|
|
output: std.ArrayList(u8),
|
|
opaque_methods: std.StringHashMap(std.ArrayList(patterns.FunctionDecl)),
|
|
|
|
pub fn generate(allocator: Allocator, decls: []Declaration) ![]const u8 {
|
|
var gen = CodeGen{
|
|
.decls = decls,
|
|
.allocator = allocator,
|
|
.output = try std.ArrayList(u8).initCapacity(allocator, 4096),
|
|
.opaque_methods = std.StringHashMap(std.ArrayList(patterns.FunctionDecl)).init(allocator),
|
|
};
|
|
defer {
|
|
var it = gen.opaque_methods.valueIterator();
|
|
while (it.next()) |methods| {
|
|
methods.deinit(allocator);
|
|
}
|
|
gen.opaque_methods.deinit();
|
|
}
|
|
defer gen.output.deinit(allocator);
|
|
|
|
try gen.categorizeDeclarations();
|
|
try gen.writeHeader();
|
|
try gen.writeDeclarations();
|
|
|
|
return try gen.output.toOwnedSlice(allocator);
|
|
}
|
|
|
|
fn categorizeDeclarations(self: *CodeGen) !void {
|
|
// First, collect all opaque type names
|
|
var opaque_names = std.ArrayList([]const u8){};
|
|
defer opaque_names.deinit(self.allocator);
|
|
|
|
for (self.decls) |decl| {
|
|
if (decl == .opaque_type) {
|
|
const zig_name = naming.typeNameToZig(decl.opaque_type.name);
|
|
try opaque_names.append(self.allocator, zig_name);
|
|
// Initialize empty method list
|
|
try self.opaque_methods.put(zig_name, std.ArrayList(patterns.FunctionDecl){});
|
|
}
|
|
}
|
|
|
|
// Then, categorize functions
|
|
for (self.decls) |decl| {
|
|
if (decl == .function_decl) {
|
|
const func = decl.function_decl;
|
|
if (func.params.len > 0) {
|
|
// Check if first param is a pointer to an opaque type
|
|
const first_param_type = try types.convertType(func.params[0].type_name, self.allocator);
|
|
defer self.allocator.free(first_param_type);
|
|
|
|
// Check if it's ?*TypeName or *TypeName
|
|
for (opaque_names.items) |opaque_name| {
|
|
const opt_ptr = try std.fmt.allocPrint(self.allocator, "?*{s}", .{opaque_name});
|
|
defer self.allocator.free(opt_ptr);
|
|
const ptr = try std.fmt.allocPrint(self.allocator, "*{s}", .{opaque_name});
|
|
defer self.allocator.free(ptr);
|
|
|
|
if (std.mem.eql(u8, first_param_type, opt_ptr) or std.mem.eql(u8, first_param_type, ptr)) {
|
|
var methods = self.opaque_methods.getPtr(opaque_name).?;
|
|
try methods.append(self.allocator, func);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn writeHeader(self: *CodeGen) !void {
|
|
const header =
|
|
\\pub const c = @import("c.zig").c;
|
|
\\
|
|
\\
|
|
;
|
|
try self.output.appendSlice(self.allocator, header);
|
|
}
|
|
|
|
fn writeDeclarations(self: *CodeGen) !void {
|
|
// Generate each declaration
|
|
for (self.decls) |decl| {
|
|
switch (decl) {
|
|
.opaque_type => |opaque_decl| try self.writeOpaqueWithMethods(opaque_decl),
|
|
.typedef_decl => |typedef_decl| try self.writeTypedef(typedef_decl),
|
|
.enum_decl => |enum_decl| try self.writeEnum(enum_decl),
|
|
.struct_decl => |struct_decl| try self.writeStruct(struct_decl),
|
|
.flag_decl => |flag_decl| try self.writeFlags(flag_decl),
|
|
.function_decl => |func| {
|
|
// Only write standalone functions (not methods)
|
|
if (try self.isStandaloneFunction(func)) {
|
|
try self.writeFunction(func);
|
|
}
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
fn isStandaloneFunction(self: *CodeGen, func: patterns.FunctionDecl) !bool {
|
|
if (func.params.len == 0) return true;
|
|
|
|
const first_param_type = try types.convertType(func.params[0].type_name, self.allocator);
|
|
defer self.allocator.free(first_param_type);
|
|
|
|
var it = self.opaque_methods.keyIterator();
|
|
while (it.next()) |opaque_name| {
|
|
const opt_ptr = try std.fmt.allocPrint(self.allocator, "?*{s}", .{opaque_name.*});
|
|
defer self.allocator.free(opt_ptr);
|
|
const ptr = try std.fmt.allocPrint(self.allocator, "*{s}", .{opaque_name.*});
|
|
defer self.allocator.free(ptr);
|
|
|
|
if (std.mem.eql(u8, first_param_type, opt_ptr) or std.mem.eql(u8, first_param_type, ptr)) {
|
|
return false; // It's a method
|
|
}
|
|
}
|
|
|
|
return true; // It's standalone
|
|
}
|
|
|
|
fn writeOpaqueWithMethods(self: *CodeGen, opaque_type: OpaqueType) !void {
|
|
const zig_name = naming.typeNameToZig(opaque_type.name);
|
|
|
|
// Write doc comment if present
|
|
if (opaque_type.doc_comment) |doc| {
|
|
try self.writeDocComment(doc);
|
|
}
|
|
|
|
// Check if we have methods for this type
|
|
const methods = self.opaque_methods.get(zig_name);
|
|
|
|
if (methods) |method_list| {
|
|
if (method_list.items.len > 0) {
|
|
// pub const GPUDevice = opaque {
|
|
try self.output.writer(self.allocator).print("pub const {s} = opaque {{\n", .{zig_name});
|
|
|
|
// Write methods
|
|
for (method_list.items) |func| {
|
|
try self.writeFunctionAsMethod(func, zig_name);
|
|
}
|
|
|
|
try self.output.appendSlice(self.allocator, "};\n\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
// No methods, write as simple opaque
|
|
try self.output.writer(self.allocator).print("pub const {s} = opaque {{}};\n\n", .{zig_name});
|
|
}
|
|
|
|
fn writeTypedef(self: *CodeGen, typedef_decl: patterns.TypedefDecl) !void {
|
|
// Write doc comment if present
|
|
if (typedef_decl.doc_comment) |doc| {
|
|
try self.writeDocComment(doc);
|
|
}
|
|
|
|
const zig_name = naming.typeNameToZig(typedef_decl.name);
|
|
const zig_type = try types.convertType(typedef_decl.underlying_type, self.allocator);
|
|
defer self.allocator.free(zig_type);
|
|
|
|
try self.output.appendSlice(self.allocator, "pub const ");
|
|
try self.output.appendSlice(self.allocator, zig_name);
|
|
try self.output.appendSlice(self.allocator, " = ");
|
|
try self.output.appendSlice(self.allocator, zig_type);
|
|
try self.output.appendSlice(self.allocator, ";\n\n");
|
|
}
|
|
|
|
fn writeEnum(self: *CodeGen, enum_decl: EnumDecl) !void {
|
|
const zig_name = naming.typeNameToZig(enum_decl.name);
|
|
|
|
// Write doc comment if present
|
|
if (enum_decl.doc_comment) |doc| {
|
|
try self.writeDocComment(doc);
|
|
}
|
|
|
|
// pub const GPUPrimitiveType = enum(c_int) {
|
|
try self.output.writer(self.allocator).print("pub const {s} = enum(c_int) {{\n", .{zig_name});
|
|
|
|
// Detect common prefix
|
|
var value_names = try self.allocator.alloc([]const u8, enum_decl.values.len);
|
|
defer self.allocator.free(value_names);
|
|
for (enum_decl.values, 0..) |value, i| {
|
|
value_names[i] = value.name;
|
|
}
|
|
const prefix = try naming.detectCommonPrefix(value_names, self.allocator);
|
|
defer self.allocator.free(prefix);
|
|
|
|
// Write enum values
|
|
for (enum_decl.values) |value| {
|
|
const zig_value = try naming.enumValueToZig(value.name, prefix, self.allocator);
|
|
defer self.allocator.free(zig_value);
|
|
|
|
if (value.comment) |comment| {
|
|
try self.output.writer(self.allocator).print(" {s}, //{s}\n", .{ zig_value, comment });
|
|
} else {
|
|
try self.output.writer(self.allocator).print(" {s},\n", .{zig_value});
|
|
}
|
|
}
|
|
|
|
try self.output.appendSlice(self.allocator, "};\n\n");
|
|
}
|
|
|
|
fn writeStruct(self: *CodeGen, struct_decl: StructDecl) !void {
|
|
const zig_name = naming.typeNameToZig(struct_decl.name);
|
|
|
|
// Write doc comment if present
|
|
if (struct_decl.doc_comment) |doc| {
|
|
try self.writeDocComment(doc);
|
|
}
|
|
|
|
// pub const GPUViewport = extern struct {
|
|
try self.output.writer(self.allocator).print("pub const {s} = extern struct {{\n", .{zig_name});
|
|
|
|
// Write fields
|
|
for (struct_decl.fields) |field| {
|
|
const zig_type = try types.convertType(field.type_name, self.allocator);
|
|
defer self.allocator.free(zig_type);
|
|
|
|
if (field.comment) |comment| {
|
|
try self.output.writer(self.allocator).print(" {s}: {s}, // {s}\n", .{
|
|
field.name,
|
|
zig_type,
|
|
comment,
|
|
});
|
|
} else {
|
|
try self.output.writer(self.allocator).print(" {s}: {s},\n", .{ field.name, zig_type });
|
|
}
|
|
}
|
|
|
|
try self.output.appendSlice(self.allocator, "};\n\n");
|
|
}
|
|
|
|
fn writeFlags(self: *CodeGen, flag_decl: FlagDecl) !void {
|
|
const zig_name = naming.typeNameToZig(flag_decl.name);
|
|
|
|
// Write doc comment if present
|
|
if (flag_decl.doc_comment) |doc| {
|
|
try self.writeDocComment(doc);
|
|
}
|
|
|
|
// Determine underlying type size (u8, u16, u32, u64)
|
|
const underlying_type = if (std.mem.eql(u8, flag_decl.underlying_type, "Uint8"))
|
|
"u8"
|
|
else if (std.mem.eql(u8, flag_decl.underlying_type, "Uint16"))
|
|
"u16"
|
|
else if (std.mem.eql(u8, flag_decl.underlying_type, "Uint64"))
|
|
"u64"
|
|
else
|
|
"u32";
|
|
|
|
const type_bits: u32 = if (std.mem.eql(u8, underlying_type, "u8"))
|
|
8
|
|
else if (std.mem.eql(u8, underlying_type, "u16"))
|
|
16
|
|
else if (std.mem.eql(u8, underlying_type, "u64"))
|
|
64
|
|
else
|
|
32;
|
|
|
|
// pub const GPUTextureUsageFlags = packed struct(u32) {
|
|
try self.output.writer(self.allocator).print("pub const {s} = packed struct({s}) {{\n", .{
|
|
zig_name,
|
|
underlying_type,
|
|
});
|
|
|
|
// Detect common prefix
|
|
var flag_names = try self.allocator.alloc([]const u8, flag_decl.flags.len);
|
|
defer self.allocator.free(flag_names);
|
|
for (flag_decl.flags, 0..) |flag, i| {
|
|
flag_names[i] = flag.name;
|
|
}
|
|
const prefix = try naming.detectCommonPrefix(flag_names, self.allocator);
|
|
defer self.allocator.free(prefix);
|
|
|
|
// Track which bits are used
|
|
var used_bits = std.bit_set.IntegerBitSet(64).initEmpty();
|
|
|
|
// Write flag fields
|
|
for (flag_decl.flags) |flag| {
|
|
const zig_flag = try naming.flagNameToZig(flag.name, prefix, self.allocator);
|
|
defer self.allocator.free(zig_flag);
|
|
|
|
// Parse bit position from value like "(1u << 0)"
|
|
const bit_pos = try self.parseBitPosition(flag.value);
|
|
used_bits.set(bit_pos);
|
|
|
|
if (flag.comment) |comment| {
|
|
try self.output.writer(self.allocator).print(" {s}: bool = false, // {s}\n", .{
|
|
zig_flag,
|
|
comment,
|
|
});
|
|
} else {
|
|
try self.output.writer(self.allocator).print(" {s}: bool = false,\n", .{zig_flag});
|
|
}
|
|
}
|
|
|
|
// Calculate padding
|
|
const used_count = used_bits.count();
|
|
const padding_bits = type_bits - used_count - 1; // -1 for reserved bit
|
|
|
|
if (padding_bits > 0) {
|
|
try self.output.writer(self.allocator).print(" pad0: u{d} = 0,\n", .{padding_bits});
|
|
}
|
|
|
|
// Always add a reserved bit at the end
|
|
try self.output.appendSlice(self.allocator, " rsvd: bool = false,\n");
|
|
try self.output.appendSlice(self.allocator, "};\n\n");
|
|
}
|
|
|
|
fn writeFunctionAsMethod(self: *CodeGen, func: patterns.FunctionDecl, owner_type: []const u8) !void {
|
|
const zig_name = try naming.functionNameToZig(func.name, self.allocator);
|
|
defer self.allocator.free(zig_name);
|
|
|
|
// Write doc comment if present
|
|
if (func.doc_comment) |doc| {
|
|
try self.writeDocComment(doc);
|
|
}
|
|
|
|
// Convert return type
|
|
const zig_return_type = try types.convertType(func.return_type, self.allocator);
|
|
defer self.allocator.free(zig_return_type);
|
|
|
|
// pub inline fn createGPUDevice(
|
|
try self.output.writer(self.allocator).print(" pub inline fn {s}(", .{zig_name});
|
|
|
|
// Write parameters - first param is renamed to lowercase type name
|
|
for (func.params, 0..) |param, i| {
|
|
const zig_type = try types.convertType(param.type_name, self.allocator);
|
|
defer self.allocator.free(zig_type);
|
|
|
|
if (i > 0) {
|
|
try self.output.appendSlice(self.allocator, ", ");
|
|
}
|
|
|
|
if (i == 0) {
|
|
// First parameter: use lowercase owner type name and non-nullable pointer
|
|
const lower_name = try std.ascii.allocLowerString(self.allocator, owner_type);
|
|
defer self.allocator.free(lower_name);
|
|
// Remove ? from type if present
|
|
const non_nullable = if (std.mem.startsWith(u8, zig_type, "?*"))
|
|
zig_type[1..]
|
|
else
|
|
zig_type;
|
|
try self.output.writer(self.allocator).print("{s}: {s}", .{ lower_name, non_nullable });
|
|
} else if (param.name.len > 0) {
|
|
try self.output.writer(self.allocator).print("{s}: {s}", .{ param.name, zig_type });
|
|
} else {
|
|
try self.output.writer(self.allocator).print("{s}", .{zig_type});
|
|
}
|
|
}
|
|
|
|
// ) *GPUDevice {
|
|
// Add trailing comma for functions with more than 3 parameters (triggers multi-line formatting)
|
|
if (func.params.len > 3) {
|
|
try self.output.writer(self.allocator).print(",) {s} {{\n", .{zig_return_type});
|
|
} else {
|
|
try self.output.writer(self.allocator).print(") {s} {{\n", .{zig_return_type});
|
|
}
|
|
|
|
// Function body - call C API with appropriate casts
|
|
try self.output.appendSlice(self.allocator, " return ");
|
|
|
|
// Determine if we need a cast
|
|
const needs_cast = !std.mem.eql(u8, zig_return_type, "void");
|
|
const return_cast = if (needs_cast) types.getCastType(zig_return_type) else .none;
|
|
if (return_cast != .none) {
|
|
const cast_str = castTypeToString(return_cast);
|
|
try self.output.writer(self.allocator).print("{s}(", .{cast_str});
|
|
}
|
|
|
|
// c.SDL_FunctionName(
|
|
try self.output.writer(self.allocator).print("c.{s}(", .{func.name});
|
|
|
|
// Pass parameters with casts
|
|
for (func.params, 0..) |param, i| {
|
|
if (i > 0) {
|
|
try self.output.appendSlice(self.allocator, ", ");
|
|
}
|
|
|
|
if (param.name.len > 0 or i == 0) {
|
|
const param_name = if (i == 0) blk: {
|
|
const lower = try std.ascii.allocLowerString(self.allocator, owner_type);
|
|
defer self.allocator.free(lower);
|
|
break :blk try self.allocator.dupe(u8, lower);
|
|
} else try self.allocator.dupe(u8, param.name);
|
|
defer self.allocator.free(param_name);
|
|
|
|
const zig_param_type = try types.convertType(param.type_name, self.allocator);
|
|
defer self.allocator.free(zig_param_type);
|
|
|
|
const param_cast = types.getCastType(zig_param_type);
|
|
|
|
if (param_cast == .none) {
|
|
try self.output.writer(self.allocator).print("{s}", .{param_name});
|
|
} else {
|
|
const cast_str = castTypeToString(param_cast);
|
|
try self.output.writer(self.allocator).print("{s}({s})", .{ cast_str, param_name });
|
|
}
|
|
}
|
|
}
|
|
|
|
// Close the call
|
|
if (return_cast != .none) {
|
|
try self.output.appendSlice(self.allocator, "));\n");
|
|
} else {
|
|
try self.output.appendSlice(self.allocator, ");\n");
|
|
}
|
|
|
|
try self.output.appendSlice(self.allocator, " }\n\n");
|
|
}
|
|
|
|
fn writeFunction(self: *CodeGen, func: patterns.FunctionDecl) !void {
|
|
const zig_name = try naming.functionNameToZig(func.name, self.allocator);
|
|
defer self.allocator.free(zig_name);
|
|
|
|
// Write doc comment if present
|
|
if (func.doc_comment) |doc| {
|
|
try self.writeDocComment(doc);
|
|
}
|
|
|
|
// Convert return type
|
|
const zig_return_type = try types.convertType(func.return_type, self.allocator);
|
|
defer self.allocator.free(zig_return_type);
|
|
|
|
// pub inline fn createGPUDevice(
|
|
try self.output.writer(self.allocator).print("pub inline fn {s}(", .{zig_name});
|
|
|
|
// Write parameters
|
|
for (func.params, 0..) |param, i| {
|
|
const zig_type = try types.convertType(param.type_name, self.allocator);
|
|
defer self.allocator.free(zig_type);
|
|
|
|
if (i > 0) {
|
|
try self.output.appendSlice(self.allocator, ", ");
|
|
}
|
|
|
|
if (param.name.len > 0) {
|
|
try self.output.writer(self.allocator).print("{s}: {s}", .{ param.name, zig_type });
|
|
} else {
|
|
// Parameter has no name (like void or unnamed param)
|
|
try self.output.writer(self.allocator).print("{s}", .{zig_type});
|
|
}
|
|
}
|
|
|
|
// ) *GPUDevice {
|
|
// Add trailing comma for functions with more than 3 parameters (triggers multi-line formatting)
|
|
if (func.params.len > 3) {
|
|
try self.output.writer(self.allocator).print(",) {s} {{\n", .{zig_return_type});
|
|
} else {
|
|
try self.output.writer(self.allocator).print(") {s} {{\n", .{zig_return_type});
|
|
}
|
|
|
|
// Function body - call C API with appropriate casts
|
|
try self.output.appendSlice(self.allocator, " return ");
|
|
|
|
// Determine if we need a cast
|
|
const needs_cast = !std.mem.eql(u8, zig_return_type, "void");
|
|
const return_cast = if (needs_cast) types.getCastType(zig_return_type) else .none;
|
|
if (return_cast != .none) {
|
|
const cast_str = castTypeToString(return_cast);
|
|
try self.output.writer(self.allocator).print("{s}(", .{cast_str});
|
|
}
|
|
|
|
// c.SDL_FunctionName(
|
|
try self.output.writer(self.allocator).print("c.{s}(", .{func.name});
|
|
|
|
// Pass parameters with casts
|
|
for (func.params, 0..) |param, i| {
|
|
if (i > 0) {
|
|
try self.output.appendSlice(self.allocator, ", ");
|
|
}
|
|
|
|
if (param.name.len > 0) {
|
|
const zig_param_type = try types.convertType(param.type_name, self.allocator);
|
|
defer self.allocator.free(zig_param_type);
|
|
|
|
const param_cast = types.getCastType(zig_param_type);
|
|
|
|
if (param_cast == .none) {
|
|
try self.output.writer(self.allocator).print("{s}", .{param.name});
|
|
} else {
|
|
const cast_str = castTypeToString(param_cast);
|
|
try self.output.writer(self.allocator).print("{s}({s})", .{ cast_str, param.name });
|
|
}
|
|
}
|
|
}
|
|
|
|
// Close the call
|
|
if (return_cast != .none) {
|
|
try self.output.appendSlice(self.allocator, "));\n");
|
|
} else {
|
|
try self.output.appendSlice(self.allocator, ");\n");
|
|
}
|
|
|
|
try self.output.appendSlice(self.allocator, "}\n\n");
|
|
}
|
|
|
|
fn castTypeToString(cast_type: types.CastType) []const u8 {
|
|
return switch (cast_type) {
|
|
.none => "none",
|
|
.ptr_cast => "@ptrCast",
|
|
.bit_cast => "@bitCast",
|
|
.int_from_enum => "@intFromEnum",
|
|
.enum_from_int => "@enumFromInt",
|
|
};
|
|
}
|
|
|
|
fn writeDocComment(self: *CodeGen, comment: []const u8) !void {
|
|
// For now, just skip doc comments
|
|
// TODO: Parse and format doc comments properly
|
|
_ = self;
|
|
_ = comment;
|
|
}
|
|
|
|
fn parseBitPosition(self: *CodeGen, value: []const u8) !u6 {
|
|
_ = self;
|
|
// Parse expressions like "(1u << 0)" or "0x01" or "SDL_UINT64_C(0x...)"
|
|
var trimmed = std.mem.trim(u8, value, " \t()");
|
|
|
|
// Handle SDL_UINT64_C(0x...) pattern
|
|
if (std.mem.startsWith(u8, trimmed, "SDL_UINT64_C(")) {
|
|
const inner_start = "SDL_UINT64_C(".len;
|
|
trimmed = std.mem.trim(u8, trimmed[inner_start..], " \t)");
|
|
}
|
|
|
|
// Look for bit shift pattern: "1u << N"
|
|
if (std.mem.indexOf(u8, trimmed, "<<")) |shift_pos| {
|
|
const after_shift = std.mem.trim(u8, trimmed[shift_pos + 2 ..], " \t)");
|
|
const bit = try std.fmt.parseInt(u6, after_shift, 10);
|
|
return bit;
|
|
}
|
|
|
|
// Hex value like "0x01" or "0x0000000000000001"
|
|
if (std.mem.startsWith(u8, trimmed, "0x")) {
|
|
const hex_str = trimmed[2..];
|
|
const val = try std.fmt.parseInt(u64, hex_str, 16);
|
|
// Find the bit position (count trailing zeros)
|
|
var bit: u6 = 0;
|
|
while (bit < 64) : (bit += 1) {
|
|
if (val == (@as(u64, 1) << @as(u6, bit))) return bit;
|
|
}
|
|
}
|
|
|
|
return error.InvalidBitPosition;
|
|
}
|
|
};
|
|
|
|
test "generate opaque type" {
|
|
const opaque_type = OpaqueType{
|
|
.name = "SDL_GPUDevice",
|
|
.doc_comment = null,
|
|
};
|
|
|
|
var decls = [_]Declaration{.{ .opaque_type = opaque_type }};
|
|
|
|
const output = try CodeGen.generate(std.testing.allocator, decls[0..]);
|
|
defer std.testing.allocator.free(output);
|
|
|
|
const expected =
|
|
\\pub const c = @import("c.zig").c;
|
|
\\
|
|
\\pub const GPUDevice = opaque {};
|
|
\\
|
|
\\
|
|
;
|
|
|
|
try std.testing.expectEqualStrings(expected, output);
|
|
}
|
|
|
|
test "generate enum" {
|
|
var values = [_]patterns.EnumValue{
|
|
.{
|
|
.name = "SDL_GPU_PRIMITIVETYPE_TRIANGLELIST",
|
|
.value = null,
|
|
.comment = " A series of triangles",
|
|
},
|
|
.{
|
|
.name = "SDL_GPU_PRIMITIVETYPE_LINELIST",
|
|
.value = null,
|
|
.comment = " A series of lines",
|
|
},
|
|
};
|
|
|
|
const enum_decl = EnumDecl{
|
|
.name = "SDL_GPUPrimitiveType",
|
|
.values = values[0..],
|
|
.doc_comment = null,
|
|
};
|
|
|
|
var decls = [_]Declaration{.{ .enum_decl = enum_decl }};
|
|
|
|
const output = try CodeGen.generate(std.testing.allocator, decls[0..]);
|
|
defer std.testing.allocator.free(output);
|
|
|
|
// Verify it contains the expected elements
|
|
try std.testing.expect(std.mem.indexOf(u8, output, "pub const GPUPrimitiveType = enum(c_int)") != null);
|
|
try std.testing.expect(std.mem.indexOf(u8, output, "trianglelist") != null);
|
|
try std.testing.expect(std.mem.indexOf(u8, output, "linelist") != null);
|
|
}
|
|
|
|
test "parse bit position" {
|
|
var gen = CodeGen{
|
|
.decls = &[_]Declaration{},
|
|
.allocator = std.testing.allocator,
|
|
.output = try std.ArrayList(u8).initCapacity(std.testing.allocator, 1),
|
|
};
|
|
defer gen.output.deinit(std.testing.allocator);
|
|
|
|
try std.testing.expectEqual(@as(u6, 0), try gen.parseBitPosition("(1u << 0)"));
|
|
try std.testing.expectEqual(@as(u6, 5), try gen.parseBitPosition("1u << 5"));
|
|
try std.testing.expectEqual(@as(u6, 0), try gen.parseBitPosition("0x01"));
|
|
try std.testing.expectEqual(@as(u6, 3), try gen.parseBitPosition("0x08"));
|
|
}
|