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.
This commit is contained in:
Karl Seguin
2025-02-01 14:31:16 +08:00
parent 00d332cd16
commit fc0ec860b0
17 changed files with 203 additions and 450 deletions

View File

@@ -42,6 +42,6 @@ pub const Interfaces = generate.Tuple(.{
URL.Interfaces, URL.Interfaces,
Iterators.Interfaces, Iterators.Interfaces,
XMLSerializer.Interfaces, XMLSerializer.Interfaces,
}); }){};
pub const UserContext = @import("user_context.zig").UserContext; pub const UserContext = @import("user_context.zig").UserContext;

View File

@@ -21,7 +21,6 @@ const std = @import("std");
const jsruntime = @import("jsruntime"); const jsruntime = @import("jsruntime");
const Case = jsruntime.test_utils.Case; const Case = jsruntime.test_utils.Case;
const checkCases = jsruntime.test_utils.checkCases; const checkCases = jsruntime.test_utils.checkCases;
const generate = @import("../generate.zig");
const parser = @import("netsurf"); const parser = @import("netsurf");
@@ -32,12 +31,12 @@ const ProcessingInstruction = @import("processing_instruction.zig").ProcessingIn
const HTMLElem = @import("../html/elements.zig"); const HTMLElem = @import("../html/elements.zig");
// CharacterData interfaces // CharacterData interfaces
pub const Interfaces = generate.Tuple(.{ pub const Interfaces = .{
Comment, Comment,
Text.Text, Text.Text,
Text.Interfaces, Text.Interfaces,
ProcessingInstruction, ProcessingInstruction,
}); };
// CharacterData implementation // CharacterData implementation
pub const CharacterData = struct { pub const CharacterData = struct {

View File

@@ -16,8 +16,6 @@
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
const generate = @import("../generate.zig");
const DOMException = @import("exceptions.zig").DOMException; const DOMException = @import("exceptions.zig").DOMException;
const EventTarget = @import("event_target.zig").EventTarget; const EventTarget = @import("event_target.zig").EventTarget;
const DOMImplementation = @import("implementation.zig").DOMImplementation; const DOMImplementation = @import("implementation.zig").DOMImplementation;
@@ -27,7 +25,7 @@ const NodeList = @import("nodelist.zig");
const Nod = @import("node.zig"); const Nod = @import("node.zig");
const MutationObserver = @import("mutation_observer.zig"); const MutationObserver = @import("mutation_observer.zig");
pub const Interfaces = generate.Tuple(.{ pub const Interfaces = .{
DOMException, DOMException,
EventTarget, EventTarget,
DOMImplementation, DOMImplementation,
@@ -37,4 +35,4 @@ pub const Interfaces = generate.Tuple(.{
Nod.Node, Nod.Node,
Nod.Interfaces, Nod.Interfaces,
MutationObserver.Interfaces, MutationObserver.Interfaces,
}); };

View File

@@ -26,15 +26,13 @@ const CallbackResult = jsruntime.CallbackResult;
const Case = jsruntime.test_utils.Case; const Case = jsruntime.test_utils.Case;
const checkCases = jsruntime.test_utils.checkCases; const checkCases = jsruntime.test_utils.checkCases;
const generate = @import("../generate.zig");
const NodeList = @import("nodelist.zig").NodeList; const NodeList = @import("nodelist.zig").NodeList;
pub const Interfaces = generate.Tuple(.{ pub const Interfaces = .{
MutationObserver, MutationObserver,
MutationRecord, MutationRecord,
MutationRecords, MutationRecords,
}); };
const Walker = @import("../dom/walker.zig").WalkerChildren; const Walker = @import("../dom/walker.zig").WalkerChildren;

View File

@@ -47,7 +47,7 @@ const HTML = @import("../html/html.zig");
const HTMLElem = @import("../html/elements.zig"); const HTMLElem = @import("../html/elements.zig");
// Node interfaces // Node interfaces
pub const Interfaces = generate.Tuple(.{ pub const Interfaces = .{
Attr, Attr,
CData.CharacterData, CData.CharacterData,
CData.Interfaces, CData.Interfaces,
@@ -57,12 +57,10 @@ pub const Interfaces = generate.Tuple(.{
DocumentFragment, DocumentFragment,
HTMLCollection, HTMLCollection,
HTMLCollectionIterator, HTMLCollectionIterator,
HTML.Interfaces, HTML.Interfaces,
}); };
const Generated = generate.Union.compile(Interfaces);
pub const Union = Generated._union; pub const Union = generate.Union(Interfaces);
pub const Tags = Generated._enum;
// Node implementation // Node implementation
pub const Node = struct { pub const Node = struct {

View File

@@ -25,7 +25,6 @@ const Callback = jsruntime.Callback;
const CallbackResult = jsruntime.CallbackResult; const CallbackResult = jsruntime.CallbackResult;
const Case = jsruntime.test_utils.Case; const Case = jsruntime.test_utils.Case;
const checkCases = jsruntime.test_utils.checkCases; const checkCases = jsruntime.test_utils.checkCases;
const generate = @import("../generate.zig");
const NodeUnion = @import("node.zig").Union; const NodeUnion = @import("node.zig").Union;
const Node = @import("node.zig").Node; const Node = @import("node.zig").Node;
@@ -36,10 +35,10 @@ const log = std.log.scoped(.nodelist);
const DOMException = @import("exceptions.zig").DOMException; const DOMException = @import("exceptions.zig").DOMException;
pub const Interfaces = generate.Tuple(.{ pub const Interfaces = .{
NodeListIterator, NodeListIterator,
NodeList, NodeList,
}); };
pub const NodeListIterator = struct { pub const NodeListIterator = struct {
pub const mem_guarantied = true; pub const mem_guarantied = true;

View File

@@ -21,7 +21,6 @@ const std = @import("std");
const jsruntime = @import("jsruntime"); const jsruntime = @import("jsruntime");
const Case = jsruntime.test_utils.Case; const Case = jsruntime.test_utils.Case;
const checkCases = jsruntime.test_utils.checkCases; const checkCases = jsruntime.test_utils.checkCases;
const generate = @import("../generate.zig");
const parser = @import("netsurf"); const parser = @import("netsurf");
@@ -31,9 +30,9 @@ const CDATASection = @import("cdata_section.zig").CDATASection;
const UserContext = @import("../user_context.zig").UserContext; const UserContext = @import("../user_context.zig").UserContext;
// Text interfaces // Text interfaces
pub const Interfaces = generate.Tuple(.{ pub const Interfaces = .{
CDATASection, CDATASection,
}); };
pub const Text = struct { pub const Text = struct {
pub const Self = parser.Text; pub const Self = parser.Text;

View File

@@ -37,12 +37,12 @@ const ProgressEvent = @import("../xhr/progress_event.zig").ProgressEvent;
const log = std.log.scoped(.events); const log = std.log.scoped(.events);
// Event interfaces // Event interfaces
pub const Interfaces = generate.Tuple(.{ pub const Interfaces = .{
Event, Event,
ProgressEvent, ProgressEvent,
}); };
const Generated = generate.Union.compile(Interfaces);
pub const Union = Generated._union; pub const Union = generate.Union(Interfaces);
// https://dom.spec.whatwg.org/#event // https://dom.spec.whatwg.org/#event
pub const Event = struct { pub const Event = struct {

View File

@@ -17,430 +17,202 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std"); const std = @import("std");
const builtin = @import("builtin"); const Type = std.builtin.Type;
// Utils pub fn Union(interfaces: anytype) type {
// ----- // @setEvalBranchQuota(10000);
const tuple = Tuple(interfaces){};
const fields = std.meta.fields(@TypeOf(tuple));
fn itoa(comptime i: u8) ![]const u8 { const tag_type = switch (fields.len) {
var len: usize = undefined; 0 => unreachable,
if (i < 10) { 1 => u0,
len = 1; 2 => u1,
} else if (i < 100) { 3...4 => u2,
len = 2; 5...8 => u3,
} else if (i < 1000) { 9...16 => u4,
len = 3; 17...32 => u5,
} else { 33...64 => u6,
return error.GenerateTooMuchMembers; 65...128 => u7,
} 129...256 => u8,
var buf: [len]u8 = undefined; else => @compileError("Too many interfaces to generate union"),
return try std.fmt.bufPrint(buf[0..], "{d}", .{i}); };
}
fn fmtName(comptime T: type) [:0]const u8 { // second iteration to generate tags
var it = std.mem.splitBackwards(u8, @typeName(T), "."); var enum_fields: [fields.len]Type.EnumField = undefined;
return it.first() ++ ""; for (fields, 0..) |field, index| {
} const member = @field(tuple, field.name);
const full_name = @typeName(member);
// Union const separator = std.mem.lastIndexOfScalar(u8, full_name, '.') orelse unreachable;
// ----- const name = full_name[separator + 1 ..];
enum_fields[index] = .{
// Generate a flatten tagged Union from various structs and union of structs .name = name ++ "",
// TODO: make this function more generic .value = index,
// 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));
}
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.GenerateArgNotTuple;
}
const tuple_members = tuple_info.Struct.fields;
// 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;
}
}
// 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;
}
// 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;
}
}
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,
}; };
} }
};
// Tuple 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 });
fn tupleNb(comptime tuple: anytype) usize { // third iteration to generate union type
var nb = 0; var union_fields: [fields.len]Type.UnionField = undefined;
for (@typeInfo(@TypeOf(tuple)).Struct.fields) |member| { for (fields, enum_fields, 0..) |field, e, index| {
const member_T = @field(tuple, member.name); var FT = @field(tuple, field.name);
if (@TypeOf(member_T) == type) { if (@hasDecl(FT, "Self")) {
nb += 1; FT = *(@field(FT, "Self"));
} 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;
} }
union_fields[index] = .{
.type = FT,
.name = e.name,
.alignment = @alignOf(FT),
};
} }
return nb;
return @Type(.{ .Union = .{
.layout = .auto,
.tag_type = enum_T,
.fields = &union_fields,
.decls = &.{},
} });
} }
fn tupleTypes(comptime nb: usize, comptime tuple: anytype) [nb]type { pub fn Tuple(args: anytype) 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;
} 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;
}
}
return false;
}
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;
} else {
dedup_indexes[i] = i;
}
}
return dedup_indexes;
}
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;
}
}
return dedup_nb;
}
fn TupleT(comptime tuple: anytype) type {
@setEvalBranchQuota(100000); @setEvalBranchQuota(100000);
// logic const count = countInterfaces(args, 0);
const nb = tupleNb(tuple); var interfaces: [count]type = undefined;
const types = tupleTypes(nb, tuple); _ = flattenInterfaces(args, &interfaces, 0);
const dedup_indexes = dedupIndexes(nb, types);
const dedup_nb = dedupNb(nb, dedup_indexes);
// generate the tuple type const unfiltered_count, const filter_set = filterMap(count, interfaces);
var fields: [dedup_nb]std.builtin.Type.StructField = undefined;
var done = 0; var field_index: usize = 0;
for (dedup_indexes) |index| { var fields: [unfiltered_count]Type.StructField = undefined;
if (index == -1) {
for (filter_set, 0..) |filter, i| {
if (filter) {
continue; continue;
} }
fields[done] = .{ fields[field_index] = .{
// StructField.name expect a null terminated string. .name = std.fmt.comptimePrint("{d}", .{field_index}),
// 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, .type = type,
.default_value = null, // has to be true in order to properly capture the default value
.is_comptime = false, .is_comptime = true,
.alignment = @alignOf(type), .alignment = @alignOf(type),
.default_value = @ptrCast(&interfaces[i]),
}; };
done += 1; field_index += 1;
} }
const decls: [0]std.builtin.Type.Declaration = undefined;
const info = std.builtin.Type.Struct{ return @Type(.{ .Struct = .{
.layout = .auto, .layout = .auto,
.fields = &fields, .fields = &fields,
.decls = &decls, .decls = &.{},
.is_tuple = true, .is_tuple = true,
}; } });
return @Type(std.builtin.Type{ .Struct = info });
} }
// Create a flatten tuple from various structs and tuple of structs fn countInterfaces(args: anytype, count: usize) usize {
// Duplicates will be removed. var new_count = count;
// TODO: make this function more generic for (@typeInfo(@TypeOf(args)).Struct.fields) |f| {
pub fn Tuple(comptime tuple: anytype) TupleT(tuple) { const member = @field(args, f.name);
if (@TypeOf(member) == type) {
// check types provided new_count += 1;
const tuple_T = @TypeOf(tuple); } else {
const tuple_info = @typeInfo(tuple_T); new_count = countInterfaces(member, new_count);
if (tuple_info != .Struct or !tuple_info.Struct.is_tuple) {
@compileError("GenerateArgNotTuple");
}
// generate the type
const T = TupleT(tuple);
// logic
const nb = tupleNb(tuple);
const types = tupleTypes(nb, tuple);
const dedup_indexes = dedupIndexes(nb, types);
// 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; return new_count;
} }
// Tests fn flattenInterfaces(args: anytype, interfaces: []type, index: usize) usize {
// ----- var new_index = index;
for (@typeInfo(@TypeOf(args)).Struct.fields) |f| {
const Error = error{ const member = @field(args, f.name);
GenerateArgNotTuple, if (@TypeOf(member) == type) {
GenerateMemberNotUnionOrStruct, interfaces[new_index] = member;
GenerateMemberNotTupleOrStruct, new_index += 1;
GenerateMemberTupleNotStruct, } else {
GenerateTooMuchMembers, new_index = flattenInterfaces(member, interfaces, new_index);
}; }
}
const Astruct = struct { return new_index;
value: u8 = 0, }
};
const Bstruct = struct { fn filterMap(comptime count: usize, interfaces: [count]type) struct { usize, [count]bool } {
value: u8 = 0, var map: [count]bool = undefined;
}; var unfiltered_count: usize = 0;
const Cstruct = struct { outer: for (interfaces, 0..) |iface, i| {
value: u8 = 0, for (interfaces[i + 1 ..]) |check| {
}; if (iface == check) {
const Dstruct = struct { map[i] = true;
value: u8 = 0, continue :outer;
}; }
}
pub fn tests() !void { map[i] = false;
unfiltered_count += 1;
// Union from structs }
const FromStructs = try Union.private_compile(.{ Astruct, Bstruct, Cstruct }); return .{ unfiltered_count, map };
}
const from_structs_enum = @typeInfo(FromStructs._enum);
try std.testing.expect(from_structs_enum == .Enum); test "generate.Union" {
try std.testing.expect(from_structs_enum.Enum.fields.len == 3); const Astruct = struct {
try std.testing.expect(from_structs_enum.Enum.tag_type == u2); pub const Self = Other;
try std.testing.expect(from_structs_enum.Enum.fields[0].value == 0); const Other = struct {};
try std.testing.expectEqualStrings(from_structs_enum.Enum.fields[0].name, "Astruct"); };
const from_structs_union = @typeInfo(FromStructs._union); const Bstruct = struct {
try std.testing.expect(from_structs_union == .Union); value: u8 = 0,
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); const Cstruct = struct {
try std.testing.expectEqualStrings(from_structs_union.Union.fields[0].name, "Astruct"); value: u8 = 0,
};
// Union from union and structs
const FromMix = try Union.private_compile(.{ FromStructs._union, Dstruct }); const value = Union(.{ Astruct, Bstruct, .{ Cstruct } });
const ti = @typeInfo(value).Union;
const from_mix_enum = @typeInfo(FromMix._enum); try std.testing.expectEqual(3, ti.fields.len);
try std.testing.expect(from_mix_enum == .Enum); try std.testing.expectEqualStrings("*generate.test.generate.Union.Astruct.Other", @typeName(ti.fields[0].type));
try std.testing.expect(from_mix_enum.Enum.fields.len == 4); try std.testing.expectEqualStrings(ti.fields[0].name, "Astruct");
try std.testing.expect(from_mix_enum.Enum.tag_type == u3); try std.testing.expectEqual(Bstruct, ti.fields[1].type);
try std.testing.expect(from_mix_enum.Enum.fields[0].value == 0); try std.testing.expectEqualStrings(ti.fields[1].name, "Bstruct");
try std.testing.expectEqualStrings(from_mix_enum.Enum.fields[3].name, "Dstruct"); try std.testing.expectEqual(Cstruct, ti.fields[2].type);
try std.testing.expectEqualStrings(ti.fields[2].name, "Cstruct");
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); test "generate.Tuple" {
try std.testing.expect(from_mix_union.Union.fields.len == 4); const Astruct = struct {
try std.testing.expect(from_mix_union.Union.fields[3].type == Dstruct); };
try std.testing.expectEqualStrings(from_mix_union.Union.fields[3].name, "Dstruct");
const Bstruct = struct {
std.debug.print("Generate Union: OK\n", .{}); value: u8 = 0,
};
// Tuple from structs
const tuple_structs = .{ Astruct, Bstruct }; const Cstruct = struct {
const tFromStructs = Tuple(tuple_structs); value: u8 = 0,
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); const tuple = Tuple(.{ Astruct, Bstruct }){};
try std.testing.expect(@field(tFromStructs, "0") == Astruct); const ti = @typeInfo(@TypeOf(tuple)).Struct;
try std.testing.expect(@field(tFromStructs, "1") == Bstruct); try std.testing.expectEqual(true, ti.is_tuple);
try std.testing.expectEqual(2, ti.fields.len);
// Tuple from tuple and structs try std.testing.expectEqual(Astruct, tuple.@"0");
const tuple_mix = .{ tFromStructs, Cstruct }; try std.testing.expectEqual(Bstruct, tuple.@"1");
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); // dedupe
try std.testing.expect(t_from_mix.Struct.fields.len == 3); const tuple = Tuple(.{ Cstruct, Astruct, .{ Astruct }, Bstruct, .{ Astruct, .{ Astruct, Bstruct } } }){};
try std.testing.expect(@field(tFromMix, "0") == Astruct); const ti = @typeInfo(@TypeOf(tuple)).Struct;
try std.testing.expect(@field(tFromMix, "1") == Bstruct); try std.testing.expectEqual(true, ti.is_tuple);
try std.testing.expect(@field(tFromMix, "2") == Cstruct); try std.testing.expectEqual(3, ti.fields.len);
try std.testing.expectEqual(Cstruct, tuple.@"0");
// Tuple with dedup try std.testing.expectEqual(Astruct, tuple.@"1");
const tuple_dedup = .{ Cstruct, Astruct, tFromStructs, Bstruct }; try std.testing.expectEqual(Bstruct, tuple.@"2");
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);
std.debug.print("Generate Tuple: OK\n", .{});
} }

