Add generate function for Tuple

Signed-off-by: Francis Bouvier <francis.bouvier@gmail.com>
This commit is contained in:
Francis Bouvier
2023-05-23 15:23:09 +02:00
parent fdafd96003
commit 76c8d8594c
3 changed files with 149 additions and 76 deletions

View File

@@ -1,3 +1,5 @@
const generate = @import("generate.zig");
const Console = @import("jsruntime").Console;
// DOM
@@ -12,7 +14,7 @@ pub const HTMLDocument = @import("html/document.zig").HTMLDocument;
const E = @import("html/elements.zig");
// Interfaces
pub const Interfaces = .{
const interfaces = .{
Console,
// DOM
@@ -23,69 +25,8 @@ pub const Interfaces = .{
// HTML
HTMLDocument,
E.HTMLElement,
E.HTMLMediaElement,
// TODO: generate HTMLElements comptime
E.HTMLUnknownElement,
E.HTMLAnchorElement,
E.HTMLAreaElement,
E.HTMLAudioElement,
E.HTMLBRElement,
E.HTMLBaseElement,
E.HTMLBodyElement,
E.HTMLButtonElement,
E.HTMLCanvasElement,
E.HTMLDListElement,
E.HTMLDialogElement,
E.HTMLDataElement,
E.HTMLDivElement,
E.HTMLEmbedElement,
E.HTMLFieldSetElement,
E.HTMLFormElement,
E.HTMLFrameSetElement,
E.HTMLHRElement,
E.HTMLHeadElement,
E.HTMLHeadingElement,
E.HTMLHtmlElement,
E.HTMLIFrameElement,
E.HTMLImageElement,
E.HTMLInputElement,
E.HTMLLIElement,
E.HTMLLabelElement,
E.HTMLLegendElement,
E.HTMLLinkElement,
E.HTMLMapElement,
E.HTMLMetaElement,
E.HTMLMeterElement,
E.HTMLModElement,
E.HTMLOListElement,
E.HTMLObjectElement,
E.HTMLOptGroupElement,
E.HTMLOptionElement,
E.HTMLOutputElement,
E.HTMLParagraphElement,
E.HTMLPictureElement,
E.HTMLPreElement,
E.HTMLProgressElement,
E.HTMLQuoteElement,
E.HTMLScriptElement,
E.HTMLSelectElement,
E.HTMLSourceElement,
E.HTMLSpanElement,
E.HTMLStyleElement,
E.HTMLTableElement,
E.HTMLTableCaptionElement,
E.HTMLTableCellElement,
E.HTMLTableColElement,
E.HTMLTableRowElement,
E.HTMLTableSectionElement,
E.HTMLTemplateElement,
E.HTMLTextAreaElement,
E.HTMLTimeElement,
E.HTMLTitleElement,
E.HTMLTrackElement,
E.HTMLUListElement,
E.HTMLVideoElement,
E.HTMLElementsTypes,
};
pub const Interfaces = generate.TupleInst(generate.TupleT(interfaces), interfaces);

View File

@@ -6,6 +6,8 @@ fn fmtName(comptime T: type) []const u8 {
return it.first();
}
// Generate a flatten tagged Union from various structs and union of structs
// TODO: make this function more generic
pub const Union = struct {
_enum: type,
_union: type,
@@ -23,7 +25,7 @@ pub const Union = struct {
const tuple_T = @TypeOf(tuple);
const tuple_info = @typeInfo(tuple_T);
if (tuple_info != .Struct or !tuple_info.Struct.is_tuple) {
return error.GenerateUnionArgNotTuple;
return error.GenerateArgNotTuple;
}
const tuple_members = tuple_info.Struct.fields;
@@ -39,7 +41,7 @@ pub const Union = struct {
} else if (member_info == .Struct) {
members_nb += 1;
} else {
return error.GenerateUnionMemberNotUnionOrStruct;
return error.GenerateMemberNotUnionOrStruct;
}
}
@@ -64,7 +66,7 @@ pub const Union = struct {
} else if (members_nb < 65536) {
tag_type = u16;
} else {
return error.GenerateUnionTooMuchMembers;
return error.GenerateTooMuchMembers;
}
// second iteration to generate tags
@@ -100,7 +102,7 @@ pub const Union = struct {
};
const enum_T = @Type(std.builtin.Type{ .Enum = enum_info });
// third iteration to generate union
// third iteration to generate union type
comptime var union_fields: [members_nb]std.builtin.Type.UnionField = undefined;
done = 0;
inline for (tuple_members) |member, i| {
@@ -141,13 +143,123 @@ pub const Union = struct {
}
};
fn itoa(comptime i: u8) ![]u8 {
comptime {
var len: usize = undefined;
if (i < 10) {
len = 1;
} else if (i < 100) {
len = 2;
} else {
return error.GenerateTooMuchMembers;
}
var buf: [len]u8 = undefined;
return try std.fmt.bufPrint(buf[0..], "{d}", .{i});
}
}
// Generate a flatten tuple type from various structs and tuple of structs.
// TODO: make this function more generic
pub fn TupleT(comptime tuple: anytype) type {
// check types provided
const tuple_T = @TypeOf(tuple);
const tuple_info = @typeInfo(tuple_T);
if (tuple_info != .Struct or !tuple_info.Struct.is_tuple) {
@compileError("GenerateArgNotTuple");
}
const tuple_members = tuple_info.Struct.fields;
// first iteration to get the total number of members
comptime var members_nb = 0;
for (tuple_members) |member| {
const member_T = @field(tuple, member.name);
if (@TypeOf(member_T) == type) {
members_nb += 1;
} else {
const member_info = @typeInfo(@TypeOf(member_T));
if (member_info != .Struct and !member_info.Struct.is_tuple) {
@compileError("GenerateMemberNotTypeOrTuple");
}
for (member_info.Struct.fields) |field| {
if (@TypeOf(@field(member_T, field.name)) != type) {
@compileError("GenerateMemberTupleChildNotType");
}
}
members_nb += member_info.Struct.fields.len;
}
}
// second iteration to generate the tuple type
var fields: [members_nb]std.builtin.Type.StructField = undefined;
var done = 0;
while (done < members_nb) {
fields[done] = .{
.name = try itoa(done),
.field_type = type,
.default_value = null,
.is_comptime = false,
.alignment = @alignOf(type),
};
done += 1;
}
const decls: [0]std.builtin.Type.Declaration = undefined;
const info = std.builtin.Type.Struct{
.layout = .Auto,
.fields = &fields,
.decls = &decls,
.is_tuple = true,
};
return @Type(std.builtin.Type{ .Struct = info });
}
// Instantiate a flatten tuple from various structs and tuple of structs
// You need to call first TupleT to generate the according type
// TODO: make this function more generic
pub fn TupleInst(comptime T: type, comptime tuple: anytype) T {
// check types provided
const tuple_T = @TypeOf(tuple);
const tuple_info = @typeInfo(tuple_T);
const tuple_members = tuple_info.Struct.fields;
// instantiate the tuple
var t: T = undefined;
var done = 0;
for (tuple_members) |member| {
const member_T = @field(tuple, member.name);
var member_info: std.builtin.Type = undefined;
if (@TypeOf(member_T) == type) {
member_info = @typeInfo(member_T);
} else {
member_info = @typeInfo(@TypeOf(member_T));
}
var member_detail = member_info.Struct;
if (member_detail.is_tuple) {
for (member_detail.fields) |field| {
const name = try itoa(done);
@field(t, name) = @field(member_T, field.name);
done += 1;
}
} else {
const name = try itoa(done);
@field(t, name) = @field(tuple, member.name);
done += 1;
}
}
return t;
}
// Tests
// -----
const Error = error{
UnionArgNotTuple,
UnionMemberNotUnionOrStruct,
UnionTooMuchMembers,
GenerateArgNotTuple,
GenerateMemberNotUnionOrStruct,
GenerateMemberNotTupleOrStruct,
GenerateMemberTupleNotStruct,
GenerateTooMuchMembers,
};
const Astruct = struct {
@@ -200,4 +312,27 @@ pub fn tests() !void {
try std.testing.expectEqualStrings(from_mix_union.Union.fields[3].name, "Dstruct");
std.debug.print("Generate Union: OK\n", .{});
// Tuple from structs
const tuple_structs = .{ Astruct, Bstruct };
const tFromStructs = TupleInst(TupleT(tuple_structs), tuple_structs);
const t_from_structs = @typeInfo(@TypeOf(tFromStructs));
try std.testing.expect(t_from_structs == .Struct);
try std.testing.expect(t_from_structs.Struct.is_tuple);
try std.testing.expect(t_from_structs.Struct.fields.len == 2);
try std.testing.expect(@field(tFromStructs, "0") == Astruct);
try std.testing.expect(@field(tFromStructs, "1") == Bstruct);
// Tuple from tuple and structs
const tuple_mix = .{ tFromStructs, Cstruct };
const tFromMix = TupleInst(TupleT(tuple_mix), tuple_mix);
const t_from_mix = @typeInfo(@TypeOf(tFromMix));
try std.testing.expect(t_from_mix == .Struct);
try std.testing.expect(t_from_mix.Struct.is_tuple);
try std.testing.expect(t_from_mix.Struct.fields.len == 3);
try std.testing.expect(@field(tFromMix, "0") == Astruct);
try std.testing.expect(@field(tFromMix, "1") == Bstruct);
try std.testing.expect(@field(tFromMix, "2") == Cstruct);
std.debug.print("Generate Tuple: OK\n", .{});
}

View File

@@ -16,7 +16,7 @@ pub const HTMLElement = struct {
}
};
const HTMLElementsTypes = .{
pub const HTMLElementsTypes = .{
HTMLUnknownElement,
HTMLAnchorElement,
HTMLAreaElement,
@@ -78,10 +78,7 @@ const HTMLElementsTypes = .{
HTMLUListElement,
HTMLVideoElement,
};
fn generateElements() generate.Union {
return generate.Union.compile(HTMLElementsTypes);
}
const HTMLElementsGenerated = generateElements();
const HTMLElementsGenerated = generate.Union.compile(HTMLElementsTypes);
pub const HTMLElements = HTMLElementsGenerated._union;
pub const HTMLElementsTags = HTMLElementsGenerated._enum;