From fc0ec860b0a166e3a8c1c70cc672b21a0d342399 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Sat, 1 Feb 2025 14:31:16 +0800 Subject: [PATCH] Tweak generate.Tuple and generate.Union Leverage comptime fields to give generated Tuple a default value, allowing TupleT and Tuple to be merged. Only call generate.Tuple on the final output. This eliminates redundant deduplication, and results in a simpler API (nested types just need to expose a natural Zig tuple). generate.Union leverages the new Tuple and removes unused features. --- src/apiweb.zig | 2 +- src/dom/character_data.zig | 5 +- src/dom/dom.zig | 6 +- src/dom/mutation_observer.zig | 6 +- src/dom/node.zig | 10 +- src/dom/nodelist.zig | 5 +- src/dom/text.zig | 5 +- src/events/event.zig | 8 +- src/generate.zig | 544 ++++++++-------------------- src/html/elements.zig | 5 +- src/html/html.zig | 4 +- src/iterator/iterator.zig | 6 +- src/run_tests.zig | 4 +- src/storage/storage.zig | 6 +- src/url/url.zig | 5 +- src/xhr/xhr.zig | 5 +- src/xmlserializer/xmlserializer.zig | 5 +- 17 files changed, 192 insertions(+), 439 deletions(-) diff --git a/src/apiweb.zig b/src/apiweb.zig index ad825044..8df1bd6d 100644 --- a/src/apiweb.zig +++ b/src/apiweb.zig @@ -42,6 +42,6 @@ pub const Interfaces = generate.Tuple(.{ URL.Interfaces, Iterators.Interfaces, XMLSerializer.Interfaces, -}); +}){}; pub const UserContext = @import("user_context.zig").UserContext; diff --git a/src/dom/character_data.zig b/src/dom/character_data.zig index b195b921..372395c2 100644 --- a/src/dom/character_data.zig +++ b/src/dom/character_data.zig @@ -21,7 +21,6 @@ const std = @import("std"); const jsruntime = @import("jsruntime"); const Case = jsruntime.test_utils.Case; const checkCases = jsruntime.test_utils.checkCases; -const generate = @import("../generate.zig"); const parser = @import("netsurf"); @@ -32,12 +31,12 @@ const ProcessingInstruction = @import("processing_instruction.zig").ProcessingIn const HTMLElem = @import("../html/elements.zig"); // CharacterData interfaces -pub const Interfaces = generate.Tuple(.{ +pub const Interfaces = .{ Comment, Text.Text, Text.Interfaces, ProcessingInstruction, -}); +}; // CharacterData implementation pub const CharacterData = struct { diff --git a/src/dom/dom.zig b/src/dom/dom.zig index dc521bb6..76a4a185 100644 --- a/src/dom/dom.zig +++ b/src/dom/dom.zig @@ -16,8 +16,6 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -const generate = @import("../generate.zig"); - const DOMException = @import("exceptions.zig").DOMException; const EventTarget = @import("event_target.zig").EventTarget; const DOMImplementation = @import("implementation.zig").DOMImplementation; @@ -27,7 +25,7 @@ const NodeList = @import("nodelist.zig"); const Nod = @import("node.zig"); const MutationObserver = @import("mutation_observer.zig"); -pub const Interfaces = generate.Tuple(.{ +pub const Interfaces = .{ DOMException, EventTarget, DOMImplementation, @@ -37,4 +35,4 @@ pub const Interfaces = generate.Tuple(.{ Nod.Node, Nod.Interfaces, MutationObserver.Interfaces, -}); +}; diff --git a/src/dom/mutation_observer.zig b/src/dom/mutation_observer.zig index 9219ccc9..f5003686 100644 --- a/src/dom/mutation_observer.zig +++ b/src/dom/mutation_observer.zig @@ -26,15 +26,13 @@ const CallbackResult = jsruntime.CallbackResult; const Case = jsruntime.test_utils.Case; const checkCases = jsruntime.test_utils.checkCases; -const generate = @import("../generate.zig"); - const NodeList = @import("nodelist.zig").NodeList; -pub const Interfaces = generate.Tuple(.{ +pub const Interfaces = .{ MutationObserver, MutationRecord, MutationRecords, -}); +}; const Walker = @import("../dom/walker.zig").WalkerChildren; diff --git a/src/dom/node.zig b/src/dom/node.zig index 05c47d74..3e0a7561 100644 --- a/src/dom/node.zig +++ b/src/dom/node.zig @@ -47,7 +47,7 @@ const HTML = @import("../html/html.zig"); const HTMLElem = @import("../html/elements.zig"); // Node interfaces -pub const Interfaces = generate.Tuple(.{ +pub const Interfaces = .{ Attr, CData.CharacterData, CData.Interfaces, @@ -57,12 +57,10 @@ pub const Interfaces = generate.Tuple(.{ DocumentFragment, HTMLCollection, HTMLCollectionIterator, - HTML.Interfaces, -}); -const Generated = generate.Union.compile(Interfaces); -pub const Union = Generated._union; -pub const Tags = Generated._enum; +}; + +pub const Union = generate.Union(Interfaces); // Node implementation pub const Node = struct { diff --git a/src/dom/nodelist.zig b/src/dom/nodelist.zig index 6dfb5cc6..5dfc7031 100644 --- a/src/dom/nodelist.zig +++ b/src/dom/nodelist.zig @@ -25,7 +25,6 @@ const Callback = jsruntime.Callback; const CallbackResult = jsruntime.CallbackResult; const Case = jsruntime.test_utils.Case; const checkCases = jsruntime.test_utils.checkCases; -const generate = @import("../generate.zig"); const NodeUnion = @import("node.zig").Union; const Node = @import("node.zig").Node; @@ -36,10 +35,10 @@ const log = std.log.scoped(.nodelist); const DOMException = @import("exceptions.zig").DOMException; -pub const Interfaces = generate.Tuple(.{ +pub const Interfaces = .{ NodeListIterator, NodeList, -}); +}; pub const NodeListIterator = struct { pub const mem_guarantied = true; diff --git a/src/dom/text.zig b/src/dom/text.zig index 39c22c47..a2d3f9cf 100644 --- a/src/dom/text.zig +++ b/src/dom/text.zig @@ -21,7 +21,6 @@ const std = @import("std"); const jsruntime = @import("jsruntime"); const Case = jsruntime.test_utils.Case; const checkCases = jsruntime.test_utils.checkCases; -const generate = @import("../generate.zig"); const parser = @import("netsurf"); @@ -31,9 +30,9 @@ const CDATASection = @import("cdata_section.zig").CDATASection; const UserContext = @import("../user_context.zig").UserContext; // Text interfaces -pub const Interfaces = generate.Tuple(.{ +pub const Interfaces = .{ CDATASection, -}); +}; pub const Text = struct { pub const Self = parser.Text; diff --git a/src/events/event.zig b/src/events/event.zig index be92ba30..35069ceb 100644 --- a/src/events/event.zig +++ b/src/events/event.zig @@ -37,12 +37,12 @@ const ProgressEvent = @import("../xhr/progress_event.zig").ProgressEvent; const log = std.log.scoped(.events); // Event interfaces -pub const Interfaces = generate.Tuple(.{ +pub const Interfaces = .{ Event, ProgressEvent, -}); -const Generated = generate.Union.compile(Interfaces); -pub const Union = Generated._union; +}; + +pub const Union = generate.Union(Interfaces); // https://dom.spec.whatwg.org/#event pub const Event = struct { diff --git a/src/generate.zig b/src/generate.zig index e1ae7d0e..62be7edb 100644 --- a/src/generate.zig +++ b/src/generate.zig @@ -17,430 +17,202 @@ // along with this program. If not, see . const std = @import("std"); -const builtin = @import("builtin"); - -// Utils -// ----- +const Type = std.builtin.Type; + +pub fn Union(interfaces: anytype) type { + // @setEvalBranchQuota(10000); + const tuple = Tuple(interfaces){}; + const fields = std.meta.fields(@TypeOf(tuple)); + + const tag_type = switch (fields.len) { + 0 => unreachable, + 1 => u0, + 2 => u1, + 3...4 => u2, + 5...8 => u3, + 9...16 => u4, + 17...32 => u5, + 33...64 => u6, + 65...128 => u7, + 129...256 => u8, + else => @compileError("Too many interfaces to generate union"), + }; -fn itoa(comptime i: u8) ![]const u8 { - var len: usize = undefined; - if (i < 10) { - len = 1; - } else if (i < 100) { - len = 2; - } else if (i < 1000) { - len = 3; - } else { - return error.GenerateTooMuchMembers; + // second iteration to generate tags + var enum_fields: [fields.len]Type.EnumField = undefined; + for (fields, 0..) |field, index| { + const member = @field(tuple, field.name); + const full_name = @typeName(member); + const separator = std.mem.lastIndexOfScalar(u8, full_name, '.') orelse unreachable; + const name = full_name[separator + 1 ..]; + enum_fields[index] = .{ + .name = name ++ "", + .value = index, + }; } - var buf: [len]u8 = undefined; - return try std.fmt.bufPrint(buf[0..], "{d}", .{i}); -} - -fn fmtName(comptime T: type) [:0]const u8 { - var it = std.mem.splitBackwards(u8, @typeName(T), "."); - return it.first() ++ ""; -} -// Union -// ----- - -// Generate a flatten tagged Union from various structs and union of structs -// TODO: make this function more generic -// TODO: dedup -pub const Union = struct { - _enum: type, - _union: type, - - pub fn compile(comptime tuple: anytype) Union { - return private_compile(tuple) catch |err| @compileError(@errorName(err)); + const enum_info = Type.Enum{ + .tag_type = tag_type, + .fields = &enum_fields, + .decls = &.{}, + .is_exhaustive = true, + }; + const enum_T = @Type(std.builtin.Type{ .Enum = enum_info }); + + // third iteration to generate union type + var union_fields: [fields.len]Type.UnionField = undefined; + for (fields, enum_fields, 0..) |field, e, index| { + var FT = @field(tuple, field.name); + if (@hasDecl(FT, "Self")) { + FT = *(@field(FT, "Self")); + } + union_fields[index] = .{ + .type = FT, + .name = e.name, + .alignment = @alignOf(FT), + }; } - fn private_compile(comptime tuple: anytype) !Union { - @setEvalBranchQuota(10000); + return @Type(.{ .Union = .{ + .layout = .auto, + .tag_type = enum_T, + .fields = &union_fields, + .decls = &.{}, + } }); +} - // 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.GenerateArgNotTuple; - } +pub fn Tuple(args: anytype) type { + @setEvalBranchQuota(100000); - const tuple_members = tuple_info.Struct.fields; + const count = countInterfaces(args, 0); + var interfaces: [count]type = undefined; + _ = flattenInterfaces(args, &interfaces, 0); - // first iteration to get the total number of members - var members_nb = 0; - 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.GenerateMemberNotUnionOrStruct; - } - } + const unfiltered_count, const filter_set = filterMap(count, interfaces); - // define the tag type regarding the members nb - 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 = u5; - } 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.GenerateTooMuchMembers; - } + var field_index: usize = 0; + var fields: [unfiltered_count]Type.StructField = undefined; - // second iteration to generate tags - var enum_fields: [members_nb]std.builtin.Type.EnumField = undefined; - var done = 0; - 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; - for (member_union.fields) |field| { - enum_fields[done] = .{ - .name = fmtName(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{ - .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 type - var union_fields: [members_nb]std.builtin.Type.UnionField = undefined; - done = 0; - for (tuple_members, 0..) |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; - for (member_union.fields) |field| { - var T: type = undefined; - if (@hasDecl(field.type, "Self")) { - T = @field(field.type, "Self"); - T = *T; - } else { - T = field.type; - } - union_fields[done] = .{ - .name = fmtName(field.type), - .type = T, - .alignment = @alignOf(T), - }; - done += 1; - } - } else if (member_info == .Struct) { - const member_name = try itoa(i); - var T = @field(tuple, member_name); - if (@hasDecl(T, "Self")) { - T = @field(T, "Self"); - T = *T; - } - union_fields[done] = .{ - // UnionField.name expect a null terminated string. - // concatenate the `[]const u8` string with an empty string - // literal (`name ++ ""`) to explicitly coerce it to `[:0]const - // u8`. - .name = fmtName(member_T) ++ "", - .type = T, - .alignment = @alignOf(T), - }; - done += 1; - } + for (filter_set, 0..) |filter, i| { + if (filter) { + continue; } - 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, + fields[field_index] = .{ + .name = std.fmt.comptimePrint("{d}", .{field_index}), + .type = type, + // has to be true in order to properly capture the default value + .is_comptime = true, + .alignment = @alignOf(type), + .default_value = @ptrCast(&interfaces[i]), }; + field_index += 1; } -}; -// Tuple -// ----- - -fn tupleNb(comptime tuple: anytype) usize { - var nb = 0; - for (@typeInfo(@TypeOf(tuple)).Struct.fields) |member| { - const member_T = @field(tuple, member.name); - if (@TypeOf(member_T) == type) { - 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"); - } - } - nb += member_info.Struct.fields.len; - } - } - return nb; + return @Type(.{ .Struct = .{ + .layout = .auto, + .fields = &fields, + .decls = &.{}, + .is_tuple = true, + } }); } -fn tupleTypes(comptime nb: usize, comptime tuple: anytype) [nb]type { - var types: [nb]type = undefined; - var done = 0; - for (@typeInfo(@TypeOf(tuple)).Struct.fields) |member| { - const T = @field(tuple, member.name); - if (@TypeOf(T) == type) { - types[done] = T; - done += 1; +fn countInterfaces(args: anytype, count: usize) usize { + var new_count = count; + for (@typeInfo(@TypeOf(args)).Struct.fields) |f| { + const member = @field(args, f.name); + if (@TypeOf(member) == type) { + new_count += 1; } else { - const info = @typeInfo(@TypeOf(T)); - for (info.Struct.fields) |field| { - types[done] = @field(T, field.name); - done += 1; - } - } - } - return types; -} - -fn isDup(comptime nb: usize, comptime list: [nb]type, comptime T: type, comptime i: usize) bool { - for (list, 0..) |item, index| { - if (i >= index) { - // check sequentially - continue; - } - if (T == item) { - return true; + new_count = countInterfaces(member, new_count); } } - return false; + return new_count; } -fn dedupIndexes(comptime nb: usize, comptime types: [nb]type) [nb]i32 { - var dedup_indexes: [nb]i32 = undefined; - for (types, 0..) |T, i| { - if (isDup(nb, types, T, i)) { - dedup_indexes[i] = -1; +fn flattenInterfaces(args: anytype, interfaces: []type, index: usize) usize { + var new_index = index; + for (@typeInfo(@TypeOf(args)).Struct.fields) |f| { + const member = @field(args, f.name); + if (@TypeOf(member) == type) { + interfaces[new_index] = member; + new_index += 1; } else { - dedup_indexes[i] = i; + new_index = flattenInterfaces(member, interfaces, new_index); } } - return dedup_indexes; + return new_index; } -fn dedupNb(comptime nb: usize, comptime dedup_indexes: [nb]i32) usize { - var dedup_nb = 0; - for (dedup_indexes) |index| { - if (index != -1) { - dedup_nb += 1; +fn filterMap(comptime count: usize, interfaces: [count]type) struct { usize, [count]bool } { + var map: [count]bool = undefined; + var unfiltered_count: usize = 0; + outer: for (interfaces, 0..) |iface, i| { + for (interfaces[i + 1 ..]) |check| { + if (iface == check) { + map[i] = true; + continue :outer; + } } + map[i] = false; + unfiltered_count += 1; } - return dedup_nb; + return .{ unfiltered_count, map }; } -fn TupleT(comptime tuple: anytype) type { - @setEvalBranchQuota(100000); - - // logic - const nb = tupleNb(tuple); - const types = tupleTypes(nb, tuple); - const dedup_indexes = dedupIndexes(nb, types); - const dedup_nb = dedupNb(nb, dedup_indexes); - - // generate the tuple type - var fields: [dedup_nb]std.builtin.Type.StructField = undefined; - var done = 0; - for (dedup_indexes) |index| { - if (index == -1) { - continue; - } - fields[done] = .{ - // StructField.name expect a null terminated string. - // concatenate the `[]const u8` string with an empty string - // literal (`name ++ ""`) to explicitly coerce it to `[:0]const - // u8`. - .name = try itoa(done) ++ "", - .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, +test "generate.Union" { + const Astruct = struct { + pub const Self = Other; + const Other = struct {}; }; - return @Type(std.builtin.Type{ .Struct = info }); -} - -// Create a flatten tuple from various structs and tuple of structs -// Duplicates will be removed. -// TODO: make this function more generic -pub fn Tuple(comptime tuple: anytype) TupleT(tuple) { - - // 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"); - } - // generate the type - const T = TupleT(tuple); + const Bstruct = struct { + value: u8 = 0, + }; - // logic - const nb = tupleNb(tuple); - const types = tupleTypes(nb, tuple); - const dedup_indexes = dedupIndexes(nb, types); + const Cstruct = struct { + value: u8 = 0, + }; - // instantiate the tuple - var t: T = undefined; - var done = 0; - for (dedup_indexes) |index| { - if (index == -1) { - continue; - } - const name = try itoa(done); - @field(t, name) = types[index]; - done += 1; - } - return t; + const value = Union(.{ Astruct, Bstruct, .{ Cstruct } }); + const ti = @typeInfo(value).Union; + try std.testing.expectEqual(3, ti.fields.len); + try std.testing.expectEqualStrings("*generate.test.generate.Union.Astruct.Other", @typeName(ti.fields[0].type)); + try std.testing.expectEqualStrings(ti.fields[0].name, "Astruct"); + try std.testing.expectEqual(Bstruct, ti.fields[1].type); + try std.testing.expectEqualStrings(ti.fields[1].name, "Bstruct"); + try std.testing.expectEqual(Cstruct, ti.fields[2].type); + try std.testing.expectEqualStrings(ti.fields[2].name, "Cstruct"); } -// Tests -// ----- - -const Error = error{ - GenerateArgNotTuple, - GenerateMemberNotUnionOrStruct, - GenerateMemberNotTupleOrStruct, - GenerateMemberTupleNotStruct, - GenerateTooMuchMembers, -}; - -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].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].type == Dstruct); - try std.testing.expectEqualStrings(from_mix_union.Union.fields[3].name, "Dstruct"); - - std.debug.print("Generate Union: OK\n", .{}); +test "generate.Tuple" { + const Astruct = struct { + }; - // Tuple from structs - const tuple_structs = .{ Astruct, Bstruct }; - const tFromStructs = Tuple(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); + const Bstruct = struct { + value: u8 = 0, + }; - // Tuple from tuple and structs - const tuple_mix = .{ tFromStructs, Cstruct }; - const tFromMix = Tuple(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); + const Cstruct = struct { + value: u8 = 0, + }; - // Tuple with dedup - const tuple_dedup = .{ Cstruct, Astruct, tFromStructs, Bstruct }; - const tFromDedup = Tuple(tuple_dedup); - const t_from_dedup = @typeInfo(@TypeOf(tFromDedup)); - try std.testing.expect(t_from_dedup == .Struct); - try std.testing.expect(t_from_dedup.Struct.is_tuple); - try std.testing.expect(t_from_dedup.Struct.fields.len == 3); - try std.testing.expect(@field(tFromDedup, "0") == Cstruct); - try std.testing.expect(@field(tFromDedup, "1") == Astruct); - try std.testing.expect(@field(tFromDedup, "2") == Bstruct); + { + const tuple = Tuple(.{ Astruct, Bstruct }){}; + const ti = @typeInfo(@TypeOf(tuple)).Struct; + try std.testing.expectEqual(true, ti.is_tuple); + try std.testing.expectEqual(2, ti.fields.len); + try std.testing.expectEqual(Astruct, tuple.@"0"); + try std.testing.expectEqual(Bstruct, tuple.@"1"); + } - std.debug.print("Generate Tuple: OK\n", .{}); + { + // dedupe + const tuple = Tuple(.{ Cstruct, Astruct, .{ Astruct }, Bstruct, .{ Astruct, .{ Astruct, Bstruct } } }){}; + const ti = @typeInfo(@TypeOf(tuple)).Struct; + try std.testing.expectEqual(true, ti.is_tuple); + try std.testing.expectEqual(3, ti.fields.len); + try std.testing.expectEqual(Cstruct, tuple.@"0"); + try std.testing.expectEqual(Astruct, tuple.@"1"); + try std.testing.expectEqual(Bstruct, tuple.@"2"); + } } diff --git a/src/html/elements.zig b/src/html/elements.zig index c632b63c..d0ff13a4 100644 --- a/src/html/elements.zig +++ b/src/html/elements.zig @@ -99,9 +99,8 @@ pub const Interfaces = .{ HTMLVideoElement, CSSProperties, }; -const Generated = generate.Union.compile(Interfaces); -pub const Union = Generated._union; -pub const Tags = Generated._enum; + +pub const Union = generate.Union(Interfaces); // Abstract class // -------------- diff --git a/src/html/html.zig b/src/html/html.zig index 43535e17..9aae1de2 100644 --- a/src/html/html.zig +++ b/src/html/html.zig @@ -25,7 +25,7 @@ const Navigator = @import("navigator.zig").Navigator; const History = @import("history.zig").History; const Location = @import("location.zig").Location; -pub const Interfaces = generate.Tuple(.{ +pub const Interfaces = .{ HTMLDocument, HTMLElem.HTMLElement, HTMLElem.HTMLMediaElement, @@ -34,4 +34,4 @@ pub const Interfaces = generate.Tuple(.{ Navigator, History, Location, -}); +}; diff --git a/src/iterator/iterator.zig b/src/iterator/iterator.zig index e1dc9d3e..803cd978 100644 --- a/src/iterator/iterator.zig +++ b/src/iterator/iterator.zig @@ -1,10 +1,8 @@ const std = @import("std"); -const generate = @import("../generate.zig"); - -pub const Interfaces = generate.Tuple(.{ +pub const Interfaces = .{ U32Iterator, -}); +}; pub const U32Iterator = struct { pub const mem_guarantied = true; diff --git a/src/run_tests.zig b/src/run_tests.zig index 5e569f26..9c0c98b9 100644 --- a/src/run_tests.zig +++ b/src/run_tests.zig @@ -332,13 +332,11 @@ test { const queryTest = @import("url/query.zig"); std.testing.refAllDecls(queryTest); + std.testing.refAllDecls(@import("generate.zig")); std.testing.refAllDecls(@import("cdp/msg.zig")); } fn testJSRuntime(alloc: std.mem.Allocator) !void { - // generate tests - try generate.tests(); - // create JS vm const vm = jsruntime.VM.init(); defer vm.deinit(); diff --git a/src/storage/storage.zig b/src/storage/storage.zig index 6937594d..205a3895 100644 --- a/src/storage/storage.zig +++ b/src/storage/storage.zig @@ -21,15 +21,13 @@ const std = @import("std"); const jsruntime = @import("jsruntime"); const Case = jsruntime.test_utils.Case; const checkCases = jsruntime.test_utils.checkCases; -const generate = @import("../generate.zig"); - const DOMError = @import("netsurf").DOMError; const log = std.log.scoped(.storage); -pub const Interfaces = generate.Tuple(.{ +pub const Interfaces = .{ Bottle, -}); +}; // See https://storage.spec.whatwg.org/#model for storage hierarchy. // A Shed contains map of Shelves. The key is the document's origin. diff --git a/src/url/url.zig b/src/url/url.zig index bea44839..683186c2 100644 --- a/src/url/url.zig +++ b/src/url/url.zig @@ -21,14 +21,13 @@ const std = @import("std"); const jsruntime = @import("jsruntime"); const Case = jsruntime.test_utils.Case; const checkCases = jsruntime.test_utils.checkCases; -const generate = @import("../generate.zig"); const query = @import("query.zig"); -pub const Interfaces = generate.Tuple(.{ +pub const Interfaces = .{ URL, URLSearchParams, -}); +}; // https://url.spec.whatwg.org/#url // diff --git a/src/xhr/xhr.zig b/src/xhr/xhr.zig index 91243150..77131577 100644 --- a/src/xhr/xhr.zig +++ b/src/xhr/xhr.zig @@ -21,7 +21,6 @@ const std = @import("std"); const jsruntime = @import("jsruntime"); const Case = jsruntime.test_utils.Case; const checkCases = jsruntime.test_utils.checkCases; -const generate = @import("../generate.zig"); const DOMError = @import("netsurf").DOMError; const DOMException = @import("../dom/exceptions.zig").DOMException; @@ -42,11 +41,11 @@ const log = std.log.scoped(.xhr); // XHR interfaces // https://xhr.spec.whatwg.org/#interface-xmlhttprequest -pub const Interfaces = generate.Tuple(.{ +pub const Interfaces = .{ XMLHttpRequestEventTarget, XMLHttpRequestUpload, XMLHttpRequest, -}); +}; pub const XMLHttpRequestUpload = struct { pub const prototype = *XMLHttpRequestEventTarget; diff --git a/src/xmlserializer/xmlserializer.zig b/src/xmlserializer/xmlserializer.zig index f39745f1..a0153558 100644 --- a/src/xmlserializer/xmlserializer.zig +++ b/src/xmlserializer/xmlserializer.zig @@ -21,16 +21,15 @@ const std = @import("std"); const jsruntime = @import("jsruntime"); const Case = jsruntime.test_utils.Case; const checkCases = jsruntime.test_utils.checkCases; -const generate = @import("../generate.zig"); const DOMError = @import("netsurf").DOMError; const parser = @import("netsurf"); const dump = @import("../browser/dump.zig"); -pub const Interfaces = generate.Tuple(.{ +pub const Interfaces = .{ XMLSerializer, -}); +}; // https://w3c.github.io/DOM-Parsing/#dom-xmlserializer-constructor pub const XMLSerializer = struct {