View File

@@ -99,9 +99,8 @@ pub const Interfaces = .{
HTMLVideoElement, HTMLVideoElement,
CSSProperties, CSSProperties,
}; };
const Generated = generate.Union.compile(Interfaces);
pub const Union = Generated._union; pub const Union = generate.Union(Interfaces);
pub const Tags = Generated._enum;
// Abstract class // Abstract class
// -------------- // --------------

View File

@@ -25,7 +25,7 @@ const Navigator = @import("navigator.zig").Navigator;
const History = @import("history.zig").History; const History = @import("history.zig").History;
const Location = @import("location.zig").Location; const Location = @import("location.zig").Location;
pub const Interfaces = generate.Tuple(.{ pub const Interfaces = .{
HTMLDocument, HTMLDocument,
HTMLElem.HTMLElement, HTMLElem.HTMLElement,
HTMLElem.HTMLMediaElement, HTMLElem.HTMLMediaElement,
@@ -34,4 +34,4 @@ pub const Interfaces = generate.Tuple(.{
Navigator, Navigator,
History, History,
Location, Location,
}); };

View File

@@ -1,10 +1,8 @@
const std = @import("std"); const std = @import("std");
const generate = @import("../generate.zig"); pub const Interfaces = .{
pub const Interfaces = generate.Tuple(.{
U32Iterator, U32Iterator,
}); };
pub const U32Iterator = struct { pub const U32Iterator = struct {
pub const mem_guarantied = true; pub const mem_guarantied = true;

View File

@@ -332,13 +332,11 @@ test {
const queryTest = @import("url/query.zig"); const queryTest = @import("url/query.zig");
std.testing.refAllDecls(queryTest); std.testing.refAllDecls(queryTest);
std.testing.refAllDecls(@import("generate.zig"));
std.testing.refAllDecls(@import("cdp/msg.zig")); std.testing.refAllDecls(@import("cdp/msg.zig"));
} }
fn testJSRuntime(alloc: std.mem.Allocator) !void { fn testJSRuntime(alloc: std.mem.Allocator) !void {
// generate tests
try generate.tests();
// create JS vm // create JS vm
const vm = jsruntime.VM.init(); const vm = jsruntime.VM.init();
defer vm.deinit(); defer vm.deinit();

View File

@@ -21,15 +21,13 @@ const std = @import("std");
const jsruntime = @import("jsruntime"); const jsruntime = @import("jsruntime");
const Case = jsruntime.test_utils.Case; const Case = jsruntime.test_utils.Case;
const checkCases = jsruntime.test_utils.checkCases; const checkCases = jsruntime.test_utils.checkCases;
const generate = @import("../generate.zig");
const DOMError = @import("netsurf").DOMError; const DOMError = @import("netsurf").DOMError;
const log = std.log.scoped(.storage); const log = std.log.scoped(.storage);
pub const Interfaces = generate.Tuple(.{ pub const Interfaces = .{
Bottle, Bottle,
}); };
// See https://storage.spec.whatwg.org/#model for storage hierarchy. // See https://storage.spec.whatwg.org/#model for storage hierarchy.
// A Shed contains map of Shelves. The key is the document's origin. // A Shed contains map of Shelves. The key is the document's origin.

View File

@@ -21,14 +21,13 @@ const std = @import("std");
const jsruntime = @import("jsruntime"); const jsruntime = @import("jsruntime");
const Case = jsruntime.test_utils.Case; const Case = jsruntime.test_utils.Case;
const checkCases = jsruntime.test_utils.checkCases; const checkCases = jsruntime.test_utils.checkCases;
const generate = @import("../generate.zig");
const query = @import("query.zig"); const query = @import("query.zig");
pub const Interfaces = generate.Tuple(.{ pub const Interfaces = .{
URL, URL,
URLSearchParams, URLSearchParams,
}); };
// https://url.spec.whatwg.org/#url // https://url.spec.whatwg.org/#url
// //

View File

@@ -21,7 +21,6 @@ const std = @import("std");
const jsruntime = @import("jsruntime"); const jsruntime = @import("jsruntime");
const Case = jsruntime.test_utils.Case; const Case = jsruntime.test_utils.Case;
const checkCases = jsruntime.test_utils.checkCases; const checkCases = jsruntime.test_utils.checkCases;
const generate = @import("../generate.zig");
const DOMError = @import("netsurf").DOMError; const DOMError = @import("netsurf").DOMError;
const DOMException = @import("../dom/exceptions.zig").DOMException; const DOMException = @import("../dom/exceptions.zig").DOMException;
@@ -42,11 +41,11 @@ const log = std.log.scoped(.xhr);
// XHR interfaces // XHR interfaces
// https://xhr.spec.whatwg.org/#interface-xmlhttprequest // https://xhr.spec.whatwg.org/#interface-xmlhttprequest
pub const Interfaces = generate.Tuple(.{ pub const Interfaces = .{
XMLHttpRequestEventTarget, XMLHttpRequestEventTarget,
XMLHttpRequestUpload, XMLHttpRequestUpload,
XMLHttpRequest, XMLHttpRequest,
}); };
pub const XMLHttpRequestUpload = struct { pub const XMLHttpRequestUpload = struct {
pub const prototype = *XMLHttpRequestEventTarget; pub const prototype = *XMLHttpRequestEventTarget;

View File

@@ -21,16 +21,15 @@ const std = @import("std");
const jsruntime = @import("jsruntime"); const jsruntime = @import("jsruntime");
const Case = jsruntime.test_utils.Case; const Case = jsruntime.test_utils.Case;
const checkCases = jsruntime.test_utils.checkCases; const checkCases = jsruntime.test_utils.checkCases;
const generate = @import("../generate.zig");
const DOMError = @import("netsurf").DOMError; const DOMError = @import("netsurf").DOMError;
const parser = @import("netsurf"); const parser = @import("netsurf");
const dump = @import("../browser/dump.zig"); const dump = @import("../browser/dump.zig");
pub const Interfaces = generate.Tuple(.{ pub const Interfaces = .{
XMLSerializer, XMLSerializer,
}); };
// https://w3c.github.io/DOM-Parsing/#dom-xmlserializer-constructor // https://w3c.github.io/DOM-Parsing/#dom-xmlserializer-constructor
pub const XMLSerializer = struct { pub const XMLSerializer = struct {