mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-28 14:43:28 +00:00
Add generate Union function
Signed-off-by: Francis Bouvier <francis.bouvier@gmail.com>
This commit is contained in:
203
src/generate.zig
Normal file
203
src/generate.zig
Normal file
@@ -0,0 +1,203 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
fn fmtName(comptime T: type) []const u8 {
|
||||
var it = std.mem.splitBackwards(u8, @typeName(T), ".");
|
||||
return it.first();
|
||||
}
|
||||
|
||||
pub const Union = struct {
|
||||
_enum: type,
|
||||
_union: type,
|
||||
|
||||
pub fn compile(comptime tuple: anytype) Union {
|
||||
comptime {
|
||||
return private_compile(tuple) catch @compileError("CompileUnion error");
|
||||
}
|
||||
}
|
||||
|
||||
fn private_compile(comptime tuple: anytype) !Union {
|
||||
@setEvalBranchQuota(10000);
|
||||
|
||||
// check types provided
|
||||
const tuple_T = @TypeOf(tuple);
|
||||
const tuple_info = @typeInfo(tuple_T);
|
||||
if (tuple_info != .Struct or !tuple_info.Struct.is_tuple) {
|
||||
return error.GenerateUnionArgNotTuple;
|
||||
}
|
||||
|
||||
const tuple_members = tuple_info.Struct.fields;
|
||||
|
||||
// first iteration to get the total number of members
|
||||
comptime var members_nb = 0;
|
||||
inline for (tuple_members) |member| {
|
||||
const member_T = @field(tuple, member.name);
|
||||
const member_info = @typeInfo(member_T);
|
||||
if (member_info == .Union) {
|
||||
const member_union = member_info.Union;
|
||||
members_nb += member_union.fields.len;
|
||||
} else if (member_info == .Struct) {
|
||||
members_nb += 1;
|
||||
} else {
|
||||
return error.GenerateUnionMemberNotUnionOrStruct;
|
||||
}
|
||||
}
|
||||
|
||||
// define the tag type regarding the members nb
|
||||
comptime var tag_type: type = undefined;
|
||||
if (members_nb < 3) {
|
||||
tag_type = u1;
|
||||
} else if (members_nb < 4) {
|
||||
tag_type = u2;
|
||||
} else if (members_nb < 8) {
|
||||
tag_type = u3;
|
||||
} else if (members_nb < 16) {
|
||||
tag_type = u4;
|
||||
} else if (members_nb < 32) {
|
||||
tag_type = u4;
|
||||
} else if (members_nb < 64) {
|
||||
tag_type = u6;
|
||||
} else if (members_nb < 128) {
|
||||
tag_type = u7;
|
||||
} else if (members_nb < 256) {
|
||||
tag_type = u8;
|
||||
} else if (members_nb < 65536) {
|
||||
tag_type = u16;
|
||||
} else {
|
||||
return error.GenerateUnionTooMuchMembers;
|
||||
}
|
||||
|
||||
// second iteration to generate tags
|
||||
comptime var enum_fields: [members_nb]std.builtin.Type.EnumField = undefined;
|
||||
comptime var done = 0;
|
||||
inline for (tuple_members) |member| {
|
||||
const member_T = @field(tuple, member.name);
|
||||
const member_info = @typeInfo(member_T);
|
||||
if (member_info == .Union) {
|
||||
const member_union = member_info.Union;
|
||||
inline for (member_union.fields) |field| {
|
||||
enum_fields[done] = .{
|
||||
.name = fmtName(field.field_type),
|
||||
.value = done,
|
||||
};
|
||||
done += 1;
|
||||
}
|
||||
} else if (member_info == .Struct) {
|
||||
enum_fields[done] = .{
|
||||
.name = fmtName(member_T),
|
||||
.value = done,
|
||||
};
|
||||
done += 1;
|
||||
}
|
||||
}
|
||||
const decls: [0]std.builtin.Type.Declaration = undefined;
|
||||
const enum_info = std.builtin.Type.Enum{
|
||||
.layout = .Auto,
|
||||
.tag_type = tag_type,
|
||||
.fields = &enum_fields,
|
||||
.decls = &decls,
|
||||
.is_exhaustive = true,
|
||||
};
|
||||
const enum_T = @Type(std.builtin.Type{ .Enum = enum_info });
|
||||
|
||||
// third iteration to generate union
|
||||
comptime var union_fields: [members_nb]std.builtin.Type.UnionField = undefined;
|
||||
done = 0;
|
||||
inline for (tuple_members) |member, i| {
|
||||
const member_T = @field(tuple, member.name);
|
||||
const member_info = @typeInfo(member_T);
|
||||
if (member_info == .Union) {
|
||||
const member_union = member_info.Union;
|
||||
inline for (member_union.fields) |field| {
|
||||
union_fields[done] = .{
|
||||
.name = fmtName(field.field_type),
|
||||
.field_type = field.field_type,
|
||||
.alignment = field.alignment,
|
||||
};
|
||||
done += 1;
|
||||
}
|
||||
} else if (member_info == .Struct) {
|
||||
const alignment = tuple_info.Struct.fields[i].alignment;
|
||||
union_fields[done] = .{
|
||||
.name = fmtName(member_T),
|
||||
.field_type = member_T,
|
||||
.alignment = alignment,
|
||||
};
|
||||
done += 1;
|
||||
}
|
||||
}
|
||||
const union_info = std.builtin.Type.Union{
|
||||
.layout = .Auto,
|
||||
.tag_type = enum_T,
|
||||
.fields = &union_fields,
|
||||
.decls = &decls,
|
||||
};
|
||||
const union_T = @Type(std.builtin.Type{ .Union = union_info });
|
||||
|
||||
return .{
|
||||
._enum = enum_T,
|
||||
._union = union_T,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// Tests
|
||||
// -----
|
||||
|
||||
const Error = error{
|
||||
UnionArgNotTuple,
|
||||
UnionMemberNotUnionOrStruct,
|
||||
UnionTooMuchMembers,
|
||||
};
|
||||
|
||||
const Astruct = struct {
|
||||
value: u8 = 0,
|
||||
};
|
||||
const Bstruct = struct {
|
||||
value: u8 = 0,
|
||||
};
|
||||
const Cstruct = struct {
|
||||
value: u8 = 0,
|
||||
};
|
||||
const Dstruct = struct {
|
||||
value: u8 = 0,
|
||||
};
|
||||
|
||||
pub fn tests() !void {
|
||||
|
||||
// Union from structs
|
||||
const FromStructs = try Union.private_compile(.{ Astruct, Bstruct, Cstruct });
|
||||
|
||||
const from_structs_enum = @typeInfo(FromStructs._enum);
|
||||
try std.testing.expect(from_structs_enum == .Enum);
|
||||
try std.testing.expect(from_structs_enum.Enum.fields.len == 3);
|
||||
try std.testing.expect(from_structs_enum.Enum.tag_type == u2);
|
||||
try std.testing.expect(from_structs_enum.Enum.fields[0].value == 0);
|
||||
try std.testing.expectEqualStrings(from_structs_enum.Enum.fields[0].name, "Astruct");
|
||||
|
||||
const from_structs_union = @typeInfo(FromStructs._union);
|
||||
try std.testing.expect(from_structs_union == .Union);
|
||||
try std.testing.expect(from_structs_union.Union.tag_type == FromStructs._enum);
|
||||
try std.testing.expect(from_structs_union.Union.fields.len == 3);
|
||||
try std.testing.expect(from_structs_union.Union.fields[0].field_type == Astruct);
|
||||
try std.testing.expectEqualStrings(from_structs_union.Union.fields[0].name, "Astruct");
|
||||
|
||||
// Union from union and structs
|
||||
const FromMix = try Union.private_compile(.{ FromStructs._union, Dstruct });
|
||||
|
||||
const from_mix_enum = @typeInfo(FromMix._enum);
|
||||
try std.testing.expect(from_mix_enum == .Enum);
|
||||
try std.testing.expect(from_mix_enum.Enum.fields.len == 4);
|
||||
try std.testing.expect(from_mix_enum.Enum.tag_type == u3);
|
||||
try std.testing.expect(from_mix_enum.Enum.fields[0].value == 0);
|
||||
try std.testing.expectEqualStrings(from_mix_enum.Enum.fields[3].name, "Dstruct");
|
||||
|
||||
const from_mix_union = @typeInfo(FromMix._union);
|
||||
try std.testing.expect(from_mix_union == .Union);
|
||||
try std.testing.expect(from_mix_union.Union.tag_type == FromMix._enum);
|
||||
try std.testing.expect(from_mix_union.Union.fields.len == 4);
|
||||
try std.testing.expect(from_mix_union.Union.fields[3].field_type == Dstruct);
|
||||
try std.testing.expectEqualStrings(from_mix_union.Union.fields[3].name, "Dstruct");
|
||||
|
||||
std.debug.print("Generate Union: OK\n", .{});
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
const std = @import("std");
|
||||
|
||||
const jsruntime = @import("jsruntime");
|
||||
const generate = @import("generate.zig");
|
||||
|
||||
const DOM = @import("dom.zig");
|
||||
const testExecFn = @import("html/document.zig").testExecFn;
|
||||
@@ -27,6 +28,11 @@ fn testsExecFn(
|
||||
}
|
||||
|
||||
test {
|
||||
std.debug.print("\n", .{});
|
||||
|
||||
// generate tests
|
||||
try generate.tests();
|
||||
|
||||
// generate APIs
|
||||
const apis = jsruntime.compile(DOM.Interfaces);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user