diff --git a/src/dom.zig b/src/dom.zig index 56e24f77..6d481fe3 100644 --- a/src/dom.zig +++ b/src/dom.zig @@ -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); diff --git a/src/generate.zig b/src/generate.zig index a05e8a2e..27b6853b 100644 --- a/src/generate.zig +++ b/src/generate.zig @@ -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", .{}); } diff --git a/src/html/elements.zig b/src/html/elements.zig index 039f8310..19a5f72e 100644 --- a/src/html/elements.zig +++ b/src/html/elements.zig @@ -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;