From fee955c50bedfe9f503aad018d9bf56fad7c0220 Mon Sep 17 00:00:00 2001 From: Nylvon Date: Thu, 16 Nov 2023 01:20:31 +0200 Subject: [PATCH 01/16] Split project into regions. Begin work on interfaces. --- src/common.zig | 187 +++++++++++++++++++++++++++++++++++++++++++++ src/interface.zig | 7 ++ src/metaplasia.zig | 182 +------------------------------------------ src/tests.zig | 115 ++++++++++++++-------------- 4 files changed, 253 insertions(+), 238 deletions(-) create mode 100644 src/common.zig create mode 100644 src/interface.zig diff --git a/src/common.zig b/src/common.zig new file mode 100644 index 0000000..b2df94f --- /dev/null +++ b/src/common.zig @@ -0,0 +1,187 @@ +/// Metaplasia Common Utils +/// ©2023 Cristian Vasilache (NNCV / Nylvon) +/// +/// Common functionality shared across the project. +pub const Common = @This(); +const std = @import("std"); + +/// Restricts look-up to only fields or declarations, or does not restrict at all. +/// If Any is selected, fields are prioritised over declarations in the look-up order. +pub const LookupMode = enum { Field, Declaration, Any }; + +pub const LookupError = error{ + InvalidType, + NotFound, + TypeHasNoFields, + TypeHasNoDeclarations, + TypeHasNoFieldsOrDeclarations, +}; + +/// Internal use only. Looks up a field from a type-info struct. +inline fn UnsafeFieldLookup(comptime type_info: anytype, comptime lookup_name: []const u8) LookupError!type { + comptime { + if (type_info.fields.len == 0) return LookupError.TypeHasNoFields; + + for (type_info.fields) |field| { + if (std.mem.eql(u8, field.name, lookup_name)) return field.type; + } + + // If we're here, we haven't found it + return LookupError.NotFound; + } +} + +/// Internal use only. Looks up a declaration from a type-info struct. +inline fn UnsafeDeclarationLookup(comptime from: type, comptime type_info: anytype, comptime lookup_name: []const u8) LookupError!type { + comptime { + if (type_info.decls.len == 0) return LookupError.TypeHasNoDeclarations; + + for (type_info.decls) |decl| { + if (std.mem.eql(u8, decl.name, lookup_name)) return @TypeOf(@field(from, lookup_name)); + } + + // If we're here, we haven't found it + return LookupError.NotFound; + } +} + +/// Internal use only. Looks up a field or declaraton from a type-info struct. +inline fn UnsafeAnyLookup(comptime from: type, comptime type_info: anytype, comptime lookup_name: []const u8) LookupError!type { + comptime { + if (type_info.fields.len == 0 and type_info.decls.len == 0) return LookupError.TypeHasNoFieldsOrDeclarations; + + for (type_info.fields) |field| { + if (std.mem.eql(u8, field.name, lookup_name)) return field.type; + } + + for (type_info.decls) |decl| { + if (std.mem.eql(u8, decl.name, lookup_name)) return @TypeOf(@field(from, lookup_name)); + } + + // If we're here, we haven't found it + return LookupError.NotFound; + } +} + +// Internal use only. Creates a tuple type for the enum look-up result (used for .Field and .Any lookups on enums) +pub fn EnumLookupResult(comptime from: type) type { + return struct { field: ?from, decl: ?type }; +} + +/// Internal use only. Looks up a field from a type-info struct. Used for enums +inline fn UnsafeEnumFieldLookup(comptime from: type, comptime type_info: anytype, comptime lookup_name: []const u8) LookupError!from { + comptime { + if (type_info.fields.len == 0) return LookupError.TypeHasNoFields; + + for (type_info.fields) |field| { + if (std.mem.eql(u8, field.name, lookup_name)) return @enumFromInt(field.value); + } + + // If we're here, we haven't found it + return LookupError.NotFound; + } +} + +/// Internal use only. Looks up a field or declaraton from a type-info struct. Used for enums. +inline fn UnsafeEnumAnyLookup(comptime from: type, comptime type_info: anytype, comptime lookup_name: []const u8) LookupError!EnumLookupResult(from) { + comptime { + const ResultType = EnumLookupResult(from); + + if (type_info.fields.len == 0 and type_info.decls.len == 0) return LookupError.TypeHasNoFieldsOrDeclarations; + + for (type_info.fields) |field| { + if (std.mem.eql(u8, field.name, lookup_name)) return ResultType{ .field = @enumFromInt(field.value), .decl = null }; + } + + for (type_info.decls) |decl| { + if (std.mem.eql(u8, decl.name, lookup_name)) return ResultType{ .field = null, .decl = @TypeOf(@field(from, lookup_name)) }; + } + + // If we're here, we haven't found it + return LookupError.NotFound; + } +} + +/// Internal use only. Looks up parameters from structs and unions. +inline fn UnsafeLookup(comptime from: type, comptime type_info: anytype, comptime lookup_name: []const u8, comptime lookup_mode: LookupMode) LookupError!type { + comptime { + switch (lookup_mode) { + // Search for a field + .Field => { + return UnsafeFieldLookup(type_info, lookup_name); + }, + // Search for a declaration + .Declaration => { + return UnsafeDeclarationLookup(from, type_info, lookup_name); + }, + // Search for either + .Any => { + return UnsafeAnyLookup(from, type_info, lookup_name); + }, + } + } +} + +/// Internal use only. Looks up parameters from enums. +inline fn UnsafeEnumLookup(comptime from: type, comptime type_info: anytype, comptime lookup_name: []const u8, comptime lookup_mode: LookupMode) switch (lookup_mode) { + .Field => LookupError!from, + .Declaration => LookupError!type, + .Any => LookupError!EnumLookupResult(from), +} { + comptime { + switch (lookup_mode) { + // Search for a field + .Field => { + return UnsafeEnumFieldLookup(from, type_info, lookup_name); + }, + // Search for a declaration + .Declaration => { + return UnsafeDeclarationLookup(from, type_info, lookup_name); + }, + // Search for either + .Any => { + return UnsafeEnumAnyLookup(from, type_info, lookup_name); + }, + } + } +} + +/// Tries to find a field or declaration given a name and a specified look-up mode +/// Returns the type of the field/declaration if found, otherwise errors with details. +pub fn FindInType(comptime from: type, comptime lookup_name: []const u8, comptime lookup_mode: LookupMode) switch (@typeInfo(from)) { + .Enum => switch (lookup_mode) { + .Field => LookupError!from, + .Declaration => LookupError!type, + .Any => LookupError!EnumLookupResult(from), + }, + else => LookupError!type, +} { + switch (@typeInfo(from)) { + // Structs can have both fields and declarations + .Struct => |struct_info| { + return UnsafeLookup(from, struct_info, lookup_name, lookup_mode); + }, + .Union => |uni_info| { + return UnsafeLookup(from, uni_info, lookup_name, lookup_mode); + }, + .Enum => |enu_info| { + return UnsafeEnumLookup(from, enu_info, lookup_name, lookup_mode); + }, + .Pointer => |ptr_info| { + return FindInType(ptr_info.child, lookup_name, lookup_mode); + }, + .Vector => |vec_info| { + return FindInType(vec_info.child, lookup_name, lookup_mode); + }, + .Optional => |opt_info| { + return FindInType(opt_info.child, lookup_name, lookup_mode); + }, + .Array => |arr_info| { + return FindInType(arr_info.child, lookup_name, lookup_mode); + }, + .ErrorUnion => |eru_info| { + return FindInType(eru_info.payload, lookup_name, lookup_mode); + }, + else => return LookupError.InvalidType, + } +} diff --git a/src/interface.zig b/src/interface.zig new file mode 100644 index 0000000..6901e67 --- /dev/null +++ b/src/interface.zig @@ -0,0 +1,7 @@ +/// Metaplasia Interface +/// ©2023 Cristian Vasilache (NNCV / Nylvon) +/// +/// TODO: Write description here. +pub const Interface = @This(); +const Common = @import("common.zig"); +const std = @import("std"); diff --git a/src/metaplasia.zig b/src/metaplasia.zig index 67dc642..67a4331 100644 --- a/src/metaplasia.zig +++ b/src/metaplasia.zig @@ -5,185 +5,5 @@ /// easy reflection, type merging and other techniques of /// type mangling. pub const Metaplasia = @This(); +pub const Common = @import("common.zig"); const std = @import("std"); - -/// Restricts look-up to only fields or declarations, or does not restrict at all. -/// If Any is selected, fields are prioritised over declarations in the look-up order. -pub const LookupMode = enum { Field, Declaration, Any }; - -pub const LookupError = error{ - InvalidType, - NotFound, - TypeHasNoFields, - TypeHasNoDeclarations, - TypeHasNoFieldsOrDeclarations, -}; - -/// Internal use only. Looks up a field from a type-info struct. -inline fn UnsafeFieldLookup(comptime type_info: anytype, comptime lookup_name: []const u8) LookupError!type { - comptime { - if (type_info.fields.len == 0) return LookupError.TypeHasNoFields; - - for (type_info.fields) |field| { - if (std.mem.eql(u8, field.name, lookup_name)) return field.type; - } - - // If we're here, we haven't found it - return LookupError.NotFound; - } -} - -/// Internal use only. Looks up a declaration from a type-info struct. -inline fn UnsafeDeclarationLookup(comptime from: type, comptime type_info: anytype, comptime lookup_name: []const u8) LookupError!type { - comptime { - if (type_info.decls.len == 0) return LookupError.TypeHasNoDeclarations; - - for (type_info.decls) |decl| { - if (std.mem.eql(u8, decl.name, lookup_name)) return @TypeOf(@field(from, lookup_name)); - } - - // If we're here, we haven't found it - return LookupError.NotFound; - } -} - -/// Internal use only. Looks up a field or declaraton from a type-info struct. -inline fn UnsafeAnyLookup(comptime from: type, comptime type_info: anytype, comptime lookup_name: []const u8) LookupError!type { - comptime { - if (type_info.fields.len == 0 and type_info.decls.len == 0) return LookupError.TypeHasNoFieldsOrDeclarations; - - for (type_info.fields) |field| { - if (std.mem.eql(u8, field.name, lookup_name)) return field.type; - } - - for (type_info.decls) |decl| { - if (std.mem.eql(u8, decl.name, lookup_name)) return @TypeOf(@field(from, lookup_name)); - } - - // If we're here, we haven't found it - return LookupError.NotFound; - } -} - -// Internal use only. Creates a tuple type for the enum look-up result (used for .Field and .Any lookups on enums) -pub fn EnumLookupResult(comptime from: type) type { - return struct { field: ?from, decl: ?type }; -} - -/// Internal use only. Looks up a field from a type-info struct. Used for enums -inline fn UnsafeEnumFieldLookup(comptime from: type, comptime type_info: anytype, comptime lookup_name: []const u8) LookupError!from { - comptime { - if (type_info.fields.len == 0) return LookupError.TypeHasNoFields; - - for (type_info.fields) |field| { - if (std.mem.eql(u8, field.name, lookup_name)) return @enumFromInt(field.value); - } - - // If we're here, we haven't found it - return LookupError.NotFound; - } -} - -/// Internal use only. Looks up a field or declaraton from a type-info struct. Used for enums. -inline fn UnsafeEnumAnyLookup(comptime from: type, comptime type_info: anytype, comptime lookup_name: []const u8) LookupError!EnumLookupResult(from) { - comptime { - const ResultType = EnumLookupResult(from); - - if (type_info.fields.len == 0 and type_info.decls.len == 0) return LookupError.TypeHasNoFieldsOrDeclarations; - - for (type_info.fields) |field| { - if (std.mem.eql(u8, field.name, lookup_name)) return ResultType{ .field = @enumFromInt(field.value), .decl = null }; - } - - for (type_info.decls) |decl| { - if (std.mem.eql(u8, decl.name, lookup_name)) return ResultType{ .field = null, .decl = @TypeOf(@field(from, lookup_name)) }; - } - - // If we're here, we haven't found it - return LookupError.NotFound; - } -} - -/// Internal use only. Looks up parameters from structs and unions. -inline fn UnsafeLookup(comptime from: type, comptime type_info: anytype, comptime lookup_name: []const u8, comptime lookup_mode: LookupMode) LookupError!type { - comptime { - switch (lookup_mode) { - // Search for a field - .Field => { - return UnsafeFieldLookup(type_info, lookup_name); - }, - // Search for a declaration - .Declaration => { - return UnsafeDeclarationLookup(from, type_info, lookup_name); - }, - // Search for either - .Any => { - return UnsafeAnyLookup(from, type_info, lookup_name); - }, - } - } -} - -/// Internal use only. Looks up parameters from enums. -inline fn UnsafeEnumLookup(comptime from: type, comptime type_info: anytype, comptime lookup_name: []const u8, comptime lookup_mode: LookupMode) switch (lookup_mode) { - .Field => LookupError!from, - .Declaration => LookupError!type, - .Any => LookupError!EnumLookupResult(from), -} { - comptime { - switch (lookup_mode) { - // Search for a field - .Field => { - return UnsafeEnumFieldLookup(from, type_info, lookup_name); - }, - // Search for a declaration - .Declaration => { - return UnsafeDeclarationLookup(from, type_info, lookup_name); - }, - // Search for either - .Any => { - return UnsafeEnumAnyLookup(from, type_info, lookup_name); - }, - } - } -} - -/// Tries to find a field or declaration given a name and a specified look-up mode -/// Returns the type of the field/declaration if found, otherwise errors with details. -pub fn FindInType(comptime from: type, comptime lookup_name: []const u8, comptime lookup_mode: LookupMode) switch (@typeInfo(from)) { - .Enum => switch (lookup_mode) { - .Field => LookupError!from, - .Declaration => LookupError!type, - .Any => LookupError!EnumLookupResult(from), - }, - else => LookupError!type, -} { - switch (@typeInfo(from)) { - // Structs can have both fields and declarations - .Struct => |struct_info| { - return UnsafeLookup(from, struct_info, lookup_name, lookup_mode); - }, - .Union => |uni_info| { - return UnsafeLookup(from, uni_info, lookup_name, lookup_mode); - }, - .Enum => |enu_info| { - return UnsafeEnumLookup(from, enu_info, lookup_name, lookup_mode); - }, - .Pointer => |ptr_info| { - return FindInType(ptr_info.child, lookup_name, lookup_mode); - }, - .Vector => |vec_info| { - return FindInType(vec_info.child, lookup_name, lookup_mode); - }, - .Optional => |opt_info| { - return FindInType(opt_info.child, lookup_name, lookup_mode); - }, - .Array => |arr_info| { - return FindInType(arr_info.child, lookup_name, lookup_mode); - }, - .ErrorUnion => |eru_info| { - return FindInType(eru_info.payload, lookup_name, lookup_mode); - }, - else => return LookupError.InvalidType, - } -} diff --git a/src/tests.zig b/src/tests.zig index b094dc9..d770e23 100644 --- a/src/tests.zig +++ b/src/tests.zig @@ -5,8 +5,9 @@ /// Each test name is formatted as follows: /// "[FeatureName] [Scenario]" pub const MetaplasiaTests = @This(); -pub const Metaplasia = @import("metaplasia.zig"); -const LookupError = Metaplasia.LookupError; +const Metaplasia = @import("metaplasia.zig"); +const Common = Metaplasia.Common; +const LookupError = Common.LookupError; const std = @import("std"); const testing = std.testing; const expect = testing.expect; @@ -23,31 +24,31 @@ test "FindInType Struct" { // Try to find the field // First two should not error, the third should error - const field_type = try Metaplasia.FindInType(sample_type, "a", .Field); + const field_type = try Common.FindInType(sample_type, "a", .Field); try expect(field_type == i32); - const field_type_any = try Metaplasia.FindInType(sample_type, "a", .Any); + const field_type_any = try Common.FindInType(sample_type, "a", .Any); try expect(field_type_any == i32); - const field_error = Metaplasia.FindInType(sample_type, "a", .Declaration); + const field_error = Common.FindInType(sample_type, "a", .Declaration); try expectError(LookupError.NotFound, field_error); // Try to find the declaration // First two should not error, the third should error - const decl_type = try Metaplasia.FindInType(sample_type, "b", .Declaration); + const decl_type = try Common.FindInType(sample_type, "b", .Declaration); try expect(decl_type == i64); - const decl_type_any = try Metaplasia.FindInType(sample_type, "b", .Any); + const decl_type_any = try Common.FindInType(sample_type, "b", .Any); try expect(decl_type_any == i64); - const decl_error = Metaplasia.FindInType(sample_type, "b", .Field); + const decl_error = Common.FindInType(sample_type, "b", .Field); try expectError(LookupError.NotFound, decl_error); } test "FindInType Struct Empty" { const sample_type = struct {}; - const field_error = Metaplasia.FindInType(sample_type, "a", .Field); + const field_error = Common.FindInType(sample_type, "a", .Field); try expectError(LookupError.TypeHasNoFields, field_error); - const decl_error = Metaplasia.FindInType(sample_type, "a", .Declaration); + const decl_error = Common.FindInType(sample_type, "a", .Declaration); try expectError(LookupError.TypeHasNoDeclarations, decl_error); - const any_error = Metaplasia.FindInType(sample_type, "a", .Any); + const any_error = Common.FindInType(sample_type, "a", .Any); try expectError(LookupError.TypeHasNoFieldsOrDeclarations, any_error); } @@ -59,31 +60,31 @@ test "FindInType Union" { // Try to find the field // First two should not error, the third should error - const field_type = try Metaplasia.FindInType(sample_type, "a", .Field); + const field_type = try Common.FindInType(sample_type, "a", .Field); try expect(field_type == i32); - const field_type_any = try Metaplasia.FindInType(sample_type, "a", .Any); + const field_type_any = try Common.FindInType(sample_type, "a", .Any); try expect(field_type_any == i32); - const field_error = Metaplasia.FindInType(sample_type, "a", .Declaration); + const field_error = Common.FindInType(sample_type, "a", .Declaration); try expectError(LookupError.NotFound, field_error); // Try to find the declaration // First two should not error, the third should error - const decl_type = try Metaplasia.FindInType(sample_type, "b", .Declaration); + const decl_type = try Common.FindInType(sample_type, "b", .Declaration); try expect(decl_type == i64); - const decl_type_any = try Metaplasia.FindInType(sample_type, "b", .Any); + const decl_type_any = try Common.FindInType(sample_type, "b", .Any); try expect(decl_type_any == i64); - const decl_error = Metaplasia.FindInType(sample_type, "b", .Field); + const decl_error = Common.FindInType(sample_type, "b", .Field); try expectError(LookupError.NotFound, decl_error); } test "FindInType Union Empty" { const sample_type = union {}; - const field_error = Metaplasia.FindInType(sample_type, "a", .Field); + const field_error = Common.FindInType(sample_type, "a", .Field); try expectError(LookupError.TypeHasNoFields, field_error); - const decl_error = Metaplasia.FindInType(sample_type, "a", .Declaration); + const decl_error = Common.FindInType(sample_type, "a", .Declaration); try expectError(LookupError.TypeHasNoDeclarations, decl_error); - const any_error = Metaplasia.FindInType(sample_type, "a", .Any); + const any_error = Common.FindInType(sample_type, "a", .Any); try expectError(LookupError.TypeHasNoFieldsOrDeclarations, any_error); } @@ -95,20 +96,20 @@ test "FindInType Enum" { // Try to find the field // First two should not error, the third should error - const field_type = try Metaplasia.FindInType(sample_type, "a", .Field); + const field_type = try Common.FindInType(sample_type, "a", .Field); try expect(field_type == sample_type.a); - const field_type_any = try Metaplasia.FindInType(sample_type, "a", .Any); + const field_type_any = try Common.FindInType(sample_type, "a", .Any); try expect(field_type_any.field == sample_type.a); - const field_error = Metaplasia.FindInType(sample_type, "a", .Declaration); + const field_error = Common.FindInType(sample_type, "a", .Declaration); try expectError(LookupError.NotFound, field_error); // Try to find the declaration // First two should not error, the third should error - const decl_type = try Metaplasia.FindInType(sample_type, "b", .Declaration); + const decl_type = try Common.FindInType(sample_type, "b", .Declaration); try expect(decl_type == i64); - const decl_type_any = try Metaplasia.FindInType(sample_type, "b", .Any); + const decl_type_any = try Common.FindInType(sample_type, "b", .Any); try expect(decl_type_any.decl == i64); - const decl_error = Metaplasia.FindInType(sample_type, "b", .Field); + const decl_error = Common.FindInType(sample_type, "b", .Field); try expectError(LookupError.NotFound, decl_error); } @@ -116,7 +117,7 @@ test "FindInType Enum Empty" { const sample_type_a = enum { a }; // Enums cannot be empty (for now; due to an error in std.fmt:527:46 (@tagName of empty enum is impossible)) - const decl_error = Metaplasia.FindInType(sample_type_a, "a", .Declaration); + const decl_error = Common.FindInType(sample_type_a, "a", .Declaration); try expectError(LookupError.TypeHasNoDeclarations, decl_error); } @@ -130,20 +131,20 @@ test "FindInType Pointer" { // Try to find the field // First two should not error, the third should error - const field_type = try Metaplasia.FindInType(target_type, "a", .Field); + const field_type = try Common.FindInType(target_type, "a", .Field); try expect(field_type == i32); - const field_type_any = try Metaplasia.FindInType(target_type, "a", .Any); + const field_type_any = try Common.FindInType(target_type, "a", .Any); try expect(field_type_any == i32); - const field_error = Metaplasia.FindInType(target_type, "a", .Declaration); + const field_error = Common.FindInType(target_type, "a", .Declaration); try expectError(LookupError.NotFound, field_error); // Try to find the declaration // First two should not error, the third should error - const decl_type = try Metaplasia.FindInType(target_type, "b", .Declaration); + const decl_type = try Common.FindInType(target_type, "b", .Declaration); try expect(decl_type == i64); - const decl_type_any = try Metaplasia.FindInType(target_type, "b", .Any); + const decl_type_any = try Common.FindInType(target_type, "b", .Any); try expect(decl_type_any == i64); - const decl_error = Metaplasia.FindInType(target_type, "b", .Field); + const decl_error = Common.FindInType(target_type, "b", .Field); try expectError(LookupError.NotFound, decl_error); } @@ -157,20 +158,20 @@ test "FindInType Optional" { // Try to find the field // First two should not error, the third should error - const field_type = try Metaplasia.FindInType(target_type, "a", .Field); + const field_type = try Common.FindInType(target_type, "a", .Field); try expect(field_type == i32); - const field_type_any = try Metaplasia.FindInType(target_type, "a", .Any); + const field_type_any = try Common.FindInType(target_type, "a", .Any); try expect(field_type_any == i32); - const field_error = Metaplasia.FindInType(target_type, "a", .Declaration); + const field_error = Common.FindInType(target_type, "a", .Declaration); try expectError(LookupError.NotFound, field_error); // Try to find the declaration // First two should not error, the third should error - const decl_type = try Metaplasia.FindInType(target_type, "b", .Declaration); + const decl_type = try Common.FindInType(target_type, "b", .Declaration); try expect(decl_type == i64); - const decl_type_any = try Metaplasia.FindInType(target_type, "b", .Any); + const decl_type_any = try Common.FindInType(target_type, "b", .Any); try expect(decl_type_any == i64); - const decl_error = Metaplasia.FindInType(target_type, "b", .Field); + const decl_error = Common.FindInType(target_type, "b", .Field); try expectError(LookupError.NotFound, decl_error); } @@ -184,20 +185,20 @@ test "FindInType Error Union" { // Try to find the field // First two should not error, the third should error - const field_type = try Metaplasia.FindInType(target_type, "a", .Field); + const field_type = try Common.FindInType(target_type, "a", .Field); try expect(field_type == i32); - const field_type_any = try Metaplasia.FindInType(target_type, "a", .Any); + const field_type_any = try Common.FindInType(target_type, "a", .Any); try expect(field_type_any == i32); - const field_error = Metaplasia.FindInType(target_type, "a", .Declaration); + const field_error = Common.FindInType(target_type, "a", .Declaration); try expectError(LookupError.NotFound, field_error); // Try to find the declaration // First two should not error, the third should error - const decl_type = try Metaplasia.FindInType(target_type, "b", .Declaration); + const decl_type = try Common.FindInType(target_type, "b", .Declaration); try expect(decl_type == i64); - const decl_type_any = try Metaplasia.FindInType(target_type, "b", .Any); + const decl_type_any = try Common.FindInType(target_type, "b", .Any); try expect(decl_type_any == i64); - const decl_error = Metaplasia.FindInType(target_type, "b", .Field); + const decl_error = Common.FindInType(target_type, "b", .Field); try expectError(LookupError.NotFound, decl_error); } @@ -211,20 +212,20 @@ test "FindInType Vector" { // Try to find the field // First two should not error, the third should error - const field_type = try Metaplasia.FindInType(target_type, "a", .Field); + const field_type = try Common.FindInType(target_type, "a", .Field); try expect(field_type == i32); - const field_type_any = try Metaplasia.FindInType(target_type, "a", .Any); + const field_type_any = try Common.FindInType(target_type, "a", .Any); try expect(field_type_any == i32); - const field_error = Metaplasia.FindInType(target_type, "a", .Declaration); + const field_error = Common.FindInType(target_type, "a", .Declaration); try expectError(LookupError.NotFound, field_error); // Try to find the declaration // First two should not error, the third should error - const decl_type = try Metaplasia.FindInType(target_type, "b", .Declaration); + const decl_type = try Common.FindInType(target_type, "b", .Declaration); try expect(decl_type == i64); - const decl_type_any = try Metaplasia.FindInType(target_type, "b", .Any); + const decl_type_any = try Common.FindInType(target_type, "b", .Any); try expect(decl_type_any == i64); - const decl_error = Metaplasia.FindInType(target_type, "b", .Field); + const decl_error = Common.FindInType(target_type, "b", .Field); try expectError(LookupError.NotFound, decl_error); } @@ -238,19 +239,19 @@ test "FindInType Array" { // Try to find the field // First two should not error, the third should error - const field_type = try Metaplasia.FindInType(target_type, "a", .Field); + const field_type = try Common.FindInType(target_type, "a", .Field); try expect(field_type == i32); - const field_type_any = try Metaplasia.FindInType(target_type, "a", .Any); + const field_type_any = try Common.FindInType(target_type, "a", .Any); try expect(field_type_any == i32); - const field_error = Metaplasia.FindInType(target_type, "a", .Declaration); + const field_error = Common.FindInType(target_type, "a", .Declaration); try expectError(LookupError.NotFound, field_error); // Try to find the declaration // First two should not error, the third should error - const decl_type = try Metaplasia.FindInType(target_type, "b", .Declaration); + const decl_type = try Common.FindInType(target_type, "b", .Declaration); try expect(decl_type == i64); - const decl_type_any = try Metaplasia.FindInType(target_type, "b", .Any); + const decl_type_any = try Common.FindInType(target_type, "b", .Any); try expect(decl_type_any == i64); - const decl_error = Metaplasia.FindInType(target_type, "b", .Field); + const decl_error = Common.FindInType(target_type, "b", .Field); try expectError(LookupError.NotFound, decl_error); } From a2e6587a0a3f7e3872527fd7b8a50da5511d334d Mon Sep 17 00:00:00 2001 From: Nylvon Date: Mon, 20 Nov 2023 00:29:27 +0200 Subject: [PATCH 02/16] Begin working on rules, rulesets and interfaces. --- src/interface.zig | 19 +++++++++++++- src/metaplasia.zig | 1 + src/rule.zig | 65 ++++++++++++++++++++++++++++++++++++++++++++++ src/ruleset.zig | 30 +++++++++++++++++++++ 4 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 src/rule.zig create mode 100644 src/ruleset.zig diff --git a/src/interface.zig b/src/interface.zig index 6901e67..026bd5a 100644 --- a/src/interface.zig +++ b/src/interface.zig @@ -1,7 +1,24 @@ /// Metaplasia Interface /// ©2023 Cristian Vasilache (NNCV / Nylvon) /// -/// TODO: Write description here. +/// An interface is a set of independent rules that +/// must all be matched in order for a type to validate it. pub const Interface = @This(); const Common = @import("common.zig"); const std = @import("std"); +const RuleSet = @import("ruleset.zig").RuleSet; +const Rule = @import("rule.zig").Rule; + +/// Creates a set of independent rules based off of the array of rules +/// that have been passed as a parameter. +pub fn Make(rules: []const Rule) RuleSet { + var interface = RuleSet{ + .rules = [1]RuleSet.RuleNode{undefined} ** rules.len, + }; + + for (rules, 0..) |r, i| { + interface.rules[i].children = null; + interface.rules[i].mode = .Independent; + interface.rules[i].rule = r; + } +} diff --git a/src/metaplasia.zig b/src/metaplasia.zig index 67a4331..94e4f39 100644 --- a/src/metaplasia.zig +++ b/src/metaplasia.zig @@ -6,4 +6,5 @@ /// type mangling. pub const Metaplasia = @This(); pub const Common = @import("common.zig"); +pub const Interface = @import("interface.zig"); const std = @import("std"); diff --git a/src/rule.zig b/src/rule.zig new file mode 100644 index 0000000..f144137 --- /dev/null +++ b/src/rule.zig @@ -0,0 +1,65 @@ +/// Metaplasia Rule +/// ©2023 Cristian Vasilache (NNCV / Nylvon) +/// +/// A rule is a constraint for a type. +pub const Rule = @This(); +const Common = @import("common.zig"); +const std = @import("std"); + +/// All base rules +pub const Type = enum { + IsField, + IsDeclaration, + IsInType, // Either as a field or as a declaration + IsType, + IsVariable, + IsConstant, +}; + +/// Dictates how a rule should be enforced +pub const Strictness = enum { Required, NotRequired }; + +type: Type, +name: []const u8, +strictness: Strictness, + +/// Checks whether a type matches a rule +pub fn Check(comptime self: *const Rule, comptime target: type) !bool { + switch (self.type) { + .IsField => { + // If there's no types, it'll return false. + // If there's another error, it'll return it. + Common.FindInType(target, self.name, .Field) catch |err| { + switch (err) { + .NotFound => return false, + else => return err, + } + }; + + // If we're here, it returned the type, which implies a successful look-up. + return true; + }, + .IsDeclaration => { + Common.FindInType(target, self.name, .Declaration) catch |err| { + switch (err) { + .NotFound => return false, + else => return err, + } + }; + + return true; + }, + .IsInType => { + Common.FindInType(target, self.name, .Any) catch |err| { + switch (err) { + .NotFound => return false, + else => return err, + } + }; + + return true; + }, + else => return error.NotImplemented, // TODO: Implement the rest + // .IsConstant, .IsVariable require a different function that returns data about the field/declaration + } +} diff --git a/src/ruleset.zig b/src/ruleset.zig new file mode 100644 index 0000000..3151287 --- /dev/null +++ b/src/ruleset.zig @@ -0,0 +1,30 @@ +/// Metaplasia RuleSet +/// ©2023 Cristian Vasilache (NNCV / Nylvon) +/// +/// A ruleset is a tree of rules with a specifier dictating +/// how the rules ought to be interpreted as a whole. +/// (i.e.: Some rules may depend on some other rules, whilst other may not) +pub const RuleSet = @This(); +const Common = @import("common.zig"); +const Rule = @import("rule.zig").Rule; +const std = @import("std"); + +/// How two rules can relate to eachother inside of a ruleset +pub const Relationship = enum { + And, + Or, + Xor, + Independent, +}; + +/// A rule node is a rule and a specifier. +/// The specifier marks that +pub const RuleNode = struct { + rule: Rule, + mode: Relationship, + children: ?[]const Rule, +}; + +/// A ruleset has one or more rules, +/// Each rule can be a tree of rules. +rules: []const RuleNode, From e5fb74d12591085eda1555f841de7f9823627413 Mon Sep 17 00:00:00 2001 From: Nylvon Date: Mon, 20 Nov 2023 10:13:12 +0200 Subject: [PATCH 03/16] Change look-up behavior. Add wrapper for type look-up. --- src/common.zig | 148 +++++++---------- src/tests.zig | 444 ++++++++++++++++++++++++------------------------- 2 files changed, 286 insertions(+), 306 deletions(-) diff --git a/src/common.zig b/src/common.zig index b2df94f..e640092 100644 --- a/src/common.zig +++ b/src/common.zig @@ -17,13 +17,54 @@ pub const LookupError = error{ TypeHasNoFieldsOrDeclarations, }; +/// Contains the type that the declaration was gotten from as well as the declaration +pub const FullDeclaration = struct { + decl: std.builtin.Type.Declaration, + type: type, +}; + +/// Contains the type that the enum field was gotten from as well as the enum field +pub const FullEnumField = struct { + field: std.builtin.Type.EnumField, + type: type, +}; + +/// A Type Item is a part of a type, it is a wrapper over +/// fields and declarations of all types +pub const TypeItem = union { + // NOTE: Should be kept up to date with the language specification. + Declaration: FullDeclaration, + StructField: std.builtin.Type.StructField, + EnumField: FullEnumField, + UnionField: std.builtin.Type.UnionField, + + /// Returns the type of the type item + pub fn GetType(comptime self: @This()) type { + switch (std.meta.activeTag(self)) { + .Declaration => |d| return @TypeOf(@field(d.type, d.decl.name)), + .StructField => |sf| return sf.type, + .EnumField => |ef| return ef.type, + .UnionField => |uf| return uf.type, + } + } +}; + /// Internal use only. Looks up a field from a type-info struct. -inline fn UnsafeFieldLookup(comptime type_info: anytype, comptime lookup_name: []const u8) LookupError!type { +inline fn UnsafeFieldLookup(comptime target: type, comptime lookup_name: []const u8) LookupError!TypeItem { comptime { + const type_info = @TypeOf(target); + if (type_info.fields.len == 0) return LookupError.TypeHasNoFields; for (type_info.fields) |field| { - if (std.mem.eql(u8, field.name, lookup_name)) return field.type; + if (std.mem.eql(u8, field.name, lookup_name)) { + switch (type_info) { + .Struct => return TypeItem{ .StructField = field }, + .Enum => return TypeItem{ .EnumField = FullEnumField{ .field = field, .type = target } }, + .Union => return TypeItem{ .UnionField = field }, + else => @compileError("Please do not use this function on its own, as the checks for its usage are done outside its context!"), + } + } } // If we're here, we haven't found it @@ -32,12 +73,14 @@ inline fn UnsafeFieldLookup(comptime type_info: anytype, comptime lookup_name: [ } /// Internal use only. Looks up a declaration from a type-info struct. -inline fn UnsafeDeclarationLookup(comptime from: type, comptime type_info: anytype, comptime lookup_name: []const u8) LookupError!type { +inline fn UnsafeDeclarationLookup(comptime target: type, comptime lookup_name: []const u8) LookupError!TypeItem { comptime { + const type_info = @TypeOf(target); + if (type_info.decls.len == 0) return LookupError.TypeHasNoDeclarations; for (type_info.decls) |decl| { - if (std.mem.eql(u8, decl.name, lookup_name)) return @TypeOf(@field(from, lookup_name)); + if (std.mem.eql(u8, decl.name, lookup_name)) return TypeItem{ .Declaration = FullDeclaration{ .decl = decl, .type = type } }; } // If we're here, we haven't found it @@ -46,55 +89,23 @@ inline fn UnsafeDeclarationLookup(comptime from: type, comptime type_info: anyty } /// Internal use only. Looks up a field or declaraton from a type-info struct. -inline fn UnsafeAnyLookup(comptime from: type, comptime type_info: anytype, comptime lookup_name: []const u8) LookupError!type { +inline fn UnsafeAnyLookup(comptime type_info: anytype, comptime lookup_name: []const u8) LookupError!TypeItem { comptime { if (type_info.fields.len == 0 and type_info.decls.len == 0) return LookupError.TypeHasNoFieldsOrDeclarations; for (type_info.fields) |field| { - if (std.mem.eql(u8, field.name, lookup_name)) return field.type; + if (std.mem.eql(u8, field.name, lookup_name)) { + switch (@typeInfo(@TypeOf(type_info))) { + .Struct => return TypeItem{ .StructField = field }, + .Enum => return TypeItem{ .EnumField = field }, + .Union => return TypeItem{ .UnionField = field }, + else => @compileError("Please do not use this function on its own, as the checks for its usage are done outside its context!"), + } + } } for (type_info.decls) |decl| { - if (std.mem.eql(u8, decl.name, lookup_name)) return @TypeOf(@field(from, lookup_name)); - } - - // If we're here, we haven't found it - return LookupError.NotFound; - } -} - -// Internal use only. Creates a tuple type for the enum look-up result (used for .Field and .Any lookups on enums) -pub fn EnumLookupResult(comptime from: type) type { - return struct { field: ?from, decl: ?type }; -} - -/// Internal use only. Looks up a field from a type-info struct. Used for enums -inline fn UnsafeEnumFieldLookup(comptime from: type, comptime type_info: anytype, comptime lookup_name: []const u8) LookupError!from { - comptime { - if (type_info.fields.len == 0) return LookupError.TypeHasNoFields; - - for (type_info.fields) |field| { - if (std.mem.eql(u8, field.name, lookup_name)) return @enumFromInt(field.value); - } - - // If we're here, we haven't found it - return LookupError.NotFound; - } -} - -/// Internal use only. Looks up a field or declaraton from a type-info struct. Used for enums. -inline fn UnsafeEnumAnyLookup(comptime from: type, comptime type_info: anytype, comptime lookup_name: []const u8) LookupError!EnumLookupResult(from) { - comptime { - const ResultType = EnumLookupResult(from); - - if (type_info.fields.len == 0 and type_info.decls.len == 0) return LookupError.TypeHasNoFieldsOrDeclarations; - - for (type_info.fields) |field| { - if (std.mem.eql(u8, field.name, lookup_name)) return ResultType{ .field = @enumFromInt(field.value), .decl = null }; - } - - for (type_info.decls) |decl| { - if (std.mem.eql(u8, decl.name, lookup_name)) return ResultType{ .field = null, .decl = @TypeOf(@field(from, lookup_name)) }; + if (std.mem.eql(u8, decl.name, lookup_name)) return TypeItem{ .Declaration = decl }; } // If we're here, we haven't found it @@ -103,7 +114,7 @@ inline fn UnsafeEnumAnyLookup(comptime from: type, comptime type_info: anytype, } /// Internal use only. Looks up parameters from structs and unions. -inline fn UnsafeLookup(comptime from: type, comptime type_info: anytype, comptime lookup_name: []const u8, comptime lookup_mode: LookupMode) LookupError!type { +inline fn UnsafeLookup(comptime type_info: anytype, comptime lookup_name: []const u8, comptime lookup_mode: LookupMode) LookupError!TypeItem { comptime { switch (lookup_mode) { // Search for a field @@ -112,35 +123,11 @@ inline fn UnsafeLookup(comptime from: type, comptime type_info: anytype, comptim }, // Search for a declaration .Declaration => { - return UnsafeDeclarationLookup(from, type_info, lookup_name); - }, - // Search for either - .Any => { - return UnsafeAnyLookup(from, type_info, lookup_name); - }, - } - } -} - -/// Internal use only. Looks up parameters from enums. -inline fn UnsafeEnumLookup(comptime from: type, comptime type_info: anytype, comptime lookup_name: []const u8, comptime lookup_mode: LookupMode) switch (lookup_mode) { - .Field => LookupError!from, - .Declaration => LookupError!type, - .Any => LookupError!EnumLookupResult(from), -} { - comptime { - switch (lookup_mode) { - // Search for a field - .Field => { - return UnsafeEnumFieldLookup(from, type_info, lookup_name); - }, - // Search for a declaration - .Declaration => { - return UnsafeDeclarationLookup(from, type_info, lookup_name); + return UnsafeDeclarationLookup(type_info, lookup_name); }, // Search for either .Any => { - return UnsafeEnumAnyLookup(from, type_info, lookup_name); + return UnsafeAnyLookup(type_info, lookup_name); }, } } @@ -148,24 +135,17 @@ inline fn UnsafeEnumLookup(comptime from: type, comptime type_info: anytype, com /// Tries to find a field or declaration given a name and a specified look-up mode /// Returns the type of the field/declaration if found, otherwise errors with details. -pub fn FindInType(comptime from: type, comptime lookup_name: []const u8, comptime lookup_mode: LookupMode) switch (@typeInfo(from)) { - .Enum => switch (lookup_mode) { - .Field => LookupError!from, - .Declaration => LookupError!type, - .Any => LookupError!EnumLookupResult(from), - }, - else => LookupError!type, -} { +pub fn FindInType(comptime from: type, comptime lookup_name: []const u8, comptime lookup_mode: LookupMode) LookupError!TypeItem { switch (@typeInfo(from)) { // Structs can have both fields and declarations .Struct => |struct_info| { - return UnsafeLookup(from, struct_info, lookup_name, lookup_mode); + return UnsafeLookup(struct_info, lookup_name, lookup_mode); }, .Union => |uni_info| { - return UnsafeLookup(from, uni_info, lookup_name, lookup_mode); + return UnsafeLookup(uni_info, lookup_name, lookup_mode); }, .Enum => |enu_info| { - return UnsafeEnumLookup(from, enu_info, lookup_name, lookup_mode); + return UnsafeLookup(enu_info, lookup_name, lookup_mode); }, .Pointer => |ptr_info| { return FindInType(ptr_info.child, lookup_name, lookup_mode); diff --git a/src/tests.zig b/src/tests.zig index d770e23..197b98d 100644 --- a/src/tests.zig +++ b/src/tests.zig @@ -24,234 +24,234 @@ test "FindInType Struct" { // Try to find the field // First two should not error, the third should error - const field_type = try Common.FindInType(sample_type, "a", .Field); - try expect(field_type == i32); - const field_type_any = try Common.FindInType(sample_type, "a", .Any); - try expect(field_type_any == i32); + const field_info = try Common.FindInType(sample_type, "a", .Field); + try expect(field_info.GetType() == i32); + const field_info_any = try Common.FindInType(sample_type, "a", .Any); + try expect(field_info_any.GetType() == i32); const field_error = Common.FindInType(sample_type, "a", .Declaration); try expectError(LookupError.NotFound, field_error); // Try to find the declaration // First two should not error, the third should error - const decl_type = try Common.FindInType(sample_type, "b", .Declaration); - try expect(decl_type == i64); - const decl_type_any = try Common.FindInType(sample_type, "b", .Any); - try expect(decl_type_any == i64); + const decl_info = try Common.FindInType(sample_type, "b", .Declaration); + try expect(decl_info == i64); + const decl_info_any = try Common.FindInType(sample_type, "b", .Any); + try expect(decl_info_any == i64); const decl_error = Common.FindInType(sample_type, "b", .Field); try expectError(LookupError.NotFound, decl_error); } -test "FindInType Struct Empty" { - const sample_type = struct {}; - - const field_error = Common.FindInType(sample_type, "a", .Field); - try expectError(LookupError.TypeHasNoFields, field_error); - const decl_error = Common.FindInType(sample_type, "a", .Declaration); - try expectError(LookupError.TypeHasNoDeclarations, decl_error); - const any_error = Common.FindInType(sample_type, "a", .Any); - try expectError(LookupError.TypeHasNoFieldsOrDeclarations, any_error); -} - -test "FindInType Union" { - const sample_type = union { - a: i32, - pub const b: i64 = 10; - }; - - // Try to find the field - // First two should not error, the third should error - const field_type = try Common.FindInType(sample_type, "a", .Field); - try expect(field_type == i32); - const field_type_any = try Common.FindInType(sample_type, "a", .Any); - try expect(field_type_any == i32); - const field_error = Common.FindInType(sample_type, "a", .Declaration); - try expectError(LookupError.NotFound, field_error); - - // Try to find the declaration - // First two should not error, the third should error - const decl_type = try Common.FindInType(sample_type, "b", .Declaration); - try expect(decl_type == i64); - const decl_type_any = try Common.FindInType(sample_type, "b", .Any); - try expect(decl_type_any == i64); - const decl_error = Common.FindInType(sample_type, "b", .Field); - try expectError(LookupError.NotFound, decl_error); -} - -test "FindInType Union Empty" { - const sample_type = union {}; - - const field_error = Common.FindInType(sample_type, "a", .Field); - try expectError(LookupError.TypeHasNoFields, field_error); - const decl_error = Common.FindInType(sample_type, "a", .Declaration); - try expectError(LookupError.TypeHasNoDeclarations, decl_error); - const any_error = Common.FindInType(sample_type, "a", .Any); - try expectError(LookupError.TypeHasNoFieldsOrDeclarations, any_error); -} - -test "FindInType Enum" { - const sample_type = enum { - a, - pub const b: i64 = 10; - }; - - // Try to find the field - // First two should not error, the third should error - const field_type = try Common.FindInType(sample_type, "a", .Field); - try expect(field_type == sample_type.a); - const field_type_any = try Common.FindInType(sample_type, "a", .Any); - try expect(field_type_any.field == sample_type.a); - const field_error = Common.FindInType(sample_type, "a", .Declaration); - try expectError(LookupError.NotFound, field_error); - - // Try to find the declaration - // First two should not error, the third should error - const decl_type = try Common.FindInType(sample_type, "b", .Declaration); - try expect(decl_type == i64); - const decl_type_any = try Common.FindInType(sample_type, "b", .Any); - try expect(decl_type_any.decl == i64); - const decl_error = Common.FindInType(sample_type, "b", .Field); - try expectError(LookupError.NotFound, decl_error); -} - -test "FindInType Enum Empty" { - const sample_type_a = enum { a }; - - // Enums cannot be empty (for now; due to an error in std.fmt:527:46 (@tagName of empty enum is impossible)) - const decl_error = Common.FindInType(sample_type_a, "a", .Declaration); - try expectError(LookupError.TypeHasNoDeclarations, decl_error); -} - -test "FindInType Pointer" { - const sample_type = struct { - a: i32, - pub const b: i64 = 10; - }; - - const target_type = *sample_type; - - // Try to find the field - // First two should not error, the third should error - const field_type = try Common.FindInType(target_type, "a", .Field); - try expect(field_type == i32); - const field_type_any = try Common.FindInType(target_type, "a", .Any); - try expect(field_type_any == i32); - const field_error = Common.FindInType(target_type, "a", .Declaration); - try expectError(LookupError.NotFound, field_error); - - // Try to find the declaration - // First two should not error, the third should error - const decl_type = try Common.FindInType(target_type, "b", .Declaration); - try expect(decl_type == i64); - const decl_type_any = try Common.FindInType(target_type, "b", .Any); - try expect(decl_type_any == i64); - const decl_error = Common.FindInType(target_type, "b", .Field); - try expectError(LookupError.NotFound, decl_error); -} - -test "FindInType Optional" { - const sample_type = struct { - a: i32, - pub const b: i64 = 10; - }; - - const target_type = ?sample_type; - - // Try to find the field - // First two should not error, the third should error - const field_type = try Common.FindInType(target_type, "a", .Field); - try expect(field_type == i32); - const field_type_any = try Common.FindInType(target_type, "a", .Any); - try expect(field_type_any == i32); - const field_error = Common.FindInType(target_type, "a", .Declaration); - try expectError(LookupError.NotFound, field_error); - - // Try to find the declaration - // First two should not error, the third should error - const decl_type = try Common.FindInType(target_type, "b", .Declaration); - try expect(decl_type == i64); - const decl_type_any = try Common.FindInType(target_type, "b", .Any); - try expect(decl_type_any == i64); - const decl_error = Common.FindInType(target_type, "b", .Field); - try expectError(LookupError.NotFound, decl_error); -} - -test "FindInType Error Union" { - const sample_type = struct { - a: i32, - pub const b: i64 = 10; - }; - - const target_type = anyerror!sample_type; - - // Try to find the field - // First two should not error, the third should error - const field_type = try Common.FindInType(target_type, "a", .Field); - try expect(field_type == i32); - const field_type_any = try Common.FindInType(target_type, "a", .Any); - try expect(field_type_any == i32); - const field_error = Common.FindInType(target_type, "a", .Declaration); - try expectError(LookupError.NotFound, field_error); - - // Try to find the declaration - // First two should not error, the third should error - const decl_type = try Common.FindInType(target_type, "b", .Declaration); - try expect(decl_type == i64); - const decl_type_any = try Common.FindInType(target_type, "b", .Any); - try expect(decl_type_any == i64); - const decl_error = Common.FindInType(target_type, "b", .Field); - try expectError(LookupError.NotFound, decl_error); -} - -test "FindInType Vector" { - const sample_type = struct { - a: i32, - pub const b: i64 = 10; - }; - - const target_type = @Vector(4, *sample_type); - - // Try to find the field - // First two should not error, the third should error - const field_type = try Common.FindInType(target_type, "a", .Field); - try expect(field_type == i32); - const field_type_any = try Common.FindInType(target_type, "a", .Any); - try expect(field_type_any == i32); - const field_error = Common.FindInType(target_type, "a", .Declaration); - try expectError(LookupError.NotFound, field_error); - - // Try to find the declaration - // First two should not error, the third should error - const decl_type = try Common.FindInType(target_type, "b", .Declaration); - try expect(decl_type == i64); - const decl_type_any = try Common.FindInType(target_type, "b", .Any); - try expect(decl_type_any == i64); - const decl_error = Common.FindInType(target_type, "b", .Field); - try expectError(LookupError.NotFound, decl_error); -} - -test "FindInType Array" { - const sample_type = struct { - a: i32, - pub const b: i64 = 10; - }; - - const target_type = [4]sample_type; - - // Try to find the field - // First two should not error, the third should error - const field_type = try Common.FindInType(target_type, "a", .Field); - try expect(field_type == i32); - const field_type_any = try Common.FindInType(target_type, "a", .Any); - try expect(field_type_any == i32); - const field_error = Common.FindInType(target_type, "a", .Declaration); - try expectError(LookupError.NotFound, field_error); - - // Try to find the declaration - // First two should not error, the third should error - const decl_type = try Common.FindInType(target_type, "b", .Declaration); - try expect(decl_type == i64); - const decl_type_any = try Common.FindInType(target_type, "b", .Any); - try expect(decl_type_any == i64); - const decl_error = Common.FindInType(target_type, "b", .Field); - try expectError(LookupError.NotFound, decl_error); -} +// test "FindInType Struct Empty" { +// const sample_type = struct {}; + +// const field_error = Common.FindInType(sample_type, "a", .Field); +// try expectError(LookupError.TypeHasNoFields, field_error); +// const decl_error = Common.FindInType(sample_type, "a", .Declaration); +// try expectError(LookupError.TypeHasNoDeclarations, decl_error); +// const any_error = Common.FindInType(sample_type, "a", .Any); +// try expectError(LookupError.TypeHasNoFieldsOrDeclarations, any_error); +// } + +// test "FindInType Union" { +// const sample_type = union { +// a: i32, +// pub const b: i64 = 10; +// }; + +// // Try to find the field +// // First two should not error, the third should error +// const field_type = try Common.FindInType(sample_type, "a", .Field); +// try expect(field_type == i32); +// const field_type_any = try Common.FindInType(sample_type, "a", .Any); +// try expect(field_type_any == i32); +// const field_error = Common.FindInType(sample_type, "a", .Declaration); +// try expectError(LookupError.NotFound, field_error); + +// // Try to find the declaration +// // First two should not error, the third should error +// const decl_type = try Common.FindInType(sample_type, "b", .Declaration); +// try expect(decl_type == i64); +// const decl_type_any = try Common.FindInType(sample_type, "b", .Any); +// try expect(decl_type_any == i64); +// const decl_error = Common.FindInType(sample_type, "b", .Field); +// try expectError(LookupError.NotFound, decl_error); +// } + +// test "FindInType Union Empty" { +// const sample_type = union {}; + +// const field_error = Common.FindInType(sample_type, "a", .Field); +// try expectError(LookupError.TypeHasNoFields, field_error); +// const decl_error = Common.FindInType(sample_type, "a", .Declaration); +// try expectError(LookupError.TypeHasNoDeclarations, decl_error); +// const any_error = Common.FindInType(sample_type, "a", .Any); +// try expectError(LookupError.TypeHasNoFieldsOrDeclarations, any_error); +// } + +// test "FindInType Enum" { +// const sample_type = enum { +// a, +// pub const b: i64 = 10; +// }; + +// // Try to find the field +// // First two should not error, the third should error +// const field_type = try Common.FindInType(sample_type, "a", .Field); +// try expect(field_type == sample_type.a); +// const field_type_any = try Common.FindInType(sample_type, "a", .Any); +// try expect(field_type_any.field == sample_type.a); +// const field_error = Common.FindInType(sample_type, "a", .Declaration); +// try expectError(LookupError.NotFound, field_error); + +// // Try to find the declaration +// // First two should not error, the third should error +// const decl_type = try Common.FindInType(sample_type, "b", .Declaration); +// try expect(decl_type == i64); +// const decl_type_any = try Common.FindInType(sample_type, "b", .Any); +// try expect(decl_type_any.decl == i64); +// const decl_error = Common.FindInType(sample_type, "b", .Field); +// try expectError(LookupError.NotFound, decl_error); +// } + +// test "FindInType Enum Empty" { +// const sample_type_a = enum { a }; + +// // Enums cannot be empty (for now; due to an error in std.fmt:527:46 (@tagName of empty enum is impossible)) +// const decl_error = Common.FindInType(sample_type_a, "a", .Declaration); +// try expectError(LookupError.TypeHasNoDeclarations, decl_error); +// } + +// test "FindInType Pointer" { +// const sample_type = struct { +// a: i32, +// pub const b: i64 = 10; +// }; + +// const target_type = *sample_type; + +// // Try to find the field +// // First two should not error, the third should error +// const field_type = try Common.FindInType(target_type, "a", .Field); +// try expect(field_type == i32); +// const field_type_any = try Common.FindInType(target_type, "a", .Any); +// try expect(field_type_any == i32); +// const field_error = Common.FindInType(target_type, "a", .Declaration); +// try expectError(LookupError.NotFound, field_error); + +// // Try to find the declaration +// // First two should not error, the third should error +// const decl_type = try Common.FindInType(target_type, "b", .Declaration); +// try expect(decl_type == i64); +// const decl_type_any = try Common.FindInType(target_type, "b", .Any); +// try expect(decl_type_any == i64); +// const decl_error = Common.FindInType(target_type, "b", .Field); +// try expectError(LookupError.NotFound, decl_error); +// } + +// test "FindInType Optional" { +// const sample_type = struct { +// a: i32, +// pub const b: i64 = 10; +// }; + +// const target_type = ?sample_type; + +// // Try to find the field +// // First two should not error, the third should error +// const field_type = try Common.FindInType(target_type, "a", .Field); +// try expect(field_type == i32); +// const field_type_any = try Common.FindInType(target_type, "a", .Any); +// try expect(field_type_any == i32); +// const field_error = Common.FindInType(target_type, "a", .Declaration); +// try expectError(LookupError.NotFound, field_error); + +// // Try to find the declaration +// // First two should not error, the third should error +// const decl_type = try Common.FindInType(target_type, "b", .Declaration); +// try expect(decl_type == i64); +// const decl_type_any = try Common.FindInType(target_type, "b", .Any); +// try expect(decl_type_any == i64); +// const decl_error = Common.FindInType(target_type, "b", .Field); +// try expectError(LookupError.NotFound, decl_error); +// } + +// test "FindInType Error Union" { +// const sample_type = struct { +// a: i32, +// pub const b: i64 = 10; +// }; + +// const target_type = anyerror!sample_type; + +// // Try to find the field +// // First two should not error, the third should error +// const field_type = try Common.FindInType(target_type, "a", .Field); +// try expect(field_type == i32); +// const field_type_any = try Common.FindInType(target_type, "a", .Any); +// try expect(field_type_any == i32); +// const field_error = Common.FindInType(target_type, "a", .Declaration); +// try expectError(LookupError.NotFound, field_error); + +// // Try to find the declaration +// // First two should not error, the third should error +// const decl_type = try Common.FindInType(target_type, "b", .Declaration); +// try expect(decl_type == i64); +// const decl_type_any = try Common.FindInType(target_type, "b", .Any); +// try expect(decl_type_any == i64); +// const decl_error = Common.FindInType(target_type, "b", .Field); +// try expectError(LookupError.NotFound, decl_error); +// } + +// test "FindInType Vector" { +// const sample_type = struct { +// a: i32, +// pub const b: i64 = 10; +// }; + +// const target_type = @Vector(4, *sample_type); + +// // Try to find the field +// // First two should not error, the third should error +// const field_type = try Common.FindInType(target_type, "a", .Field); +// try expect(field_type == i32); +// const field_type_any = try Common.FindInType(target_type, "a", .Any); +// try expect(field_type_any == i32); +// const field_error = Common.FindInType(target_type, "a", .Declaration); +// try expectError(LookupError.NotFound, field_error); + +// // Try to find the declaration +// // First two should not error, the third should error +// const decl_type = try Common.FindInType(target_type, "b", .Declaration); +// try expect(decl_type == i64); +// const decl_type_any = try Common.FindInType(target_type, "b", .Any); +// try expect(decl_type_any == i64); +// const decl_error = Common.FindInType(target_type, "b", .Field); +// try expectError(LookupError.NotFound, decl_error); +// } + +// test "FindInType Array" { +// const sample_type = struct { +// a: i32, +// pub const b: i64 = 10; +// }; + +// const target_type = [4]sample_type; + +// // Try to find the field +// // First two should not error, the third should error +// const field_type = try Common.FindInType(target_type, "a", .Field); +// try expect(field_type == i32); +// const field_type_any = try Common.FindInType(target_type, "a", .Any); +// try expect(field_type_any == i32); +// const field_error = Common.FindInType(target_type, "a", .Declaration); +// try expectError(LookupError.NotFound, field_error); + +// // Try to find the declaration +// // First two should not error, the third should error +// const decl_type = try Common.FindInType(target_type, "b", .Declaration); +// try expect(decl_type == i64); +// const decl_type_any = try Common.FindInType(target_type, "b", .Any); +// try expect(decl_type_any == i64); +// const decl_error = Common.FindInType(target_type, "b", .Field); +// try expectError(LookupError.NotFound, decl_error); +// } From bf69862a884e01ffdf1abcfdde5bae5f628b4bcc Mon Sep 17 00:00:00 2001 From: Nylvon Date: Mon, 20 Nov 2023 10:21:29 +0200 Subject: [PATCH 04/16] Rewriting --- src/common.zig | 38 ++++++++++++++++++++------------------ src/tests.zig | 14 +++++++------- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/common.zig b/src/common.zig index e640092..c4255bc 100644 --- a/src/common.zig +++ b/src/common.zig @@ -52,13 +52,19 @@ pub const TypeItem = union { /// Internal use only. Looks up a field from a type-info struct. inline fn UnsafeFieldLookup(comptime target: type, comptime lookup_name: []const u8) LookupError!TypeItem { comptime { - const type_info = @TypeOf(target); + const pre_type_info = @typeInfo(target); + + const type_info = switch (pre_type_info) { + .Struct => pre_type_info.Struct, + .Enum => pre_type_info.Enum, + .Union => pre_type_info.Union, + }; if (type_info.fields.len == 0) return LookupError.TypeHasNoFields; for (type_info.fields) |field| { if (std.mem.eql(u8, field.name, lookup_name)) { - switch (type_info) { + switch (@typeInfo(@TypeOf(target))) { .Struct => return TypeItem{ .StructField = field }, .Enum => return TypeItem{ .EnumField = FullEnumField{ .field = field, .type = target } }, .Union => return TypeItem{ .UnionField = field }, @@ -89,15 +95,17 @@ inline fn UnsafeDeclarationLookup(comptime target: type, comptime lookup_name: [ } /// Internal use only. Looks up a field or declaraton from a type-info struct. -inline fn UnsafeAnyLookup(comptime type_info: anytype, comptime lookup_name: []const u8) LookupError!TypeItem { +inline fn UnsafeAnyLookup(comptime target: type, comptime lookup_name: []const u8) LookupError!TypeItem { comptime { + const type_info = @typeInfo(target); + if (type_info.fields.len == 0 and type_info.decls.len == 0) return LookupError.TypeHasNoFieldsOrDeclarations; for (type_info.fields) |field| { if (std.mem.eql(u8, field.name, lookup_name)) { - switch (@typeInfo(@TypeOf(type_info))) { + switch (type_info) { .Struct => return TypeItem{ .StructField = field }, - .Enum => return TypeItem{ .EnumField = field }, + .Enum => return TypeItem{ .EnumField = FullEnumField{ .field = field, .type = target } }, .Union => return TypeItem{ .UnionField = field }, else => @compileError("Please do not use this function on its own, as the checks for its usage are done outside its context!"), } @@ -105,7 +113,7 @@ inline fn UnsafeAnyLookup(comptime type_info: anytype, comptime lookup_name: []c } for (type_info.decls) |decl| { - if (std.mem.eql(u8, decl.name, lookup_name)) return TypeItem{ .Declaration = decl }; + if (std.mem.eql(u8, decl.name, lookup_name)) return TypeItem{ .Declaration = FullDeclaration{ .decl = decl, .type = type } }; } // If we're here, we haven't found it @@ -114,20 +122,20 @@ inline fn UnsafeAnyLookup(comptime type_info: anytype, comptime lookup_name: []c } /// Internal use only. Looks up parameters from structs and unions. -inline fn UnsafeLookup(comptime type_info: anytype, comptime lookup_name: []const u8, comptime lookup_mode: LookupMode) LookupError!TypeItem { +inline fn UnsafeLookup(comptime target: type, comptime lookup_name: []const u8, comptime lookup_mode: LookupMode) LookupError!TypeItem { comptime { switch (lookup_mode) { // Search for a field .Field => { - return UnsafeFieldLookup(type_info, lookup_name); + return UnsafeFieldLookup(target, lookup_name); }, // Search for a declaration .Declaration => { - return UnsafeDeclarationLookup(type_info, lookup_name); + return UnsafeDeclarationLookup(target, lookup_name); }, // Search for either .Any => { - return UnsafeAnyLookup(type_info, lookup_name); + return UnsafeAnyLookup(target, lookup_name); }, } } @@ -138,14 +146,8 @@ inline fn UnsafeLookup(comptime type_info: anytype, comptime lookup_name: []cons pub fn FindInType(comptime from: type, comptime lookup_name: []const u8, comptime lookup_mode: LookupMode) LookupError!TypeItem { switch (@typeInfo(from)) { // Structs can have both fields and declarations - .Struct => |struct_info| { - return UnsafeLookup(struct_info, lookup_name, lookup_mode); - }, - .Union => |uni_info| { - return UnsafeLookup(uni_info, lookup_name, lookup_mode); - }, - .Enum => |enu_info| { - return UnsafeLookup(enu_info, lookup_name, lookup_mode); + .Struct, .Union, .Enum => { + return UnsafeLookup(from, lookup_name, lookup_mode); }, .Pointer => |ptr_info| { return FindInType(ptr_info.child, lookup_name, lookup_mode); diff --git a/src/tests.zig b/src/tests.zig index 197b98d..00c8503 100644 --- a/src/tests.zig +++ b/src/tests.zig @@ -32,13 +32,13 @@ test "FindInType Struct" { try expectError(LookupError.NotFound, field_error); // Try to find the declaration - // First two should not error, the third should error - const decl_info = try Common.FindInType(sample_type, "b", .Declaration); - try expect(decl_info == i64); - const decl_info_any = try Common.FindInType(sample_type, "b", .Any); - try expect(decl_info_any == i64); - const decl_error = Common.FindInType(sample_type, "b", .Field); - try expectError(LookupError.NotFound, decl_error); + // // First two should not error, the third should error + // const decl_info = try Common.FindInType(sample_type, "b", .Declaration); + // try expect(decl_info == i64); + // const decl_info_any = try Common.FindInType(sample_type, "b", .Any); + // try expect(decl_info_any == i64); + // const decl_error = Common.FindInType(sample_type, "b", .Field); + // try expectError(LookupError.NotFound, decl_error); } // test "FindInType Struct Empty" { From 89ad8d797ba04a5c73c0f2a2e9d99868589e7549 Mon Sep 17 00:00:00 2001 From: Nylvon Date: Mon, 20 Nov 2023 21:21:54 +0200 Subject: [PATCH 05/16] Fixed type infos in unsafe look-ups and the GetType function in TypeItem --- src/common.zig | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/src/common.zig b/src/common.zig index c4255bc..e82349c 100644 --- a/src/common.zig +++ b/src/common.zig @@ -31,7 +31,7 @@ pub const FullEnumField = struct { /// A Type Item is a part of a type, it is a wrapper over /// fields and declarations of all types -pub const TypeItem = union { +pub const TypeItem = union(enum) { // NOTE: Should be kept up to date with the language specification. Declaration: FullDeclaration, StructField: std.builtin.Type.StructField, @@ -41,10 +41,11 @@ pub const TypeItem = union { /// Returns the type of the type item pub fn GetType(comptime self: @This()) type { switch (std.meta.activeTag(self)) { - .Declaration => |d| return @TypeOf(@field(d.type, d.decl.name)), - .StructField => |sf| return sf.type, - .EnumField => |ef| return ef.type, - .UnionField => |uf| return uf.type, + // NOTE: There's probably a prettier way of doing this. Look into it. + .Declaration => return @TypeOf(@field(self.Declaration.type, self.Declaration.decl.name)), + .StructField => return self.StructField.type, + .EnumField => return self.EnumField.type, + .UnionField => return self.UnionField.type, } } }; @@ -52,23 +53,22 @@ pub const TypeItem = union { /// Internal use only. Looks up a field from a type-info struct. inline fn UnsafeFieldLookup(comptime target: type, comptime lookup_name: []const u8) LookupError!TypeItem { comptime { - const pre_type_info = @typeInfo(target); - - const type_info = switch (pre_type_info) { - .Struct => pre_type_info.Struct, - .Enum => pre_type_info.Enum, - .Union => pre_type_info.Union, + const type_info = switch (@typeInfo(target)) { + .Struct => |s| s, + .Enum => |e| e, + .Union => |u| u, + else => unreachable, }; if (type_info.fields.len == 0) return LookupError.TypeHasNoFields; for (type_info.fields) |field| { if (std.mem.eql(u8, field.name, lookup_name)) { - switch (@typeInfo(@TypeOf(target))) { + switch (@typeInfo(target)) { .Struct => return TypeItem{ .StructField = field }, .Enum => return TypeItem{ .EnumField = FullEnumField{ .field = field, .type = target } }, .Union => return TypeItem{ .UnionField = field }, - else => @compileError("Please do not use this function on its own, as the checks for its usage are done outside its context!"), + else => unreachable, } } } @@ -81,7 +81,12 @@ inline fn UnsafeFieldLookup(comptime target: type, comptime lookup_name: []const /// Internal use only. Looks up a declaration from a type-info struct. inline fn UnsafeDeclarationLookup(comptime target: type, comptime lookup_name: []const u8) LookupError!TypeItem { comptime { - const type_info = @TypeOf(target); + const type_info = switch (@typeInfo(target)) { + .Struct => |s| s, + .Enum => |e| e, + .Union => |u| u, + else => unreachable, + }; if (type_info.decls.len == 0) return LookupError.TypeHasNoDeclarations; @@ -97,13 +102,18 @@ inline fn UnsafeDeclarationLookup(comptime target: type, comptime lookup_name: [ /// Internal use only. Looks up a field or declaraton from a type-info struct. inline fn UnsafeAnyLookup(comptime target: type, comptime lookup_name: []const u8) LookupError!TypeItem { comptime { - const type_info = @typeInfo(target); + const type_info = switch (@typeInfo(target)) { + .Struct => |s| s, + .Enum => |e| e, + .Union => |u| u, + else => unreachable, + }; if (type_info.fields.len == 0 and type_info.decls.len == 0) return LookupError.TypeHasNoFieldsOrDeclarations; for (type_info.fields) |field| { if (std.mem.eql(u8, field.name, lookup_name)) { - switch (type_info) { + switch (@typeInfo(target)) { .Struct => return TypeItem{ .StructField = field }, .Enum => return TypeItem{ .EnumField = FullEnumField{ .field = field, .type = target } }, .Union => return TypeItem{ .UnionField = field }, From e0e893eeb9be31ecd2eb1b965c2513958fa60456 Mon Sep 17 00:00:00 2001 From: Nylvon Date: Tue, 21 Nov 2023 10:37:48 +0200 Subject: [PATCH 06/16] Fix GetType for declarations, add GetName --- src/common.zig | 19 ++++++++++++++++--- src/tests.zig | 12 ++++++------ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/common.zig b/src/common.zig index e82349c..0e5de88 100644 --- a/src/common.zig +++ b/src/common.zig @@ -38,7 +38,7 @@ pub const TypeItem = union(enum) { EnumField: FullEnumField, UnionField: std.builtin.Type.UnionField, - /// Returns the type of the type item + /// Returns the type of the entity described pub fn GetType(comptime self: @This()) type { switch (std.meta.activeTag(self)) { // NOTE: There's probably a prettier way of doing this. Look into it. @@ -48,6 +48,19 @@ pub const TypeItem = union(enum) { .UnionField => return self.UnionField.type, } } + + /// Returns the name of the entity described + pub fn GetName(comptime self: @This()) []const u8 { + switch (std.meta.activeTag(self)) { + // NOTE: There's probably a prettier way of doing this. Look into it. + .Declaration => return self.Declaration.decl.name, + .StructField => return self.StructField.name, + .EnumField => return self.EnumField.field.name, + .UnionField => return self.UnionField.name, + } + } + + // pub fn GetDefaultValue(comptime self: @This()) !switch }; /// Internal use only. Looks up a field from a type-info struct. @@ -91,7 +104,7 @@ inline fn UnsafeDeclarationLookup(comptime target: type, comptime lookup_name: [ if (type_info.decls.len == 0) return LookupError.TypeHasNoDeclarations; for (type_info.decls) |decl| { - if (std.mem.eql(u8, decl.name, lookup_name)) return TypeItem{ .Declaration = FullDeclaration{ .decl = decl, .type = type } }; + if (std.mem.eql(u8, decl.name, lookup_name)) return TypeItem{ .Declaration = FullDeclaration{ .decl = decl, .type = target } }; } // If we're here, we haven't found it @@ -123,7 +136,7 @@ inline fn UnsafeAnyLookup(comptime target: type, comptime lookup_name: []const u } for (type_info.decls) |decl| { - if (std.mem.eql(u8, decl.name, lookup_name)) return TypeItem{ .Declaration = FullDeclaration{ .decl = decl, .type = type } }; + if (std.mem.eql(u8, decl.name, lookup_name)) return TypeItem{ .Declaration = FullDeclaration{ .decl = decl, .type = target } }; } // If we're here, we haven't found it diff --git a/src/tests.zig b/src/tests.zig index 00c8503..23e7d56 100644 --- a/src/tests.zig +++ b/src/tests.zig @@ -33,12 +33,12 @@ test "FindInType Struct" { // Try to find the declaration // // First two should not error, the third should error - // const decl_info = try Common.FindInType(sample_type, "b", .Declaration); - // try expect(decl_info == i64); - // const decl_info_any = try Common.FindInType(sample_type, "b", .Any); - // try expect(decl_info_any == i64); - // const decl_error = Common.FindInType(sample_type, "b", .Field); - // try expectError(LookupError.NotFound, decl_error); + const decl_info = try Common.FindInType(sample_type, "b", .Declaration); + try expect(decl_info.GetType() == i64); + const decl_info_any = try Common.FindInType(sample_type, "b", .Any); + try expect(decl_info_any.GetType() == i64); + const decl_error = Common.FindInType(sample_type, "b", .Field); + try expectError(LookupError.NotFound, decl_error); } // test "FindInType Struct Empty" { From c2f844bad40b55cd028ea4beba6c970949ac4dba Mon Sep 17 00:00:00 2001 From: Nylvon Date: Tue, 21 Nov 2023 12:24:26 +0200 Subject: [PATCH 07/16] Split tests, revamp common tests, expand build system for expansion --- build.zig | 32 ++++-- src/common.zig | 70 ++++++++---- src/common_tests.zig | 191 ++++++++++++++++++++++++++++++++ src/tests.zig | 257 ------------------------------------------- 4 files changed, 261 insertions(+), 289 deletions(-) create mode 100644 src/common_tests.zig delete mode 100644 src/tests.zig diff --git a/build.zig b/build.zig index 0fe87b1..7595c8c 100644 --- a/build.zig +++ b/build.zig @@ -1,5 +1,26 @@ const std = @import("std"); +/// Creates a new test suite, makes a test step, and returns the test step. +pub fn add_tests(b: *std.Build, name: []const u8, desc: []const u8, path: []const u8, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode) *std.Build.Step { + const new_tests = b.addTest(.{ + .root_source_file = .{ .path = path }, + .target = target, + .optimize = optimize, + }); + + const run_new_tests = b.addRunArtifact(new_tests); + + const new_tests_step = b.step(name, desc); + new_tests_step.dependOn(&run_new_tests.step); + + return new_tests_step; +} + +// TODO: Continue splitting the project into multiple sub-modules and implement this function. +// pub fn add_module(b: *std.Build, name: []const u8, test_desc: []const u8, path: []const u8, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode) *std.Build.Step { +// const new_module = b.addModule // ??? +// } + pub fn build(b: *std.Build) void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); @@ -12,14 +33,7 @@ pub fn build(b: *std.Build) void { }); b.installArtifact(lib); - const main_tests = b.addTest(.{ - .root_source_file = .{ .path = "src/tests.zig" }, - .target = target, - .optimize = optimize, - }); - - const run_main_tests = b.addRunArtifact(main_tests); - const test_step = b.step("test", "Run library tests"); - test_step.dependOn(&run_main_tests.step); + + test_step.dependOn(add_tests(b, "test_common", "Run common library tests", "src/common_tests.zig", target, optimize)); } diff --git a/src/common.zig b/src/common.zig index 0e5de88..ac53a45 100644 --- a/src/common.zig +++ b/src/common.zig @@ -9,6 +9,7 @@ const std = @import("std"); /// If Any is selected, fields are prioritised over declarations in the look-up order. pub const LookupMode = enum { Field, Declaration, Any }; +/// Used by FindInType to detail why the function call failed. pub const LookupError = error{ InvalidType, NotFound, @@ -17,50 +18,73 @@ pub const LookupError = error{ TypeHasNoFieldsOrDeclarations, }; -/// Contains the type that the declaration was gotten from as well as the declaration +/// Contains the type that the declaration was gotten from as well as the declaration. pub const FullDeclaration = struct { decl: std.builtin.Type.Declaration, type: type, }; -/// Contains the type that the enum field was gotten from as well as the enum field +/// Contains the type that the enum field was gotten from as well as the enum field. pub const FullEnumField = struct { field: std.builtin.Type.EnumField, type: type, }; +/// This enum is used to identify the active part of the +/// TypeItem union wrapper. +pub const TypeItemKind = enum { + Declaration, + StructField, + EnumField, + UnionField, +}; + /// A Type Item is a part of a type, it is a wrapper over -/// fields and declarations of all types -pub const TypeItem = union(enum) { +/// fields and declarations of all types. +pub const TypeItem = union(TypeItemKind) { // NOTE: Should be kept up to date with the language specification. Declaration: FullDeclaration, StructField: std.builtin.Type.StructField, EnumField: FullEnumField, UnionField: std.builtin.Type.UnionField, - /// Returns the type of the entity described + /// The error set that can be returned by the functions of the TypeItem wrapper. + pub const Error = error{ + InvalidType, + }; + + /// Returns the type of the entity described. pub fn GetType(comptime self: @This()) type { - switch (std.meta.activeTag(self)) { - // NOTE: There's probably a prettier way of doing this. Look into it. - .Declaration => return @TypeOf(@field(self.Declaration.type, self.Declaration.decl.name)), - .StructField => return self.StructField.type, - .EnumField => return self.EnumField.type, - .UnionField => return self.UnionField.type, + switch (self) { + .Declaration => |d| return @TypeOf(@field(d.type, d.decl.name)), + .StructField => |sf| return sf.type, + .EnumField => |ef| return ef.type, + .UnionField => |uf| return uf.type, } } - /// Returns the name of the entity described + /// Returns the name of the entity described. pub fn GetName(comptime self: @This()) []const u8 { - switch (std.meta.activeTag(self)) { - // NOTE: There's probably a prettier way of doing this. Look into it. - .Declaration => return self.Declaration.decl.name, - .StructField => return self.StructField.name, - .EnumField => return self.EnumField.field.name, - .UnionField => return self.UnionField.name, + switch (self) { + .Declaration => |d| return d.decl.name, + .StructField => |sf| return sf.name, + .EnumField => |ef| return ef.field.name, + .UnionField => |uf| return uf.name, } } - // pub fn GetDefaultValue(comptime self: @This()) !switch + /// Returns the default value of the entity described. + /// If the entity doesn't have a default value, null will be returned. + /// If the entity cannot have a default value, an error will be returned. + pub fn GetDefaultValue(comptime self: @This()) switch (self) { + .Declaration, .EnumField, .UnionField => TypeItem.Error, + .StructField => ?*const anyopaque, + } { + switch (self) { + .Declaration, .EnumField, .UnionField => return TypeItem.Error.InvalidType, + .StructField => |sf| return sf.default_value, + } + } }; /// Internal use only. Looks up a field from a type-info struct. @@ -86,7 +110,7 @@ inline fn UnsafeFieldLookup(comptime target: type, comptime lookup_name: []const } } - // If we're here, we haven't found it + // If we're here, we haven't found it. return LookupError.NotFound; } } @@ -107,7 +131,7 @@ inline fn UnsafeDeclarationLookup(comptime target: type, comptime lookup_name: [ if (std.mem.eql(u8, decl.name, lookup_name)) return TypeItem{ .Declaration = FullDeclaration{ .decl = decl, .type = target } }; } - // If we're here, we haven't found it + // If we're here, we haven't found it. return LookupError.NotFound; } } @@ -139,7 +163,7 @@ inline fn UnsafeAnyLookup(comptime target: type, comptime lookup_name: []const u if (std.mem.eql(u8, decl.name, lookup_name)) return TypeItem{ .Declaration = FullDeclaration{ .decl = decl, .type = target } }; } - // If we're here, we haven't found it + // If we're here, we haven't found it. return LookupError.NotFound; } } @@ -164,7 +188,7 @@ inline fn UnsafeLookup(comptime target: type, comptime lookup_name: []const u8, } } -/// Tries to find a field or declaration given a name and a specified look-up mode +/// Tries to find a field or declaration given a name and a specified look-up mode. /// Returns the type of the field/declaration if found, otherwise errors with details. pub fn FindInType(comptime from: type, comptime lookup_name: []const u8, comptime lookup_mode: LookupMode) LookupError!TypeItem { switch (@typeInfo(from)) { diff --git a/src/common_tests.zig b/src/common_tests.zig new file mode 100644 index 0000000..69dc475 --- /dev/null +++ b/src/common_tests.zig @@ -0,0 +1,191 @@ +/// Metaplasia Common Tests +/// ©2023 Cristian Vasilache (NNCV / Nylvon) +/// +/// Provides an exhaustive test suite for the common +/// functionality of the Metaplasia library. +/// +/// Each test name is formatted as follows: +/// "[FeatureName] [Scenario]" +pub const CommonTests = @This(); +const Common = @import("common.zig").Common; +const TypeItemKind = Common.TypeItemKind; +const LookupMode = Common.LookupMode; +const LookupError = Common.LookupError; +const std = @import("std"); +const testing = std.testing; +const expect = testing.expect; +const expectEqual = testing.expectEqual; +const expectError = testing.expectError; +const activeTag = std.meta.activeTag; +const eql = std.mem.eql; + +// FindInType tests + +/// Convenience wrapper for testing the +/// FindInType function over types. +pub fn test_extract( + // L = Look-up + // E = Expected + comptime target: type, + comptime l_name: []const u8, + comptime l_mode: LookupMode, + comptime e_type: ?type, + comptime e_kind: ?TypeItemKind, + comptime e_default: ?*const anyopaque, + comptime e_outcome: enum { Success, Failure }, + comptime e_error: ?anyerror, +) !void { + switch (e_outcome) { + .Success => { + // Sanity-checks + try expect(e_type != null); + try expect(e_kind != null); + try expect(e_error == null); + + const info = try Common.FindInType(target, l_name, l_mode); + + if (e_kind) |kind| { + try expect(activeTag(info) == kind); + } else return error.NullKind; + + if (e_type) |t| { + try expect(info.GetType() == t); + } else return error.NullType; + + try expect(eql(u8, info.GetName(), l_name)); + switch (info) { + .StructField => { + try expect(info.GetDefaultValue() == e_default); + }, + else => {}, + } + }, + .Failure => { + const info = Common.FindInType(target, l_name, l_mode); + if (e_error) |err| { + try expectError(err, info); + } else { + return error.NullError; + } + }, + } +} + +test "FindInType" { + const test_struct = struct { + a: i32, + pub const b: i64 = 10; + }; + + try test_extract(test_struct, "a", .Field, i32, .StructField, null, .Success, null); + try test_extract(test_struct, "a", .Any, i32, .StructField, null, .Success, null); + try test_extract(test_struct, "b", .Declaration, i64, .Declaration, null, .Success, null); + try test_extract(test_struct, "b", .Any, i64, .Declaration, null, .Success, null); + + try test_extract(test_struct, "a", .Declaration, null, null, null, .Failure, LookupError.NotFound); + try test_extract(test_struct, "b", .Field, null, null, null, .Failure, LookupError.NotFound); + + const test_struct_empty = struct {}; + + try test_extract(test_struct_empty, "", .Field, null, null, null, .Failure, LookupError.TypeHasNoFields); + try test_extract(test_struct_empty, "", .Declaration, null, null, null, .Failure, LookupError.TypeHasNoDeclarations); + try test_extract(test_struct_empty, "", .Any, null, null, null, .Failure, LookupError.TypeHasNoFieldsOrDeclarations); + + const test_union = union { + a: i32, + pub const b: i64 = 10; + }; + + try test_extract(test_union, "a", .Field, i32, .UnionField, null, .Success, null); + try test_extract(test_union, "a", .Any, i32, .UnionField, null, .Success, null); + try test_extract(test_union, "b", .Declaration, i64, .Declaration, null, .Success, null); + try test_extract(test_union, "b", .Any, i64, .Declaration, null, .Success, null); + + try test_extract(test_union, "a", .Declaration, null, null, null, .Failure, LookupError.NotFound); + try test_extract(test_union, "b", .Field, null, null, null, .Failure, LookupError.NotFound); + + const test_union_empty = union {}; + + try test_extract(test_union_empty, "", .Field, null, null, null, .Failure, LookupError.TypeHasNoFields); + try test_extract(test_union_empty, "", .Declaration, null, null, null, .Failure, LookupError.TypeHasNoDeclarations); + try test_extract(test_union_empty, "", .Any, null, null, null, .Failure, LookupError.TypeHasNoFieldsOrDeclarations); + + const test_enum = enum { + a, + pub const b: i64 = 10; + }; + + // NOTE: Rethink the expected type on an enum. Maybe the enum should return the actual enum selector? + // Maybe a different function would be of use. + try test_extract(test_enum, "a", .Field, test_enum, .EnumField, null, .Success, null); + try test_extract(test_enum, "a", .Any, test_enum, .EnumField, null, .Success, null); + try test_extract(test_enum, "b", .Declaration, i64, .Declaration, null, .Success, null); + try test_extract(test_enum, "b", .Any, i64, .Declaration, null, .Success, null); + + try test_extract(test_enum, "a", .Declaration, null, null, null, .Failure, LookupError.NotFound); + try test_extract(test_enum, "b", .Field, null, null, null, .Failure, LookupError.NotFound); + + const test_enum_empty = enum {}; + + try test_extract(test_enum_empty, "", .Field, null, null, null, .Failure, LookupError.TypeHasNoFields); + try test_extract(test_enum_empty, "", .Declaration, null, null, null, .Failure, LookupError.TypeHasNoDeclarations); + try test_extract(test_enum_empty, "", .Any, null, null, null, .Failure, LookupError.TypeHasNoFieldsOrDeclarations); + + // + // For pointers, optionals, error unions, vectors and arrays + // the test_struct type was used as the base. + // Any other could have been chosen, but I have chosen this as it isolates + // the checking for the base type from the pointer, optional, error union, vector + // and array code-paths for checking (even though they eventually converge). + // + + const test_pointer = *test_struct; + + try test_extract(test_pointer, "a", .Field, i32, .StructField, null, .Success, null); + try test_extract(test_pointer, "a", .Any, i32, .StructField, null, .Success, null); + try test_extract(test_pointer, "b", .Declaration, i64, .Declaration, null, .Success, null); + try test_extract(test_pointer, "b", .Any, i64, .Declaration, null, .Success, null); + + try test_extract(test_pointer, "a", .Declaration, null, null, null, .Failure, LookupError.NotFound); + try test_extract(test_pointer, "b", .Field, null, null, null, .Failure, LookupError.NotFound); + + const test_optional = ?test_struct; + + try test_extract(test_optional, "a", .Field, i32, .StructField, null, .Success, null); + try test_extract(test_optional, "a", .Any, i32, .StructField, null, .Success, null); + try test_extract(test_optional, "b", .Declaration, i64, .Declaration, null, .Success, null); + try test_extract(test_optional, "b", .Any, i64, .Declaration, null, .Success, null); + + try test_extract(test_optional, "a", .Declaration, null, null, null, .Failure, LookupError.NotFound); + try test_extract(test_optional, "b", .Field, null, null, null, .Failure, LookupError.NotFound); + + const test_error_union = anyerror!test_struct; + + try test_extract(test_error_union, "a", .Field, i32, .StructField, null, .Success, null); + try test_extract(test_error_union, "a", .Any, i32, .StructField, null, .Success, null); + try test_extract(test_error_union, "b", .Declaration, i64, .Declaration, null, .Success, null); + try test_extract(test_error_union, "b", .Any, i64, .Declaration, null, .Success, null); + + try test_extract(test_error_union, "a", .Declaration, null, null, null, .Failure, LookupError.NotFound); + try test_extract(test_error_union, "b", .Field, null, null, null, .Failure, LookupError.NotFound); + + const test_vector = @Vector(4, *test_struct); + + try test_extract(test_vector, "a", .Field, i32, .StructField, null, .Success, null); + try test_extract(test_vector, "a", .Any, i32, .StructField, null, .Success, null); + try test_extract(test_vector, "b", .Declaration, i64, .Declaration, null, .Success, null); + try test_extract(test_vector, "b", .Any, i64, .Declaration, null, .Success, null); + + try test_extract(test_vector, "a", .Declaration, null, null, null, .Failure, LookupError.NotFound); + try test_extract(test_vector, "b", .Field, null, null, null, .Failure, LookupError.NotFound); + + const test_array = [4]test_struct; + + try test_extract(test_array, "a", .Field, i32, .StructField, null, .Success, null); + try test_extract(test_array, "a", .Any, i32, .StructField, null, .Success, null); + try test_extract(test_array, "b", .Declaration, i64, .Declaration, null, .Success, null); + try test_extract(test_array, "b", .Any, i64, .Declaration, null, .Success, null); + + try test_extract(test_array, "a", .Declaration, null, null, null, .Failure, LookupError.NotFound); + try test_extract(test_array, "b", .Field, null, null, null, .Failure, LookupError.NotFound); +} diff --git a/src/tests.zig b/src/tests.zig deleted file mode 100644 index 23e7d56..0000000 --- a/src/tests.zig +++ /dev/null @@ -1,257 +0,0 @@ -/// Metaplasia Tests -/// ©2023 Cristian Vasilache (NNCV / Nylvon) -/// -/// Provides an exhaustive test suite for the Metaplasia library. -/// Each test name is formatted as follows: -/// "[FeatureName] [Scenario]" -pub const MetaplasiaTests = @This(); -const Metaplasia = @import("metaplasia.zig"); -const Common = Metaplasia.Common; -const LookupError = Common.LookupError; -const std = @import("std"); -const testing = std.testing; -const expect = testing.expect; -const expectEqual = testing.expectEqual; -const expectError = testing.expectError; - -// FindInType tests - -test "FindInType Struct" { - const sample_type = struct { - a: i32, - pub const b: i64 = 10; - }; - - // Try to find the field - // First two should not error, the third should error - const field_info = try Common.FindInType(sample_type, "a", .Field); - try expect(field_info.GetType() == i32); - const field_info_any = try Common.FindInType(sample_type, "a", .Any); - try expect(field_info_any.GetType() == i32); - const field_error = Common.FindInType(sample_type, "a", .Declaration); - try expectError(LookupError.NotFound, field_error); - - // Try to find the declaration - // // First two should not error, the third should error - const decl_info = try Common.FindInType(sample_type, "b", .Declaration); - try expect(decl_info.GetType() == i64); - const decl_info_any = try Common.FindInType(sample_type, "b", .Any); - try expect(decl_info_any.GetType() == i64); - const decl_error = Common.FindInType(sample_type, "b", .Field); - try expectError(LookupError.NotFound, decl_error); -} - -// test "FindInType Struct Empty" { -// const sample_type = struct {}; - -// const field_error = Common.FindInType(sample_type, "a", .Field); -// try expectError(LookupError.TypeHasNoFields, field_error); -// const decl_error = Common.FindInType(sample_type, "a", .Declaration); -// try expectError(LookupError.TypeHasNoDeclarations, decl_error); -// const any_error = Common.FindInType(sample_type, "a", .Any); -// try expectError(LookupError.TypeHasNoFieldsOrDeclarations, any_error); -// } - -// test "FindInType Union" { -// const sample_type = union { -// a: i32, -// pub const b: i64 = 10; -// }; - -// // Try to find the field -// // First two should not error, the third should error -// const field_type = try Common.FindInType(sample_type, "a", .Field); -// try expect(field_type == i32); -// const field_type_any = try Common.FindInType(sample_type, "a", .Any); -// try expect(field_type_any == i32); -// const field_error = Common.FindInType(sample_type, "a", .Declaration); -// try expectError(LookupError.NotFound, field_error); - -// // Try to find the declaration -// // First two should not error, the third should error -// const decl_type = try Common.FindInType(sample_type, "b", .Declaration); -// try expect(decl_type == i64); -// const decl_type_any = try Common.FindInType(sample_type, "b", .Any); -// try expect(decl_type_any == i64); -// const decl_error = Common.FindInType(sample_type, "b", .Field); -// try expectError(LookupError.NotFound, decl_error); -// } - -// test "FindInType Union Empty" { -// const sample_type = union {}; - -// const field_error = Common.FindInType(sample_type, "a", .Field); -// try expectError(LookupError.TypeHasNoFields, field_error); -// const decl_error = Common.FindInType(sample_type, "a", .Declaration); -// try expectError(LookupError.TypeHasNoDeclarations, decl_error); -// const any_error = Common.FindInType(sample_type, "a", .Any); -// try expectError(LookupError.TypeHasNoFieldsOrDeclarations, any_error); -// } - -// test "FindInType Enum" { -// const sample_type = enum { -// a, -// pub const b: i64 = 10; -// }; - -// // Try to find the field -// // First two should not error, the third should error -// const field_type = try Common.FindInType(sample_type, "a", .Field); -// try expect(field_type == sample_type.a); -// const field_type_any = try Common.FindInType(sample_type, "a", .Any); -// try expect(field_type_any.field == sample_type.a); -// const field_error = Common.FindInType(sample_type, "a", .Declaration); -// try expectError(LookupError.NotFound, field_error); - -// // Try to find the declaration -// // First two should not error, the third should error -// const decl_type = try Common.FindInType(sample_type, "b", .Declaration); -// try expect(decl_type == i64); -// const decl_type_any = try Common.FindInType(sample_type, "b", .Any); -// try expect(decl_type_any.decl == i64); -// const decl_error = Common.FindInType(sample_type, "b", .Field); -// try expectError(LookupError.NotFound, decl_error); -// } - -// test "FindInType Enum Empty" { -// const sample_type_a = enum { a }; - -// // Enums cannot be empty (for now; due to an error in std.fmt:527:46 (@tagName of empty enum is impossible)) -// const decl_error = Common.FindInType(sample_type_a, "a", .Declaration); -// try expectError(LookupError.TypeHasNoDeclarations, decl_error); -// } - -// test "FindInType Pointer" { -// const sample_type = struct { -// a: i32, -// pub const b: i64 = 10; -// }; - -// const target_type = *sample_type; - -// // Try to find the field -// // First two should not error, the third should error -// const field_type = try Common.FindInType(target_type, "a", .Field); -// try expect(field_type == i32); -// const field_type_any = try Common.FindInType(target_type, "a", .Any); -// try expect(field_type_any == i32); -// const field_error = Common.FindInType(target_type, "a", .Declaration); -// try expectError(LookupError.NotFound, field_error); - -// // Try to find the declaration -// // First two should not error, the third should error -// const decl_type = try Common.FindInType(target_type, "b", .Declaration); -// try expect(decl_type == i64); -// const decl_type_any = try Common.FindInType(target_type, "b", .Any); -// try expect(decl_type_any == i64); -// const decl_error = Common.FindInType(target_type, "b", .Field); -// try expectError(LookupError.NotFound, decl_error); -// } - -// test "FindInType Optional" { -// const sample_type = struct { -// a: i32, -// pub const b: i64 = 10; -// }; - -// const target_type = ?sample_type; - -// // Try to find the field -// // First two should not error, the third should error -// const field_type = try Common.FindInType(target_type, "a", .Field); -// try expect(field_type == i32); -// const field_type_any = try Common.FindInType(target_type, "a", .Any); -// try expect(field_type_any == i32); -// const field_error = Common.FindInType(target_type, "a", .Declaration); -// try expectError(LookupError.NotFound, field_error); - -// // Try to find the declaration -// // First two should not error, the third should error -// const decl_type = try Common.FindInType(target_type, "b", .Declaration); -// try expect(decl_type == i64); -// const decl_type_any = try Common.FindInType(target_type, "b", .Any); -// try expect(decl_type_any == i64); -// const decl_error = Common.FindInType(target_type, "b", .Field); -// try expectError(LookupError.NotFound, decl_error); -// } - -// test "FindInType Error Union" { -// const sample_type = struct { -// a: i32, -// pub const b: i64 = 10; -// }; - -// const target_type = anyerror!sample_type; - -// // Try to find the field -// // First two should not error, the third should error -// const field_type = try Common.FindInType(target_type, "a", .Field); -// try expect(field_type == i32); -// const field_type_any = try Common.FindInType(target_type, "a", .Any); -// try expect(field_type_any == i32); -// const field_error = Common.FindInType(target_type, "a", .Declaration); -// try expectError(LookupError.NotFound, field_error); - -// // Try to find the declaration -// // First two should not error, the third should error -// const decl_type = try Common.FindInType(target_type, "b", .Declaration); -// try expect(decl_type == i64); -// const decl_type_any = try Common.FindInType(target_type, "b", .Any); -// try expect(decl_type_any == i64); -// const decl_error = Common.FindInType(target_type, "b", .Field); -// try expectError(LookupError.NotFound, decl_error); -// } - -// test "FindInType Vector" { -// const sample_type = struct { -// a: i32, -// pub const b: i64 = 10; -// }; - -// const target_type = @Vector(4, *sample_type); - -// // Try to find the field -// // First two should not error, the third should error -// const field_type = try Common.FindInType(target_type, "a", .Field); -// try expect(field_type == i32); -// const field_type_any = try Common.FindInType(target_type, "a", .Any); -// try expect(field_type_any == i32); -// const field_error = Common.FindInType(target_type, "a", .Declaration); -// try expectError(LookupError.NotFound, field_error); - -// // Try to find the declaration -// // First two should not error, the third should error -// const decl_type = try Common.FindInType(target_type, "b", .Declaration); -// try expect(decl_type == i64); -// const decl_type_any = try Common.FindInType(target_type, "b", .Any); -// try expect(decl_type_any == i64); -// const decl_error = Common.FindInType(target_type, "b", .Field); -// try expectError(LookupError.NotFound, decl_error); -// } - -// test "FindInType Array" { -// const sample_type = struct { -// a: i32, -// pub const b: i64 = 10; -// }; - -// const target_type = [4]sample_type; - -// // Try to find the field -// // First two should not error, the third should error -// const field_type = try Common.FindInType(target_type, "a", .Field); -// try expect(field_type == i32); -// const field_type_any = try Common.FindInType(target_type, "a", .Any); -// try expect(field_type_any == i32); -// const field_error = Common.FindInType(target_type, "a", .Declaration); -// try expectError(LookupError.NotFound, field_error); - -// // Try to find the declaration -// // First two should not error, the third should error -// const decl_type = try Common.FindInType(target_type, "b", .Declaration); -// try expect(decl_type == i64); -// const decl_type_any = try Common.FindInType(target_type, "b", .Any); -// try expect(decl_type_any == i64); -// const decl_error = Common.FindInType(target_type, "b", .Field); -// try expectError(LookupError.NotFound, decl_error); -// } From ffc18abfcc5f09027d67d49115542b586a72ab27 Mon Sep 17 00:00:00 2001 From: Nylvon Date: Tue, 21 Nov 2023 12:48:19 +0200 Subject: [PATCH 08/16] Reduce redundancy for the .Success branch of the test_extract function --- src/common_tests.zig | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/common_tests.zig b/src/common_tests.zig index 69dc475..5002127 100644 --- a/src/common_tests.zig +++ b/src/common_tests.zig @@ -37,11 +37,6 @@ pub fn test_extract( ) !void { switch (e_outcome) { .Success => { - // Sanity-checks - try expect(e_type != null); - try expect(e_kind != null); - try expect(e_error == null); - const info = try Common.FindInType(target, l_name, l_mode); if (e_kind) |kind| { From 4d37494d297ae2466209e6c1b00be5380bc78023 Mon Sep 17 00:00:00 2001 From: Nylvon Date: Sun, 10 Dec 2023 14:59:11 +0200 Subject: [PATCH 09/16] Implement base rules and rule pseudo-interface. Add LookupData wrapper to Common and rename test function for Common. --- src/common.zig | 14 ++- src/common_tests.zig | 117 +++++++++--------- src/rule.zig | 283 +++++++++++++++++++++++++++++++++++-------- 3 files changed, 305 insertions(+), 109 deletions(-) diff --git a/src/common.zig b/src/common.zig index ac53a45..a2d1871 100644 --- a/src/common.zig +++ b/src/common.zig @@ -5,6 +5,13 @@ pub const Common = @This(); const std = @import("std"); +/// Small container for look-ups. +/// TODO: Refactor using this type. +pub const LookupData = struct { + lookup_name: []const u8, + lookup_mode: LookupMode, +}; + /// Restricts look-up to only fields or declarations, or does not restrict at all. /// If Any is selected, fields are prioritised over declarations in the look-up order. pub const LookupMode = enum { Field, Declaration, Any }; @@ -39,6 +46,9 @@ pub const TypeItemKind = enum { UnionField, }; +/// The mutability of a TypeItem entity. +pub const TypeItemMutability = enum { Variable, Constant }; + /// A Type Item is a part of a type, it is a wrapper over /// fields and declarations of all types. pub const TypeItem = union(TypeItemKind) { @@ -77,12 +87,12 @@ pub const TypeItem = union(TypeItemKind) { /// If the entity doesn't have a default value, null will be returned. /// If the entity cannot have a default value, an error will be returned. pub fn GetDefaultValue(comptime self: @This()) switch (self) { - .Declaration, .EnumField, .UnionField => TypeItem.Error, .StructField => ?*const anyopaque, + else => TypeItem.Error, } { switch (self) { - .Declaration, .EnumField, .UnionField => return TypeItem.Error.InvalidType, .StructField => |sf| return sf.default_value, + else => return TypeItem.Error.InvalidType, } } }; diff --git a/src/common_tests.zig b/src/common_tests.zig index 5002127..f0dcb2d 100644 --- a/src/common_tests.zig +++ b/src/common_tests.zig @@ -9,6 +9,7 @@ pub const CommonTests = @This(); const Common = @import("common.zig").Common; const TypeItemKind = Common.TypeItemKind; +const TypeItemMutability = Common.TypeItemMutability; const LookupMode = Common.LookupMode; const LookupError = Common.LookupError; const std = @import("std"); @@ -23,7 +24,7 @@ const eql = std.mem.eql; /// Convenience wrapper for testing the /// FindInType function over types. -pub fn test_extract( +pub fn test_findintype( // L = Look-up // E = Expected comptime target: type, @@ -72,38 +73,38 @@ test "FindInType" { pub const b: i64 = 10; }; - try test_extract(test_struct, "a", .Field, i32, .StructField, null, .Success, null); - try test_extract(test_struct, "a", .Any, i32, .StructField, null, .Success, null); - try test_extract(test_struct, "b", .Declaration, i64, .Declaration, null, .Success, null); - try test_extract(test_struct, "b", .Any, i64, .Declaration, null, .Success, null); + try test_findintype(test_struct, "a", .Field, i32, .StructField, null, .Success, null); + try test_findintype(test_struct, "a", .Any, i32, .StructField, null, .Success, null); + try test_findintype(test_struct, "b", .Declaration, i64, .Declaration, null, .Success, null); + try test_findintype(test_struct, "b", .Any, i64, .Declaration, null, .Success, null); - try test_extract(test_struct, "a", .Declaration, null, null, null, .Failure, LookupError.NotFound); - try test_extract(test_struct, "b", .Field, null, null, null, .Failure, LookupError.NotFound); + try test_findintype(test_struct, "a", .Declaration, null, null, null, .Failure, LookupError.NotFound); + try test_findintype(test_struct, "b", .Field, null, null, null, .Failure, LookupError.NotFound); const test_struct_empty = struct {}; - try test_extract(test_struct_empty, "", .Field, null, null, null, .Failure, LookupError.TypeHasNoFields); - try test_extract(test_struct_empty, "", .Declaration, null, null, null, .Failure, LookupError.TypeHasNoDeclarations); - try test_extract(test_struct_empty, "", .Any, null, null, null, .Failure, LookupError.TypeHasNoFieldsOrDeclarations); + try test_findintype(test_struct_empty, "", .Field, null, null, null, .Failure, LookupError.TypeHasNoFields); + try test_findintype(test_struct_empty, "", .Declaration, null, null, null, .Failure, LookupError.TypeHasNoDeclarations); + try test_findintype(test_struct_empty, "", .Any, null, null, null, .Failure, LookupError.TypeHasNoFieldsOrDeclarations); const test_union = union { a: i32, pub const b: i64 = 10; }; - try test_extract(test_union, "a", .Field, i32, .UnionField, null, .Success, null); - try test_extract(test_union, "a", .Any, i32, .UnionField, null, .Success, null); - try test_extract(test_union, "b", .Declaration, i64, .Declaration, null, .Success, null); - try test_extract(test_union, "b", .Any, i64, .Declaration, null, .Success, null); + try test_findintype(test_union, "a", .Field, i32, .UnionField, null, .Success, null); + try test_findintype(test_union, "a", .Any, i32, .UnionField, null, .Success, null); + try test_findintype(test_union, "b", .Declaration, i64, .Declaration, null, .Success, null); + try test_findintype(test_union, "b", .Any, i64, .Declaration, null, .Success, null); - try test_extract(test_union, "a", .Declaration, null, null, null, .Failure, LookupError.NotFound); - try test_extract(test_union, "b", .Field, null, null, null, .Failure, LookupError.NotFound); + try test_findintype(test_union, "a", .Declaration, null, null, null, .Failure, LookupError.NotFound); + try test_findintype(test_union, "b", .Field, null, null, null, .Failure, LookupError.NotFound); const test_union_empty = union {}; - try test_extract(test_union_empty, "", .Field, null, null, null, .Failure, LookupError.TypeHasNoFields); - try test_extract(test_union_empty, "", .Declaration, null, null, null, .Failure, LookupError.TypeHasNoDeclarations); - try test_extract(test_union_empty, "", .Any, null, null, null, .Failure, LookupError.TypeHasNoFieldsOrDeclarations); + try test_findintype(test_union_empty, "", .Field, null, null, null, .Failure, LookupError.TypeHasNoFields); + try test_findintype(test_union_empty, "", .Declaration, null, null, null, .Failure, LookupError.TypeHasNoDeclarations); + try test_findintype(test_union_empty, "", .Any, null, null, null, .Failure, LookupError.TypeHasNoFieldsOrDeclarations); const test_enum = enum { a, @@ -112,19 +113,19 @@ test "FindInType" { // NOTE: Rethink the expected type on an enum. Maybe the enum should return the actual enum selector? // Maybe a different function would be of use. - try test_extract(test_enum, "a", .Field, test_enum, .EnumField, null, .Success, null); - try test_extract(test_enum, "a", .Any, test_enum, .EnumField, null, .Success, null); - try test_extract(test_enum, "b", .Declaration, i64, .Declaration, null, .Success, null); - try test_extract(test_enum, "b", .Any, i64, .Declaration, null, .Success, null); + try test_findintype(test_enum, "a", .Field, test_enum, .EnumField, null, .Success, null); + try test_findintype(test_enum, "a", .Any, test_enum, .EnumField, null, .Success, null); + try test_findintype(test_enum, "b", .Declaration, i64, .Declaration, null, .Success, null); + try test_findintype(test_enum, "b", .Any, i64, .Declaration, null, .Success, null); - try test_extract(test_enum, "a", .Declaration, null, null, null, .Failure, LookupError.NotFound); - try test_extract(test_enum, "b", .Field, null, null, null, .Failure, LookupError.NotFound); + try test_findintype(test_enum, "a", .Declaration, null, null, null, .Failure, LookupError.NotFound); + try test_findintype(test_enum, "b", .Field, null, null, null, .Failure, LookupError.NotFound); const test_enum_empty = enum {}; - try test_extract(test_enum_empty, "", .Field, null, null, null, .Failure, LookupError.TypeHasNoFields); - try test_extract(test_enum_empty, "", .Declaration, null, null, null, .Failure, LookupError.TypeHasNoDeclarations); - try test_extract(test_enum_empty, "", .Any, null, null, null, .Failure, LookupError.TypeHasNoFieldsOrDeclarations); + try test_findintype(test_enum_empty, "", .Field, null, null, null, .Failure, LookupError.TypeHasNoFields); + try test_findintype(test_enum_empty, "", .Declaration, null, null, null, .Failure, LookupError.TypeHasNoDeclarations); + try test_findintype(test_enum_empty, "", .Any, null, null, null, .Failure, LookupError.TypeHasNoFieldsOrDeclarations); // // For pointers, optionals, error unions, vectors and arrays @@ -136,51 +137,51 @@ test "FindInType" { const test_pointer = *test_struct; - try test_extract(test_pointer, "a", .Field, i32, .StructField, null, .Success, null); - try test_extract(test_pointer, "a", .Any, i32, .StructField, null, .Success, null); - try test_extract(test_pointer, "b", .Declaration, i64, .Declaration, null, .Success, null); - try test_extract(test_pointer, "b", .Any, i64, .Declaration, null, .Success, null); + try test_findintype(test_pointer, "a", .Field, i32, .StructField, null, .Success, null); + try test_findintype(test_pointer, "a", .Any, i32, .StructField, null, .Success, null); + try test_findintype(test_pointer, "b", .Declaration, i64, .Declaration, null, .Success, null); + try test_findintype(test_pointer, "b", .Any, i64, .Declaration, null, .Success, null); - try test_extract(test_pointer, "a", .Declaration, null, null, null, .Failure, LookupError.NotFound); - try test_extract(test_pointer, "b", .Field, null, null, null, .Failure, LookupError.NotFound); + try test_findintype(test_pointer, "a", .Declaration, null, null, null, .Failure, LookupError.NotFound); + try test_findintype(test_pointer, "b", .Field, null, null, null, .Failure, LookupError.NotFound); const test_optional = ?test_struct; - try test_extract(test_optional, "a", .Field, i32, .StructField, null, .Success, null); - try test_extract(test_optional, "a", .Any, i32, .StructField, null, .Success, null); - try test_extract(test_optional, "b", .Declaration, i64, .Declaration, null, .Success, null); - try test_extract(test_optional, "b", .Any, i64, .Declaration, null, .Success, null); + try test_findintype(test_optional, "a", .Field, i32, .StructField, null, .Success, null); + try test_findintype(test_optional, "a", .Any, i32, .StructField, null, .Success, null); + try test_findintype(test_optional, "b", .Declaration, i64, .Declaration, null, .Success, null); + try test_findintype(test_optional, "b", .Any, i64, .Declaration, null, .Success, null); - try test_extract(test_optional, "a", .Declaration, null, null, null, .Failure, LookupError.NotFound); - try test_extract(test_optional, "b", .Field, null, null, null, .Failure, LookupError.NotFound); + try test_findintype(test_optional, "a", .Declaration, null, null, null, .Failure, LookupError.NotFound); + try test_findintype(test_optional, "b", .Field, null, null, null, .Failure, LookupError.NotFound); const test_error_union = anyerror!test_struct; - try test_extract(test_error_union, "a", .Field, i32, .StructField, null, .Success, null); - try test_extract(test_error_union, "a", .Any, i32, .StructField, null, .Success, null); - try test_extract(test_error_union, "b", .Declaration, i64, .Declaration, null, .Success, null); - try test_extract(test_error_union, "b", .Any, i64, .Declaration, null, .Success, null); + try test_findintype(test_error_union, "a", .Field, i32, .StructField, null, .Success, null); + try test_findintype(test_error_union, "a", .Any, i32, .StructField, null, .Success, null); + try test_findintype(test_error_union, "b", .Declaration, i64, .Declaration, null, .Success, null); + try test_findintype(test_error_union, "b", .Any, i64, .Declaration, null, .Success, null); - try test_extract(test_error_union, "a", .Declaration, null, null, null, .Failure, LookupError.NotFound); - try test_extract(test_error_union, "b", .Field, null, null, null, .Failure, LookupError.NotFound); + try test_findintype(test_error_union, "a", .Declaration, null, null, null, .Failure, LookupError.NotFound); + try test_findintype(test_error_union, "b", .Field, null, null, null, .Failure, LookupError.NotFound); const test_vector = @Vector(4, *test_struct); - try test_extract(test_vector, "a", .Field, i32, .StructField, null, .Success, null); - try test_extract(test_vector, "a", .Any, i32, .StructField, null, .Success, null); - try test_extract(test_vector, "b", .Declaration, i64, .Declaration, null, .Success, null); - try test_extract(test_vector, "b", .Any, i64, .Declaration, null, .Success, null); + try test_findintype(test_vector, "a", .Field, i32, .StructField, null, .Success, null); + try test_findintype(test_vector, "a", .Any, i32, .StructField, null, .Success, null); + try test_findintype(test_vector, "b", .Declaration, i64, .Declaration, null, .Success, null); + try test_findintype(test_vector, "b", .Any, i64, .Declaration, null, .Success, null); - try test_extract(test_vector, "a", .Declaration, null, null, null, .Failure, LookupError.NotFound); - try test_extract(test_vector, "b", .Field, null, null, null, .Failure, LookupError.NotFound); + try test_findintype(test_vector, "a", .Declaration, null, null, null, .Failure, LookupError.NotFound); + try test_findintype(test_vector, "b", .Field, null, null, null, .Failure, LookupError.NotFound); const test_array = [4]test_struct; - try test_extract(test_array, "a", .Field, i32, .StructField, null, .Success, null); - try test_extract(test_array, "a", .Any, i32, .StructField, null, .Success, null); - try test_extract(test_array, "b", .Declaration, i64, .Declaration, null, .Success, null); - try test_extract(test_array, "b", .Any, i64, .Declaration, null, .Success, null); + try test_findintype(test_array, "a", .Field, i32, .StructField, null, .Success, null); + try test_findintype(test_array, "a", .Any, i32, .StructField, null, .Success, null); + try test_findintype(test_array, "b", .Declaration, i64, .Declaration, null, .Success, null); + try test_findintype(test_array, "b", .Any, i64, .Declaration, null, .Success, null); - try test_extract(test_array, "a", .Declaration, null, null, null, .Failure, LookupError.NotFound); - try test_extract(test_array, "b", .Field, null, null, null, .Failure, LookupError.NotFound); + try test_findintype(test_array, "a", .Declaration, null, null, null, .Failure, LookupError.NotFound); + try test_findintype(test_array, "b", .Field, null, null, null, .Failure, LookupError.NotFound); } diff --git a/src/rule.zig b/src/rule.zig index f144137..4160650 100644 --- a/src/rule.zig +++ b/src/rule.zig @@ -2,64 +2,249 @@ /// ©2023 Cristian Vasilache (NNCV / Nylvon) /// /// A rule is a constraint for a type. -pub const Rule = @This(); +pub const Rule = Rule_internal; const Common = @import("common.zig"); const std = @import("std"); /// All base rules -pub const Type = enum { - IsField, - IsDeclaration, - IsInType, // Either as a field or as a declaration - IsType, +pub const Kind = enum { + /// Look-up must be within the type definition. + IsInType, + + /// Look-up must be within the definition of any types within the type definition. + /// Depth for look-up is set, and filters are also set. + IsInTypeMembers, + + /// Look-up must be of a given type. + IsOfType, + + /// Look-up must be bound(?) within a given type set (Ints of any bits, etc). + /// TODO: Rethink this. + IsOfTypeKind, + + /// Look-up must be a function type. + IsFunction, + + /// Look-up must be a variable. IsVariable, + + /// Look-up must be a constant. IsConstant, + + /// Look-up must be available at compile time. + IsComptime, + + /// Custom Rules? (?) + CustomDefinition, }; -/// Dictates how a rule should be enforced -pub const Strictness = enum { Required, NotRequired }; - -type: Type, -name: []const u8, -strictness: Strictness, - -/// Checks whether a type matches a rule -pub fn Check(comptime self: *const Rule, comptime target: type) !bool { - switch (self.type) { - .IsField => { - // If there's no types, it'll return false. - // If there's another error, it'll return it. - Common.FindInType(target, self.name, .Field) catch |err| { - switch (err) { - .NotFound => return false, - else => return err, - } - }; +/// An all-encompassing wrapper for rules for types. +/// All rules must validate the IRule interface. +/// TODO: When typechecking is implemented, make sure all +/// rules are checked against it (and pass). +const Rule_internal = union(Kind) { + /// Checks whether a field or declaration is within the definition of the type. + IsInType: struct { + lookup_data: Common.LookupData, - // If we're here, it returned the type, which implies a successful look-up. - return true; - }, - .IsDeclaration => { - Common.FindInType(target, self.name, .Declaration) catch |err| { - switch (err) { - .NotFound => return false, - else => return err, - } + pub fn Check(comptime self: @This(), comptime target: type) !bool { + _ = Common.FindInType(target, self.lookup_data.lookup_name, self.lookup_data.lookup_mode) catch |err| { + if (err == Common.LookupError.NotFound) { + return false; + } else return err; }; - return true; - }, - .IsInType => { - Common.FindInType(target, self.name, .Any) catch |err| { - switch (err) { - .NotFound => return false, - else => return err, - } - }; + } + }, - return true; - }, - else => return error.NotImplemented, // TODO: Implement the rest - // .IsConstant, .IsVariable require a different function that returns data about the field/declaration - } -} + /// Checks whether a field or declaration is a function within the definition of the type. + IsFunction: struct { + lookup_data: Common.LookupData, + + pub fn Check(comptime self: @This(), comptime target: type) !bool { + const lookup = try Common.FindInType(target, self.lookup_data.lookup_name, self.lookup_data.lookup_mode); + switch (@typeInfo(lookup.GetType())) { + .Fn => return true, + else => return false, + } + // Below may do the same thing as above, I can't remember if it is the exact same, though. + // return std.meta.activeTag(@typeInfo(lookup.GetType())) == .Fn; + } + }, + + /// Checks whether a field or declaration is variable within the definition of the type. + IsVariable: struct { + lookup_data: Common.LookupData, + + pub fn Check(comptime self: @This(), comptime target: type) !bool { + const lookup = try Common.FindInType(target, self.lookup_data.lookup_name, self.lookup_data.lookup_mode); + const type_before = lookup.GetType(); + // If it didn't change, it wasn't constant. + return type_before == @constCast(type_before); + } + }, + + /// Checks whether a field or declaration is constant within the definition of the type. + IsConstant: struct { + lookup_data: Common.LookupData, + + pub fn Check(comptime self: @This(), comptime target: type) !bool { + const lookup = try Common.FindInType(target, self.lookup_data.lookup_name, self.lookup_data.lookup_mode); + const type_before = lookup.GetType(); + // If it became variable now, it means that it was constant before. + return type_before != @constCast(type_before); + } + }, + + /// Checks whether a field or declaration is available at compile time within the definition of the type. + IsComptime: struct { + lookup_data: Common.LookupData, + + pub fn Check(comptime self: @This(), comptime target: type) !bool { + const lookup = try Common.FindInType(target, self.lookup_data.lookup_name, self.lookup_data.lookup_mode); + _ = lookup; + // TODO: Add GetIsComptime to TypeItem in Common + return error.NotImplemented; + } + }, + + /// Checks whether a field or declaration is of a specified type within the definition of the type. + IsOfType: struct { + lookup_data: Common.LookupData, + lookup_type: type, + + pub fn Check(comptime self: @This(), comptime target: type) !bool { + const lookup = try Common.FindInType(target, self.lookup_data.lookup_name, self.lookup_data.lookup_mode); + return lookup.GetType() == self.lookup_type; + } + }, + + /// Checks whether the given member is of a specific kind of types. + /// (i.e. std.builtin.Type.Int of any bits) + /// TODO: Re-think this. + /// Maybe we could pull it off with just some custom rules? + IsOfTypeKind: struct { + lookup_data: Common.LookupData, + lookup_kind: std.builtin.Type, + + pub fn Check(comptime self: @This(), comptime target: type) !bool { + _ = target; + _ = self; + // TODO: Come up with a proper definition for this rule. + // Maybe leave it as custom rules. + return error.NotImplemented; + } + }, + + /// Checks according to the custom definition given by a custom descriptor type. + /// All custom descriptors must obey the rule definition interface IRuleDefinition. + /// TODO: Define IRuleDefinition, and make sure all rules are checked against it. + /// TODO: When interfaces are implemented, use an interface typechecker here. + /// (Or maybe somewhere else?). + CustomDefinition: struct { + definition: type, + + // Placeholder for interface typechecker. + // (Will be replaced, of course) + // TODO: Replace with something proper + // when typechecker is implemented. + pub const InterfacePass: bool = false; + + pub fn Check(comptime self: @This(), comptime target: type) !bool { + // Or maybe do the check here before returning? + // TODO: Think about it. + return self.definition.Check(target); + } + }, +}; + +// NOTE: Below is obsolete code, which will get reworked into +// the new framework above, or get removed completely. + +// kind: Kind, +// name: []const u8, +// children: ?[]const Rule, + +// /// Checks whether the children conditions match +// pub fn CheckChildren(comptime self: *const Rule, comptime target: type) !bool { +// var checked = true; +// if (self.children) |*children| { +// for (children) |*c| { +// checked &= c.Check(target); +// } +// } +// return checked; +// } + +// /// Checks whether a type matches a rule +// pub fn Check(comptime self: *const Rule, comptime target: type) !bool { +// switch (self.kind) { +// .IsField => { +// // If there's no types, it'll return false. +// // If there's another error, it'll return it. +// _ = Common.FindInType(target, self.name, .Field) catch |err| { +// switch (err) { +// .NotFound => return false, +// else => return err, +// } +// }; + +// // TODO: Think of a better way to do this. +// var checked = true; +// if (self.children) |*children| { +// for (children) |*c| { +// checked &= c.Check(target); +// } +// } +// return checked; +// }, +// .IsDeclaration => { +// _ = Common.FindInType(target, self.name, .Declaration) catch |err| { +// switch (err) { +// .NotFound => return false, +// else => return err, +// } +// }; + +// return true; +// }, +// .IsInType => { +// _ = Common.FindInType(target, self.name, .Any) catch |err| { +// switch (err) { +// .NotFound => return false, +// else => return err, +// } +// }; + +// return true; +// }, +// .IsFunction => { +// const type_item = Common.FindInType(target, self.name, .Any) catch |err| { +// switch (err) { +// .NotFound => return false, +// else => return err, +// } +// }; + +// const info = @typeInfo(type_item.GetType()); +// switch (info) { +// .Fn => return true, +// else => return false, +// } +// }, +// .IsVariable => { +// const type_item = Common.FindInType(target, self.name, .Declaration) catch |err| { +// switch (err) { +// .NotFound => return false, +// else => return err, +// } +// }; + +// const decl_type = type_item.GetType(); +// if (@constCast(decl_type) == decl_type) { +// return true; +// } else return false; +// }, +// else => return error.NotImplemented, // TODO: Implement the rest +// } +// } From db15a97b2ab6e60256253ac5831097357ab4fdc5 Mon Sep 17 00:00:00 2001 From: Nylvon Date: Mon, 11 Dec 2023 16:23:09 +0200 Subject: [PATCH 10/16] Make rule types explicit, add wrappers, add tests. --- build.zig | 3 +- src/rule.zig | 223 +++++++++++++++++---------------------------- src/rule_tests.zig | 90 ++++++++++++++++++ 3 files changed, 175 insertions(+), 141 deletions(-) create mode 100644 src/rule_tests.zig diff --git a/build.zig b/build.zig index 7595c8c..b7c5fdd 100644 --- a/build.zig +++ b/build.zig @@ -35,5 +35,6 @@ pub fn build(b: *std.Build) void { const test_step = b.step("test", "Run library tests"); - test_step.dependOn(add_tests(b, "test_common", "Run common library tests", "src/common_tests.zig", target, optimize)); + test_step.dependOn(add_tests(b, "common_tests", "Run the unit tests for the Common library.", "src/common_tests.zig", target, optimize)); + test_step.dependOn(add_tests(b, "rule_tests", "Run the unit tests for the Rule type.", "src/rule_tests.zig", target, optimize)); } diff --git a/src/rule.zig b/src/rule.zig index 4160650..2f92745 100644 --- a/src/rule.zig +++ b/src/rule.zig @@ -44,7 +44,64 @@ pub const Kind = enum { /// rules are checked against it (and pass). const Rule_internal = union(Kind) { /// Checks whether a field or declaration is within the definition of the type. - IsInType: struct { + IsInType: IsInTypeRule, + + IsInTypeMembers: IsInTypeRule, + + /// Checks whether a field or declaration is of a specified type within the definition of the type. + IsOfType: IsOfTypeRule, + + /// Checks whether the given member is of a specific kind of types. + /// (i.e. std.builtin.Type.Int of any bits) + /// TODO: Re-think this. + /// Maybe we could pull it off with just some custom rules? + IsOfTypeKind: void, + + /// Checks whether a field or declaration is a function within the definition of the type. + IsFunction: IsFunctionRule, + + /// Checks whether a field or declaration is variable within the definition of the type. + IsVariable: IsVariableRule, + + /// Checks whether a field or declaration is constant within the definition of the type. + IsConstant: IsConstantRule, + + /// Checks whether a field or declaration is available at compile time within the definition of the type. + IsComptime: IsComptimeRule, + + /// Checks according to the custom definition given by a custom descriptor type. + /// All custom descriptors must obey the rule definition interface IRuleDefinition. + /// TODO: Define IRuleDefinition, and make sure all rules are checked against it. + /// TODO: When interfaces are implemented, use an interface typechecker here. + /// (Or maybe somewhere else?). + CustomDefinition: CustomDefinitionRule, + + // + // Rule operations below + // + + /// Calls the rule descriptor's 'Check' function. + pub fn Check(comptime self: @This(), comptime target: type) !bool { + switch (self) { + // NOTE: "ir" stands for "inner rule". + .IsInType => |ir| return ir.Check(target), + .IsInTypeMembers => |ir| return ir.Check(target), + .IsOfType => |ir| return ir.Check(target), + .IsOfTypeKind => |ir| return ir.Check(target), + .IsFunction => |ir| return ir.Check(target), + .IsVariable => |ir| return ir.Check(target), + .IsConstant => |ir| return ir.Check(target), + .IsComptime => |ir| return ir.Check(target), + // .CustomDefinition => |cd| return cd.Check(target), + else => return error.NotImplemented, + } + } + + // + // Rule types below + // + + pub const IsInTypeRule = struct { lookup_data: Common.LookupData, pub fn Check(comptime self: @This(), comptime target: type) !bool { @@ -55,10 +112,19 @@ const Rule_internal = union(Kind) { }; return true; } - }, + }; - /// Checks whether a field or declaration is a function within the definition of the type. - IsFunction: struct { + pub const IsOfTypeRule = struct { + lookup_data: Common.LookupData, + lookup_type: type, + + pub fn Check(comptime self: @This(), comptime target: type) !bool { + const lookup = try Common.FindInType(target, self.lookup_data.lookup_name, self.lookup_data.lookup_mode); + return lookup.GetType() == self.lookup_type; + } + }; + + pub const IsFunctionRule = struct { lookup_data: Common.LookupData, pub fn Check(comptime self: @This(), comptime target: type) !bool { @@ -70,10 +136,9 @@ const Rule_internal = union(Kind) { // Below may do the same thing as above, I can't remember if it is the exact same, though. // return std.meta.activeTag(@typeInfo(lookup.GetType())) == .Fn; } - }, + }; - /// Checks whether a field or declaration is variable within the definition of the type. - IsVariable: struct { + pub const IsVariableRule = struct { lookup_data: Common.LookupData, pub fn Check(comptime self: @This(), comptime target: type) !bool { @@ -82,10 +147,9 @@ const Rule_internal = union(Kind) { // If it didn't change, it wasn't constant. return type_before == @constCast(type_before); } - }, + }; - /// Checks whether a field or declaration is constant within the definition of the type. - IsConstant: struct { + pub const IsConstantRule = struct { lookup_data: Common.LookupData, pub fn Check(comptime self: @This(), comptime target: type) !bool { @@ -94,10 +158,9 @@ const Rule_internal = union(Kind) { // If it became variable now, it means that it was constant before. return type_before != @constCast(type_before); } - }, + }; - /// Checks whether a field or declaration is available at compile time within the definition of the type. - IsComptime: struct { + pub const IsComptimeRule = struct { lookup_data: Common.LookupData, pub fn Check(comptime self: @This(), comptime target: type) !bool { @@ -106,43 +169,14 @@ const Rule_internal = union(Kind) { // TODO: Add GetIsComptime to TypeItem in Common return error.NotImplemented; } - }, - - /// Checks whether a field or declaration is of a specified type within the definition of the type. - IsOfType: struct { - lookup_data: Common.LookupData, - lookup_type: type, - - pub fn Check(comptime self: @This(), comptime target: type) !bool { - const lookup = try Common.FindInType(target, self.lookup_data.lookup_name, self.lookup_data.lookup_mode); - return lookup.GetType() == self.lookup_type; - } - }, + }; - /// Checks whether the given member is of a specific kind of types. - /// (i.e. std.builtin.Type.Int of any bits) - /// TODO: Re-think this. - /// Maybe we could pull it off with just some custom rules? - IsOfTypeKind: struct { - lookup_data: Common.LookupData, - lookup_kind: std.builtin.Type, - - pub fn Check(comptime self: @This(), comptime target: type) !bool { - _ = target; - _ = self; - // TODO: Come up with a proper definition for this rule. - // Maybe leave it as custom rules. - return error.NotImplemented; - } - }, - - /// Checks according to the custom definition given by a custom descriptor type. - /// All custom descriptors must obey the rule definition interface IRuleDefinition. - /// TODO: Define IRuleDefinition, and make sure all rules are checked against it. - /// TODO: When interfaces are implemented, use an interface typechecker here. - /// (Or maybe somewhere else?). - CustomDefinition: struct { + pub const CustomDefinitionRule = struct { + // NOTE: This won't work unless the data is baked into the type. + // TODO: How do we do this? definition: type, + // NOTE: Are anyopaques going to fix this? + //def: anyopaque, // Placeholder for interface typechecker. // (Will be replaced, of course) @@ -155,96 +189,5 @@ const Rule_internal = union(Kind) { // TODO: Think about it. return self.definition.Check(target); } - }, + }; }; - -// NOTE: Below is obsolete code, which will get reworked into -// the new framework above, or get removed completely. - -// kind: Kind, -// name: []const u8, -// children: ?[]const Rule, - -// /// Checks whether the children conditions match -// pub fn CheckChildren(comptime self: *const Rule, comptime target: type) !bool { -// var checked = true; -// if (self.children) |*children| { -// for (children) |*c| { -// checked &= c.Check(target); -// } -// } -// return checked; -// } - -// /// Checks whether a type matches a rule -// pub fn Check(comptime self: *const Rule, comptime target: type) !bool { -// switch (self.kind) { -// .IsField => { -// // If there's no types, it'll return false. -// // If there's another error, it'll return it. -// _ = Common.FindInType(target, self.name, .Field) catch |err| { -// switch (err) { -// .NotFound => return false, -// else => return err, -// } -// }; - -// // TODO: Think of a better way to do this. -// var checked = true; -// if (self.children) |*children| { -// for (children) |*c| { -// checked &= c.Check(target); -// } -// } -// return checked; -// }, -// .IsDeclaration => { -// _ = Common.FindInType(target, self.name, .Declaration) catch |err| { -// switch (err) { -// .NotFound => return false, -// else => return err, -// } -// }; - -// return true; -// }, -// .IsInType => { -// _ = Common.FindInType(target, self.name, .Any) catch |err| { -// switch (err) { -// .NotFound => return false, -// else => return err, -// } -// }; - -// return true; -// }, -// .IsFunction => { -// const type_item = Common.FindInType(target, self.name, .Any) catch |err| { -// switch (err) { -// .NotFound => return false, -// else => return err, -// } -// }; - -// const info = @typeInfo(type_item.GetType()); -// switch (info) { -// .Fn => return true, -// else => return false, -// } -// }, -// .IsVariable => { -// const type_item = Common.FindInType(target, self.name, .Declaration) catch |err| { -// switch (err) { -// .NotFound => return false, -// else => return err, -// } -// }; - -// const decl_type = type_item.GetType(); -// if (@constCast(decl_type) == decl_type) { -// return true; -// } else return false; -// }, -// else => return error.NotImplemented, // TODO: Implement the rest -// } -// } diff --git a/src/rule_tests.zig b/src/rule_tests.zig new file mode 100644 index 0000000..1e76e7c --- /dev/null +++ b/src/rule_tests.zig @@ -0,0 +1,90 @@ +/// Metaplasia Rule Tests +/// ©2023 Cristian Vasilache (NNCV / Nylvon) +/// +/// Provides an exhaustive test suite for the +/// Rule part of the Metaplasia library. +/// +/// Each test name is formatted as follows: +/// "[FeatureName] [Scenario]" +pub const RuleTests = @This(); +const LookupData = @import("common.zig").LookupData; +const Rule = @import("rule.zig").Rule; +const std = @import("std"); +const testing = std.testing; +const expect = testing.expect; +const expectEqual = testing.expectEqual; +const expectError = testing.expectError; +const activeTag = std.meta.activeTag; +const eql = std.mem.eql; + +// Check tests +// NOTE: Crudely implemented just to test out whether the idea works. + +test "Check" { + const test_struct = struct { + a: i32, + pub const b: i64 = 10; + }; + + // Example valid look-up data. + const lookup_a_any = LookupData{ + .lookup_name = "a", + .lookup_mode = .Any, + }; + + // NOTE: All rules below assume a correct look-up. + // TODO: Check behavior when look-up fails and ensure + // it returns the expected look-up error. + // TODO: Tidy up everything, (maybe?) put it in a function. + + // IsInType + const r_isintype = Rule{ .IsInType = .{ + .lookup_data = lookup_a_any, + } }; + const o_isintype = try r_isintype.Check(test_struct); + try expect(o_isintype == true); + + // NOTE: IsInTypeMembers is not checked right now + // because it is still being developed. + + // IsOfType + const r_isoftype = Rule{ .IsOfType = .{ + .lookup_data = lookup_a_any, + .lookup_type = i32, + } }; + const o_isoftype = try r_isoftype.Check(test_struct); + try expect(o_isoftype == true); + + // NOTE: IsOfTypeKind is not checked right now + // because it is still being developed. + + // IsFunction + const r_isfunction = Rule{ .IsFunction = .{ + .lookup_data = lookup_a_any, + } }; + const o_isfunction = try r_isfunction.Check(test_struct); + try expect(o_isfunction == false); + + // TODO: Rework how IsVariable and IsConstant work. + // TODO: Add "GetMutability" function to TypeItem in Common. + // -> It should return whether the member (field/decl) + // -> is mutable (variable) or not (const). + + // IsVariable + // const r_isvariable = Rule{ .IsVariable = .{ + // .lookup_data = lookup_a_any, + // } }; + // const o_isvariable = try r_isvariable.Check(test_struct); + // try expect(o_isvariable == true); + + // IsConstant + // const r_isconstant = Rule{ .IsConstant = .{ + // .lookup_data = lookup_a_any, + // } }; + // const o_isconstant = try r_isconstant.Check(test_struct); + // try expect(o_isconstant == false); + + // NOTE: Add "GetAvailability" function to TypeItem in Common. + // -> It should return if the item is available + // -> at compile time or at runtime (or both). +} From a65217739563d30c313b30f9244088a265cf879e Mon Sep 17 00:00:00 2001 From: Nylvon Date: Mon, 11 Dec 2023 16:48:59 +0200 Subject: [PATCH 11/16] Add new functionality to TypeItem from Common --- src/common.zig | 29 +++++++++++++++++++++++++++++ src/rule_tests.zig | 8 ++++---- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/common.zig b/src/common.zig index a2d1871..2a6b495 100644 --- a/src/common.zig +++ b/src/common.zig @@ -95,6 +95,35 @@ pub const TypeItem = union(TypeItemKind) { else => return TypeItem.Error.InvalidType, } } + + /// Returns whether the entity described has a constant value. + /// NOTE: This is still WIP. + pub fn GetIsConstant(comptime self: @This()) bool { + switch (self) { + .Declaration => |decl| { + switch (@typeInfo(decl.GetType())) { + .Pointer => |ptr| return ptr.is_const, + else => return false, + } + }, + else => return false, + } + } + + /// Returns whether the entity described has a non-constant value. + pub fn GetIsVariable(comptime self: @This()) bool { + return !self.GetIsConstant(); + } + + /// Returns whether the entity described is available at compile time. + /// NOTE: This is still WIP. + pub fn GetIsComptime(comptime self: @This()) bool { + switch (self) { + .StructField => |sf| return sf.is_comptime, + // NOTE: This is not correct, I just have not implemented the rest. + else => return false, + } + } }; /// Internal use only. Looks up a field from a type-info struct. diff --git a/src/rule_tests.zig b/src/rule_tests.zig index 1e76e7c..bee373c 100644 --- a/src/rule_tests.zig +++ b/src/rule_tests.zig @@ -66,9 +66,9 @@ test "Check" { try expect(o_isfunction == false); // TODO: Rework how IsVariable and IsConstant work. - // TODO: Add "GetMutability" function to TypeItem in Common. + // TODO: Add "GetIsConstant" function to TypeItem in Common. // -> It should return whether the member (field/decl) - // -> is mutable (variable) or not (const). + // -> is constant or not. // IsVariable // const r_isvariable = Rule{ .IsVariable = .{ @@ -84,7 +84,7 @@ test "Check" { // const o_isconstant = try r_isconstant.Check(test_struct); // try expect(o_isconstant == false); - // NOTE: Add "GetAvailability" function to TypeItem in Common. + // NOTE: Add "GetIsComptime" function to TypeItem in Common. // -> It should return if the item is available - // -> at compile time or at runtime (or both). + // -> at compile time or not. } From a97cf22760b67b927b985b70091364dcee707139 Mon Sep 17 00:00:00 2001 From: Nylvon Date: Sun, 17 Dec 2023 05:16:48 +0200 Subject: [PATCH 12/16] Clean up unused rules. Set up Custom Definitions Rules. --- src/common.zig | 30 ++++++------------ src/rule.zig | 78 +++++++--------------------------------------- src/rule_tests.zig | 19 ++--------- 3 files changed, 24 insertions(+), 103 deletions(-) diff --git a/src/common.zig b/src/common.zig index 2a6b495..7ea8061 100644 --- a/src/common.zig +++ b/src/common.zig @@ -5,6 +5,10 @@ pub const Common = @This(); const std = @import("std"); +/// NOTE: A temporary string to showcase that a feature is not available due +/// to the language spec not letting it be possible to implement right now. +pub const NotInLangSpecString = "This is not implemented. Zig does not yet expose this information."; + /// Small container for look-ups. /// TODO: Refactor using this type. pub const LookupData = struct { @@ -96,34 +100,20 @@ pub const TypeItem = union(TypeItemKind) { } } + /// ! This is not implemented right now ! + /// ! Zig does not yet expose this information ! /// Returns whether the entity described has a constant value. - /// NOTE: This is still WIP. pub fn GetIsConstant(comptime self: @This()) bool { - switch (self) { - .Declaration => |decl| { - switch (@typeInfo(decl.GetType())) { - .Pointer => |ptr| return ptr.is_const, - else => return false, - } - }, - else => return false, - } + _ = self; + @compileError(NotInLangSpecString); } + /// ! This is not implemented right now ! + /// ! Zig does not yet expose this information ! /// Returns whether the entity described has a non-constant value. pub fn GetIsVariable(comptime self: @This()) bool { return !self.GetIsConstant(); } - - /// Returns whether the entity described is available at compile time. - /// NOTE: This is still WIP. - pub fn GetIsComptime(comptime self: @This()) bool { - switch (self) { - .StructField => |sf| return sf.is_comptime, - // NOTE: This is not correct, I just have not implemented the rest. - else => return false, - } - } }; /// Internal use only. Looks up a field from a type-info struct. diff --git a/src/rule.zig b/src/rule.zig index 2f92745..f1569bc 100644 --- a/src/rule.zig +++ b/src/rule.zig @@ -11,16 +11,8 @@ pub const Kind = enum { /// Look-up must be within the type definition. IsInType, - /// Look-up must be within the definition of any types within the type definition. - /// Depth for look-up is set, and filters are also set. - IsInTypeMembers, - /// Look-up must be of a given type. - IsOfType, - - /// Look-up must be bound(?) within a given type set (Ints of any bits, etc). - /// TODO: Rethink this. - IsOfTypeKind, + IsType, /// Look-up must be a function type. IsFunction, @@ -31,9 +23,6 @@ pub const Kind = enum { /// Look-up must be a constant. IsConstant, - /// Look-up must be available at compile time. - IsComptime, - /// Custom Rules? (?) CustomDefinition, }; @@ -46,16 +35,8 @@ const Rule_internal = union(Kind) { /// Checks whether a field or declaration is within the definition of the type. IsInType: IsInTypeRule, - IsInTypeMembers: IsInTypeRule, - /// Checks whether a field or declaration is of a specified type within the definition of the type. - IsOfType: IsOfTypeRule, - - /// Checks whether the given member is of a specific kind of types. - /// (i.e. std.builtin.Type.Int of any bits) - /// TODO: Re-think this. - /// Maybe we could pull it off with just some custom rules? - IsOfTypeKind: void, + IsType: IsTypeRule, /// Checks whether a field or declaration is a function within the definition of the type. IsFunction: IsFunctionRule, @@ -66,14 +47,7 @@ const Rule_internal = union(Kind) { /// Checks whether a field or declaration is constant within the definition of the type. IsConstant: IsConstantRule, - /// Checks whether a field or declaration is available at compile time within the definition of the type. - IsComptime: IsComptimeRule, - - /// Checks according to the custom definition given by a custom descriptor type. - /// All custom descriptors must obey the rule definition interface IRuleDefinition. - /// TODO: Define IRuleDefinition, and make sure all rules are checked against it. - /// TODO: When interfaces are implemented, use an interface typechecker here. - /// (Or maybe somewhere else?). + /// Checks according to the custom checking function and custom rule data. CustomDefinition: CustomDefinitionRule, // @@ -85,15 +59,11 @@ const Rule_internal = union(Kind) { switch (self) { // NOTE: "ir" stands for "inner rule". .IsInType => |ir| return ir.Check(target), - .IsInTypeMembers => |ir| return ir.Check(target), - .IsOfType => |ir| return ir.Check(target), - .IsOfTypeKind => |ir| return ir.Check(target), + .IsType => |ir| return ir.Check(target), .IsFunction => |ir| return ir.Check(target), .IsVariable => |ir| return ir.Check(target), .IsConstant => |ir| return ir.Check(target), - .IsComptime => |ir| return ir.Check(target), - // .CustomDefinition => |cd| return cd.Check(target), - else => return error.NotImplemented, + .CustomDefinition => |ir| return ir.Check(target), } } @@ -114,7 +84,7 @@ const Rule_internal = union(Kind) { } }; - pub const IsOfTypeRule = struct { + pub const IsTypeRule = struct { lookup_data: Common.LookupData, lookup_type: type, @@ -143,9 +113,7 @@ const Rule_internal = union(Kind) { pub fn Check(comptime self: @This(), comptime target: type) !bool { const lookup = try Common.FindInType(target, self.lookup_data.lookup_name, self.lookup_data.lookup_mode); - const type_before = lookup.GetType(); - // If it didn't change, it wasn't constant. - return type_before == @constCast(type_before); + return lookup.GetIsVariable(); } }; @@ -154,40 +122,16 @@ const Rule_internal = union(Kind) { pub fn Check(comptime self: @This(), comptime target: type) !bool { const lookup = try Common.FindInType(target, self.lookup_data.lookup_name, self.lookup_data.lookup_mode); - const type_before = lookup.GetType(); - // If it became variable now, it means that it was constant before. - return type_before != @constCast(type_before); - } - }; - - pub const IsComptimeRule = struct { - lookup_data: Common.LookupData, - - pub fn Check(comptime self: @This(), comptime target: type) !bool { - const lookup = try Common.FindInType(target, self.lookup_data.lookup_name, self.lookup_data.lookup_mode); - _ = lookup; - // TODO: Add GetIsComptime to TypeItem in Common - return error.NotImplemented; + return lookup.GetIsConstant(); } }; pub const CustomDefinitionRule = struct { - // NOTE: This won't work unless the data is baked into the type. - // TODO: How do we do this? - definition: type, - // NOTE: Are anyopaques going to fix this? - //def: anyopaque, - - // Placeholder for interface typechecker. - // (Will be replaced, of course) - // TODO: Replace with something proper - // when typechecker is implemented. - pub const InterfacePass: bool = false; + check_fn: *const fn (comptime type, comptime *anyopaque) anyerror!bool, + rule_data: *anyopaque, pub fn Check(comptime self: @This(), comptime target: type) !bool { - // Or maybe do the check here before returning? - // TODO: Think about it. - return self.definition.Check(target); + return self.check_fn(target, self.rule_data); } }; }; diff --git a/src/rule_tests.zig b/src/rule_tests.zig index bee373c..4a93951 100644 --- a/src/rule_tests.zig +++ b/src/rule_tests.zig @@ -44,20 +44,14 @@ test "Check" { const o_isintype = try r_isintype.Check(test_struct); try expect(o_isintype == true); - // NOTE: IsInTypeMembers is not checked right now - // because it is still being developed. - // IsOfType - const r_isoftype = Rule{ .IsOfType = .{ + const r_isoftype = Rule{ .IsType = .{ .lookup_data = lookup_a_any, .lookup_type = i32, } }; const o_isoftype = try r_isoftype.Check(test_struct); try expect(o_isoftype == true); - // NOTE: IsOfTypeKind is not checked right now - // because it is still being developed. - // IsFunction const r_isfunction = Rule{ .IsFunction = .{ .lookup_data = lookup_a_any, @@ -65,11 +59,8 @@ test "Check" { const o_isfunction = try r_isfunction.Check(test_struct); try expect(o_isfunction == false); - // TODO: Rework how IsVariable and IsConstant work. - // TODO: Add "GetIsConstant" function to TypeItem in Common. - // -> It should return whether the member (field/decl) - // -> is constant or not. - + // ! IsVariable and IsConstant aren't yet implemented. + // TODO: Once implemented, set up these tests. // IsVariable // const r_isvariable = Rule{ .IsVariable = .{ // .lookup_data = lookup_a_any, @@ -83,8 +74,4 @@ test "Check" { // } }; // const o_isconstant = try r_isconstant.Check(test_struct); // try expect(o_isconstant == false); - - // NOTE: Add "GetIsComptime" function to TypeItem in Common. - // -> It should return if the item is available - // -> at compile time or not. } From 2b503f04bd31f8bf5d1074b5c1b594c500320ea4 Mon Sep 17 00:00:00 2001 From: Nylvon Date: Sat, 23 Dec 2023 07:48:37 +0200 Subject: [PATCH 13/16] Add IsConst and IsVar for Declarations + tests. --- src/common.zig | 31 ++++++----- src/common_tests.zig | 121 +++++++++++++++++++++++-------------------- 2 files changed, 82 insertions(+), 70 deletions(-) diff --git a/src/common.zig b/src/common.zig index 7ea8061..54faa9a 100644 --- a/src/common.zig +++ b/src/common.zig @@ -5,10 +5,6 @@ pub const Common = @This(); const std = @import("std"); -/// NOTE: A temporary string to showcase that a feature is not available due -/// to the language spec not letting it be possible to implement right now. -pub const NotInLangSpecString = "This is not implemented. Zig does not yet expose this information."; - /// Small container for look-ups. /// TODO: Refactor using this type. pub const LookupData = struct { @@ -33,6 +29,7 @@ pub const LookupError = error{ pub const FullDeclaration = struct { decl: std.builtin.Type.Declaration, type: type, + is_const: bool, }; /// Contains the type that the enum field was gotten from as well as the enum field. @@ -100,16 +97,14 @@ pub const TypeItem = union(TypeItemKind) { } } - /// ! This is not implemented right now ! - /// ! Zig does not yet expose this information ! /// Returns whether the entity described has a constant value. pub fn GetIsConstant(comptime self: @This()) bool { - _ = self; - @compileError(NotInLangSpecString); + switch (self) { + .Declaration => |d| return d.is_const, + else => return false, + } } - /// ! This is not implemented right now ! - /// ! Zig does not yet expose this information ! /// Returns whether the entity described has a non-constant value. pub fn GetIsVariable(comptime self: @This()) bool { return !self.GetIsConstant(); @@ -157,7 +152,12 @@ inline fn UnsafeDeclarationLookup(comptime target: type, comptime lookup_name: [ if (type_info.decls.len == 0) return LookupError.TypeHasNoDeclarations; for (type_info.decls) |decl| { - if (std.mem.eql(u8, decl.name, lookup_name)) return TypeItem{ .Declaration = FullDeclaration{ .decl = decl, .type = target } }; + if (std.mem.eql(u8, decl.name, lookup_name)) + return TypeItem{ .Declaration = FullDeclaration{ + .decl = decl, + .type = target, + .is_const = @typeInfo(@TypeOf(&@field(target, decl.name))).Pointer.is_const, + } }; } // If we're here, we haven't found it. @@ -183,13 +183,18 @@ inline fn UnsafeAnyLookup(comptime target: type, comptime lookup_name: []const u .Struct => return TypeItem{ .StructField = field }, .Enum => return TypeItem{ .EnumField = FullEnumField{ .field = field, .type = target } }, .Union => return TypeItem{ .UnionField = field }, - else => @compileError("Please do not use this function on its own, as the checks for its usage are done outside its context!"), + else => @compileError("Please do not use this function on its own, as this function does not do any checks on the inputs!"), } } } for (type_info.decls) |decl| { - if (std.mem.eql(u8, decl.name, lookup_name)) return TypeItem{ .Declaration = FullDeclaration{ .decl = decl, .type = target } }; + if (std.mem.eql(u8, decl.name, lookup_name)) + return TypeItem{ .Declaration = FullDeclaration{ + .decl = decl, + .type = target, + .is_const = @typeInfo(@TypeOf(&@field(target, decl.name))).Pointer.is_const, + } }; } // If we're here, we haven't found it. diff --git a/src/common_tests.zig b/src/common_tests.zig index f0dcb2d..acdaed0 100644 --- a/src/common_tests.zig +++ b/src/common_tests.zig @@ -33,6 +33,7 @@ pub fn test_findintype( comptime e_type: ?type, comptime e_kind: ?TypeItemKind, comptime e_default: ?*const anyopaque, + comptime e_decl_const: ?bool, comptime e_outcome: enum { Success, Failure }, comptime e_error: ?anyerror, ) !void { @@ -53,6 +54,9 @@ pub fn test_findintype( .StructField => { try expect(info.GetDefaultValue() == e_default); }, + .Declaration => { + try expect(info.GetIsConstant() == e_decl_const.?); + }, else => {}, } }, @@ -71,40 +75,43 @@ test "FindInType" { const test_struct = struct { a: i32, pub const b: i64 = 10; + pub var c: i64 = 20; }; - try test_findintype(test_struct, "a", .Field, i32, .StructField, null, .Success, null); - try test_findintype(test_struct, "a", .Any, i32, .StructField, null, .Success, null); - try test_findintype(test_struct, "b", .Declaration, i64, .Declaration, null, .Success, null); - try test_findintype(test_struct, "b", .Any, i64, .Declaration, null, .Success, null); + try test_findintype(test_struct, "a", .Field, i32, .StructField, null, null, .Success, null); + try test_findintype(test_struct, "a", .Any, i32, .StructField, null, null, .Success, null); + try test_findintype(test_struct, "b", .Declaration, i64, .Declaration, null, true, .Success, null); + try test_findintype(test_struct, "b", .Any, i64, .Declaration, null, true, .Success, null); + try test_findintype(test_struct, "c", .Declaration, i64, .Declaration, null, false, .Success, null); + try test_findintype(test_struct, "c", .Any, i64, .Declaration, null, false, .Success, null); - try test_findintype(test_struct, "a", .Declaration, null, null, null, .Failure, LookupError.NotFound); - try test_findintype(test_struct, "b", .Field, null, null, null, .Failure, LookupError.NotFound); + try test_findintype(test_struct, "a", .Declaration, null, null, null, null, .Failure, LookupError.NotFound); + try test_findintype(test_struct, "b", .Field, null, null, null, null, .Failure, LookupError.NotFound); const test_struct_empty = struct {}; - try test_findintype(test_struct_empty, "", .Field, null, null, null, .Failure, LookupError.TypeHasNoFields); - try test_findintype(test_struct_empty, "", .Declaration, null, null, null, .Failure, LookupError.TypeHasNoDeclarations); - try test_findintype(test_struct_empty, "", .Any, null, null, null, .Failure, LookupError.TypeHasNoFieldsOrDeclarations); + try test_findintype(test_struct_empty, "", .Field, null, null, null, null, .Failure, LookupError.TypeHasNoFields); + try test_findintype(test_struct_empty, "", .Declaration, null, null, null, null, .Failure, LookupError.TypeHasNoDeclarations); + try test_findintype(test_struct_empty, "", .Any, null, null, null, null, .Failure, LookupError.TypeHasNoFieldsOrDeclarations); const test_union = union { a: i32, pub const b: i64 = 10; }; - try test_findintype(test_union, "a", .Field, i32, .UnionField, null, .Success, null); - try test_findintype(test_union, "a", .Any, i32, .UnionField, null, .Success, null); - try test_findintype(test_union, "b", .Declaration, i64, .Declaration, null, .Success, null); - try test_findintype(test_union, "b", .Any, i64, .Declaration, null, .Success, null); + try test_findintype(test_union, "a", .Field, i32, .UnionField, null, null, .Success, null); + try test_findintype(test_union, "a", .Any, i32, .UnionField, null, null, .Success, null); + try test_findintype(test_union, "b", .Declaration, i64, .Declaration, null, true, .Success, null); + try test_findintype(test_union, "b", .Any, i64, .Declaration, null, true, .Success, null); - try test_findintype(test_union, "a", .Declaration, null, null, null, .Failure, LookupError.NotFound); - try test_findintype(test_union, "b", .Field, null, null, null, .Failure, LookupError.NotFound); + try test_findintype(test_union, "a", .Declaration, null, null, null, null, .Failure, LookupError.NotFound); + try test_findintype(test_union, "b", .Field, null, null, null, null, .Failure, LookupError.NotFound); const test_union_empty = union {}; - try test_findintype(test_union_empty, "", .Field, null, null, null, .Failure, LookupError.TypeHasNoFields); - try test_findintype(test_union_empty, "", .Declaration, null, null, null, .Failure, LookupError.TypeHasNoDeclarations); - try test_findintype(test_union_empty, "", .Any, null, null, null, .Failure, LookupError.TypeHasNoFieldsOrDeclarations); + try test_findintype(test_union_empty, "", .Field, null, null, null, null, .Failure, LookupError.TypeHasNoFields); + try test_findintype(test_union_empty, "", .Declaration, null, null, null, null, .Failure, LookupError.TypeHasNoDeclarations); + try test_findintype(test_union_empty, "", .Any, null, null, null, null, .Failure, LookupError.TypeHasNoFieldsOrDeclarations); const test_enum = enum { a, @@ -113,19 +120,19 @@ test "FindInType" { // NOTE: Rethink the expected type on an enum. Maybe the enum should return the actual enum selector? // Maybe a different function would be of use. - try test_findintype(test_enum, "a", .Field, test_enum, .EnumField, null, .Success, null); - try test_findintype(test_enum, "a", .Any, test_enum, .EnumField, null, .Success, null); - try test_findintype(test_enum, "b", .Declaration, i64, .Declaration, null, .Success, null); - try test_findintype(test_enum, "b", .Any, i64, .Declaration, null, .Success, null); + try test_findintype(test_enum, "a", .Field, test_enum, .EnumField, null, null, .Success, null); + try test_findintype(test_enum, "a", .Any, test_enum, .EnumField, null, null, .Success, null); + try test_findintype(test_enum, "b", .Declaration, i64, .Declaration, null, true, .Success, null); + try test_findintype(test_enum, "b", .Any, i64, .Declaration, null, true, .Success, null); - try test_findintype(test_enum, "a", .Declaration, null, null, null, .Failure, LookupError.NotFound); - try test_findintype(test_enum, "b", .Field, null, null, null, .Failure, LookupError.NotFound); + try test_findintype(test_enum, "a", .Declaration, null, null, null, null, .Failure, LookupError.NotFound); + try test_findintype(test_enum, "b", .Field, null, null, null, null, .Failure, LookupError.NotFound); const test_enum_empty = enum {}; - try test_findintype(test_enum_empty, "", .Field, null, null, null, .Failure, LookupError.TypeHasNoFields); - try test_findintype(test_enum_empty, "", .Declaration, null, null, null, .Failure, LookupError.TypeHasNoDeclarations); - try test_findintype(test_enum_empty, "", .Any, null, null, null, .Failure, LookupError.TypeHasNoFieldsOrDeclarations); + try test_findintype(test_enum_empty, "", .Field, null, null, null, null, .Failure, LookupError.TypeHasNoFields); + try test_findintype(test_enum_empty, "", .Declaration, null, null, null, null, .Failure, LookupError.TypeHasNoDeclarations); + try test_findintype(test_enum_empty, "", .Any, null, null, null, null, .Failure, LookupError.TypeHasNoFieldsOrDeclarations); // // For pointers, optionals, error unions, vectors and arrays @@ -137,51 +144,51 @@ test "FindInType" { const test_pointer = *test_struct; - try test_findintype(test_pointer, "a", .Field, i32, .StructField, null, .Success, null); - try test_findintype(test_pointer, "a", .Any, i32, .StructField, null, .Success, null); - try test_findintype(test_pointer, "b", .Declaration, i64, .Declaration, null, .Success, null); - try test_findintype(test_pointer, "b", .Any, i64, .Declaration, null, .Success, null); + try test_findintype(test_pointer, "a", .Field, i32, .StructField, null, null, .Success, null); + try test_findintype(test_pointer, "a", .Any, i32, .StructField, null, null, .Success, null); + try test_findintype(test_pointer, "b", .Declaration, i64, .Declaration, null, true, .Success, null); + try test_findintype(test_pointer, "b", .Any, i64, .Declaration, null, true, .Success, null); - try test_findintype(test_pointer, "a", .Declaration, null, null, null, .Failure, LookupError.NotFound); - try test_findintype(test_pointer, "b", .Field, null, null, null, .Failure, LookupError.NotFound); + try test_findintype(test_pointer, "a", .Declaration, null, null, null, null, .Failure, LookupError.NotFound); + try test_findintype(test_pointer, "b", .Field, null, null, null, null, .Failure, LookupError.NotFound); const test_optional = ?test_struct; - try test_findintype(test_optional, "a", .Field, i32, .StructField, null, .Success, null); - try test_findintype(test_optional, "a", .Any, i32, .StructField, null, .Success, null); - try test_findintype(test_optional, "b", .Declaration, i64, .Declaration, null, .Success, null); - try test_findintype(test_optional, "b", .Any, i64, .Declaration, null, .Success, null); + try test_findintype(test_optional, "a", .Field, i32, .StructField, null, null, .Success, null); + try test_findintype(test_optional, "a", .Any, i32, .StructField, null, null, .Success, null); + try test_findintype(test_optional, "b", .Declaration, i64, .Declaration, null, true, .Success, null); + try test_findintype(test_optional, "b", .Any, i64, .Declaration, null, true, .Success, null); - try test_findintype(test_optional, "a", .Declaration, null, null, null, .Failure, LookupError.NotFound); - try test_findintype(test_optional, "b", .Field, null, null, null, .Failure, LookupError.NotFound); + try test_findintype(test_optional, "a", .Declaration, null, null, null, null, .Failure, LookupError.NotFound); + try test_findintype(test_optional, "b", .Field, null, null, null, null, .Failure, LookupError.NotFound); const test_error_union = anyerror!test_struct; - try test_findintype(test_error_union, "a", .Field, i32, .StructField, null, .Success, null); - try test_findintype(test_error_union, "a", .Any, i32, .StructField, null, .Success, null); - try test_findintype(test_error_union, "b", .Declaration, i64, .Declaration, null, .Success, null); - try test_findintype(test_error_union, "b", .Any, i64, .Declaration, null, .Success, null); + try test_findintype(test_error_union, "a", .Field, i32, .StructField, null, null, .Success, null); + try test_findintype(test_error_union, "a", .Any, i32, .StructField, null, null, .Success, null); + try test_findintype(test_error_union, "b", .Declaration, i64, .Declaration, null, true, .Success, null); + try test_findintype(test_error_union, "b", .Any, i64, .Declaration, null, true, .Success, null); - try test_findintype(test_error_union, "a", .Declaration, null, null, null, .Failure, LookupError.NotFound); - try test_findintype(test_error_union, "b", .Field, null, null, null, .Failure, LookupError.NotFound); + try test_findintype(test_error_union, "a", .Declaration, null, null, null, null, .Failure, LookupError.NotFound); + try test_findintype(test_error_union, "b", .Field, null, null, null, null, .Failure, LookupError.NotFound); const test_vector = @Vector(4, *test_struct); - try test_findintype(test_vector, "a", .Field, i32, .StructField, null, .Success, null); - try test_findintype(test_vector, "a", .Any, i32, .StructField, null, .Success, null); - try test_findintype(test_vector, "b", .Declaration, i64, .Declaration, null, .Success, null); - try test_findintype(test_vector, "b", .Any, i64, .Declaration, null, .Success, null); + try test_findintype(test_vector, "a", .Field, i32, .StructField, null, null, .Success, null); + try test_findintype(test_vector, "a", .Any, i32, .StructField, null, null, .Success, null); + try test_findintype(test_vector, "b", .Declaration, i64, .Declaration, null, true, .Success, null); + try test_findintype(test_vector, "b", .Any, i64, .Declaration, null, true, .Success, null); - try test_findintype(test_vector, "a", .Declaration, null, null, null, .Failure, LookupError.NotFound); - try test_findintype(test_vector, "b", .Field, null, null, null, .Failure, LookupError.NotFound); + try test_findintype(test_vector, "a", .Declaration, null, null, null, null, .Failure, LookupError.NotFound); + try test_findintype(test_vector, "b", .Field, null, null, null, null, .Failure, LookupError.NotFound); const test_array = [4]test_struct; - try test_findintype(test_array, "a", .Field, i32, .StructField, null, .Success, null); - try test_findintype(test_array, "a", .Any, i32, .StructField, null, .Success, null); - try test_findintype(test_array, "b", .Declaration, i64, .Declaration, null, .Success, null); - try test_findintype(test_array, "b", .Any, i64, .Declaration, null, .Success, null); + try test_findintype(test_array, "a", .Field, i32, .StructField, null, null, .Success, null); + try test_findintype(test_array, "a", .Any, i32, .StructField, null, null, .Success, null); + try test_findintype(test_array, "b", .Declaration, i64, .Declaration, null, true, .Success, null); + try test_findintype(test_array, "b", .Any, i64, .Declaration, null, true, .Success, null); - try test_findintype(test_array, "a", .Declaration, null, null, null, .Failure, LookupError.NotFound); - try test_findintype(test_array, "b", .Field, null, null, null, .Failure, LookupError.NotFound); + try test_findintype(test_array, "a", .Declaration, null, null, null, null, .Failure, LookupError.NotFound); + try test_findintype(test_array, "b", .Field, null, null, null, null, .Failure, LookupError.NotFound); } From 7e3fc26987e3bec846f5ba0ac179d18aebaec0f4 Mon Sep 17 00:00:00 2001 From: Nylvon Date: Sat, 23 Dec 2023 08:18:02 +0200 Subject: [PATCH 14/16] Add GetBaseType, simplify functions, work on rules. --- src/common.zig | 28 +++++++++---------- src/rule.zig | 14 ++++++++-- src/rule_tests.zig | 68 ++++++++++++++++++++++++++++++++++------------ 3 files changed, 75 insertions(+), 35 deletions(-) diff --git a/src/common.zig b/src/common.zig index 54faa9a..0610162 100644 --- a/src/common.zig +++ b/src/common.zig @@ -230,21 +230,21 @@ pub fn FindInType(comptime from: type, comptime lookup_name: []const u8, comptim .Struct, .Union, .Enum => { return UnsafeLookup(from, lookup_name, lookup_mode); }, - .Pointer => |ptr_info| { - return FindInType(ptr_info.child, lookup_name, lookup_mode); - }, - .Vector => |vec_info| { - return FindInType(vec_info.child, lookup_name, lookup_mode); - }, - .Optional => |opt_info| { - return FindInType(opt_info.child, lookup_name, lookup_mode); - }, - .Array => |arr_info| { - return FindInType(arr_info.child, lookup_name, lookup_mode); - }, - .ErrorUnion => |eru_info| { - return FindInType(eru_info.payload, lookup_name, lookup_mode); + .Pointer, .Vector, .Optional, .Array, .ErrorUnion => { + return FindInType(GetBaseType(from), lookup_name, lookup_mode); }, else => return LookupError.InvalidType, } } + +/// Gets the base type of any pointer/vector/array/etc type. +pub fn GetBaseType(comptime from: type) type { + switch (@typeInfo(from)) { + .Pointer => |ptr| return GetBaseType(ptr.child), + .Vector => |vec| return GetBaseType(vec.child), + .Optional => |opt| return GetBaseType(opt.child), + .Array => |arr| return GetBaseType(arr.child), + .ErrorUnion => |eru| return GetBaseType(eru.payload), + else => return from, + } +} diff --git a/src/rule.zig b/src/rule.zig index f1569bc..c1c6141 100644 --- a/src/rule.zig +++ b/src/rule.zig @@ -39,6 +39,8 @@ const Rule_internal = union(Kind) { IsType: IsTypeRule, /// Checks whether a field or declaration is a function within the definition of the type. + /// If "strict" is not set, it will return true for pointers to functions, function arrays, etc. + /// If "strict" is set, it will only return true for functions. IsFunction: IsFunctionRule, /// Checks whether a field or declaration is variable within the definition of the type. @@ -96,15 +98,21 @@ const Rule_internal = union(Kind) { pub const IsFunctionRule = struct { lookup_data: Common.LookupData, + strict: bool, pub fn Check(comptime self: @This(), comptime target: type) !bool { const lookup = try Common.FindInType(target, self.lookup_data.lookup_name, self.lookup_data.lookup_mode); - switch (@typeInfo(lookup.GetType())) { + comptime var base_type = lookup.GetType(); + + // If not strict, we can check if it's "some form" of a function. + if (!self.strict) { + base_type = Common.GetBaseType(base_type); + } + + switch (@typeInfo(base_type)) { .Fn => return true, else => return false, } - // Below may do the same thing as above, I can't remember if it is the exact same, though. - // return std.meta.activeTag(@typeInfo(lookup.GetType())) == .Fn; } }; diff --git a/src/rule_tests.zig b/src/rule_tests.zig index 4a93951..3963ab2 100644 --- a/src/rule_tests.zig +++ b/src/rule_tests.zig @@ -18,11 +18,11 @@ const activeTag = std.meta.activeTag; const eql = std.mem.eql; // Check tests -// NOTE: Crudely implemented just to test out whether the idea works. test "Check" { const test_struct = struct { a: i32, + c: *const fn (void) void, pub const b: i64 = 10; }; @@ -31,11 +31,16 @@ test "Check" { .lookup_name = "a", .lookup_mode = .Any, }; + const lookup_b_decl = LookupData{ + .lookup_name = "b", + .lookup_mode = .Declaration, + }; + const lookup_c_fn_field = LookupData{ + .lookup_name = "c", + .lookup_mode = .Field, + }; // NOTE: All rules below assume a correct look-up. - // TODO: Check behavior when look-up fails and ensure - // it returns the expected look-up error. - // TODO: Tidy up everything, (maybe?) put it in a function. // IsInType const r_isintype = Rule{ .IsInType = .{ @@ -55,23 +60,50 @@ test "Check" { // IsFunction const r_isfunction = Rule{ .IsFunction = .{ .lookup_data = lookup_a_any, + .strict = false, } }; const o_isfunction = try r_isfunction.Check(test_struct); try expect(o_isfunction == false); - // ! IsVariable and IsConstant aren't yet implemented. - // TODO: Once implemented, set up these tests. - // IsVariable - // const r_isvariable = Rule{ .IsVariable = .{ - // .lookup_data = lookup_a_any, - // } }; - // const o_isvariable = try r_isvariable.Check(test_struct); - // try expect(o_isvariable == true); + const r_isfunction_field = Rule{ .IsFunction = .{ + .lookup_data = lookup_c_fn_field, + .strict = false, + } }; + const o_isfunction_field = try r_isfunction_field.Check(test_struct); + try expect(o_isfunction_field == true); + + const r_isfunction_field_strict = Rule{ .IsFunction = .{ + .lookup_data = lookup_c_fn_field, + .strict = true, + } }; + const o_isfunction_field_strict = try r_isfunction_field_strict.Check(test_struct); + try expect(o_isfunction_field_strict == false); + + // IsVariable For Fields + const r_isvariable_field = Rule{ .IsVariable = .{ + .lookup_data = lookup_a_any, + } }; + const o_isvariable_field = try r_isvariable_field.Check(test_struct); + try expect(o_isvariable_field == true); + + // IsConstant For Fields + const r_isconstant_field = Rule{ .IsConstant = .{ + .lookup_data = lookup_a_any, + } }; + const o_isconstant_field = try r_isconstant_field.Check(test_struct); + try expect(o_isconstant_field == false); - // IsConstant - // const r_isconstant = Rule{ .IsConstant = .{ - // .lookup_data = lookup_a_any, - // } }; - // const o_isconstant = try r_isconstant.Check(test_struct); - // try expect(o_isconstant == false); + // IsVariable For Declarations + const r_isvariable_decl = Rule{ .IsVariable = .{ + .lookup_data = lookup_b_decl, + } }; + const o_isvariable_decl = try r_isvariable_decl.Check(test_struct); + try expect(o_isvariable_decl == false); + + // IsConstant For Declarations + const r_isconstant_decl = Rule{ .IsConstant = .{ + .lookup_data = lookup_b_decl, + } }; + const o_isconstant_decl = try r_isconstant_decl.Check(test_struct); + try expect(o_isconstant_decl == true); } From d1f9bbe823a76430b16054ff94c0ecdec2cc12fc Mon Sep 17 00:00:00 2001 From: Nylvon Date: Tue, 2 Jan 2024 02:35:14 +0200 Subject: [PATCH 15/16] Revamp ruleset type. --- src/rule.zig | 278 ++++++++++++++++++++++++++------------------- src/rule_tests.zig | 109 ++++-------------- 2 files changed, 184 insertions(+), 203 deletions(-) diff --git a/src/rule.zig b/src/rule.zig index c1c6141..023f5cb 100644 --- a/src/rule.zig +++ b/src/rule.zig @@ -1,145 +1,187 @@ /// Metaplasia Rule /// ©2023 Cristian Vasilache (NNCV / Nylvon) /// -/// A rule is a constraint for a type. -pub const Rule = Rule_internal; +/// A rule is a function that validates a constraint on a type. +/// A ruleset is a set of rules that validate multiple constraints in any manner. +pub const Rule = @This(); const Common = @import("common.zig"); const std = @import("std"); -/// All base rules -pub const Kind = enum { - /// Look-up must be within the type definition. - IsInType, - - /// Look-up must be of a given type. - IsType, - - /// Look-up must be a function type. - IsFunction, - - /// Look-up must be a variable. - IsVariable, - - /// Look-up must be a constant. - IsConstant, - - /// Custom Rules? (?) - CustomDefinition, -}; - -/// An all-encompassing wrapper for rules for types. -/// All rules must validate the IRule interface. -/// TODO: When typechecking is implemented, make sure all -/// rules are checked against it (and pass). -const Rule_internal = union(Kind) { - /// Checks whether a field or declaration is within the definition of the type. - IsInType: IsInTypeRule, - - /// Checks whether a field or declaration is of a specified type within the definition of the type. - IsType: IsTypeRule, - - /// Checks whether a field or declaration is a function within the definition of the type. - /// If "strict" is not set, it will return true for pointers to functions, function arrays, etc. - /// If "strict" is set, it will only return true for functions. - IsFunction: IsFunctionRule, - - /// Checks whether a field or declaration is variable within the definition of the type. - IsVariable: IsVariableRule, - - /// Checks whether a field or declaration is constant within the definition of the type. - IsConstant: IsConstantRule, - - /// Checks according to the custom checking function and custom rule data. - CustomDefinition: CustomDefinitionRule, +/// Similar to std.meta.trait's TraitFn, but it can also return errors. +/// Useful for debugging why something doesn't validate a rule. +pub const RuleFn = fn (comptime type) anyerror!bool; + +/// A rule set is a decision tree that upon traversal determines whether +/// a type meets certain criteria, and if not, returns all the errors. +pub const RuleSet = struct { + rule: ?RuleFn, + children: ?[]const RuleSet, + link: Link, + outcome: ?anyerror!bool, + + /// Determines how this rule is linked with the previous rule. + pub const Link = enum { + Root, // Reserved for the root rule-set. + And, // Previous rule is valid if this one is also valid. + Or, // Previous rule may be valid if this one isn't. + // TODO: Add more links. + // NOTE: Is it necessary? + }; // - // Rule operations below + // Rule-set generation utilities // - /// Calls the rule descriptor's 'Check' function. - pub fn Check(comptime self: @This(), comptime target: type) !bool { - switch (self) { - // NOTE: "ir" stands for "inner rule". - .IsInType => |ir| return ir.Check(target), - .IsType => |ir| return ir.Check(target), - .IsFunction => |ir| return ir.Check(target), - .IsVariable => |ir| return ir.Check(target), - .IsConstant => |ir| return ir.Check(target), - .CustomDefinition => |ir| return ir.Check(target), - } + /// Returns a blank rule-set. + /// Used to return a root rule-set node. + pub fn Blank() RuleSet { + return RuleSet{ + .rule = null, + .children = null, + .link = .Root, + .outcome = null, + }; } - // - // Rule types below - // - - pub const IsInTypeRule = struct { - lookup_data: Common.LookupData, + /// Returns an insular rule-set from a rule function. + pub fn From(comptime rule: RuleFn, comptime link: RuleSet.Link) RuleSet { + return RuleSet{ + .rule = rule, + .children = null, + .link = link, + .outcome = null, + }; + } - pub fn Check(comptime self: @This(), comptime target: type) !bool { - _ = Common.FindInType(target, self.lookup_data.lookup_name, self.lookup_data.lookup_mode) catch |err| { - if (err == Common.LookupError.NotFound) { - return false; - } else return err; + /// Adds the specified rule-set to the current one as a child of it. + /// Returns a pointer to the current rule-set after modifying it. + pub fn Inject(comptime self: *const RuleSet, comptime ruleset: RuleSet) *const RuleSet { + if (self.children != null) { + const new_ruleset = RuleSet{ + // + .rule = self.rule, + .link = self.link, + .outcome = self.outcome, + .children = self.children.? ++ &[1]RuleSet{ruleset}, }; - return true; - } - }; - - pub const IsTypeRule = struct { - lookup_data: Common.LookupData, - lookup_type: type, - - pub fn Check(comptime self: @This(), comptime target: type) !bool { - const lookup = try Common.FindInType(target, self.lookup_data.lookup_name, self.lookup_data.lookup_mode); - return lookup.GetType() == self.lookup_type; + return &new_ruleset; + } else { + const new_ruleset = RuleSet{ + // + .rule = self.rule, + .link = self.link, + .outcome = self.outcome, + .children = &[1]RuleSet{ruleset}, + }; + return &new_ruleset; + // self.children = [1]RuleSet{ruleset}; + // var new_ruleset = RuleSet{ .rule = self.rule, .link = self.link, .outcome = self.outcome, .children = @constCast(&[1]RuleSet{ruleset}) }; + // return &new_ruleset; } - }; + } - pub const IsFunctionRule = struct { - lookup_data: Common.LookupData, - strict: bool, + /// Adds another rule with an "and" relationship to the current rule-set. + /// Returns a pointer to the current rule-set after modifying it. + pub fn And(comptime self: *const RuleSet, comptime rule: RuleFn) *const RuleSet { + const new_ruleset = From(rule, .And); + return self.Inject(new_ruleset); + } - pub fn Check(comptime self: @This(), comptime target: type) !bool { - const lookup = try Common.FindInType(target, self.lookup_data.lookup_name, self.lookup_data.lookup_mode); - comptime var base_type = lookup.GetType(); + /// Adds another rule with an "or" relationship to the current rule-set. + /// Returns a pointer to the current rule-set after modifying it. + pub fn Or(comptime self: *const RuleSet, comptime rule: RuleFn) *const RuleSet { + const new_ruleset = From(rule, .Or); + return self.Inject(new_ruleset); + } - // If not strict, we can check if it's "some form" of a function. - if (!self.strict) { - base_type = Common.GetBaseType(base_type); + /// Prints the ruleset as a string + pub fn Print(comptime self: *const RuleSet, comptime index: usize) []const u8 { + comptime var pad: []const u8 = ""; + comptime { + for (0..index) |i| { + _ = i; + pad = pad ++ "\t"; } - - switch (@typeInfo(base_type)) { - .Fn => return true, - else => return false, + switch (self.link) { + .And => pad = pad ++ "AND", + .Or => pad = pad ++ "OR", + .Root => pad = pad ++ "ROOT", + } + pad = pad ++ "\n"; + if (self.children) |children| { + for (children) |c| { + pad = pad ++ c.Print(index + 1); + } } } - }; + return pad; + } +}; - pub const IsVariableRule = struct { - lookup_data: Common.LookupData, +// +// Basic rules +// - pub fn Check(comptime self: @This(), comptime target: type) !bool { - const lookup = try Common.FindInType(target, self.lookup_data.lookup_name, self.lookup_data.lookup_mode); +/// Checks whether a looked-up member is inside a type. +/// eg: Whether a struct has a member "check" that is a declaration. +pub fn IsInType(comptime lookup_data: Common.LookupData) RuleFn { + return struct { + pub fn rule(comptime target: type) anyerror!bool { + return Common.FindInType(target, lookup_data.lookup_name, lookup_data.lookup_mode); + } + }.rule; +} + +/// Checks whether a looked-up member is of a certain type. +/// eg: Whether a struct has a member "check" that is of "fn(void) void" type. +pub fn IsType(comptime lookup_data: Common.LookupData, comptime wanted_type: type) RuleFn { + return struct { + pub fn rule(comptime target: type) anyerror!bool { + const lookup = try Common.FindInType(target, lookup_data.lookup_name, lookup_data.lookup_mode); + return lookup.GetType() == wanted_type; + } + }.rule; +} + +/// Checks whether a looked-up member is of a certain archetype. +/// eg: Whether a struct has a member "check" that is a function (.Fn archetype) +pub fn IsArchetype(comptime lookup_data: Common.LookupData, comptime archetype: std.meta.tags(std.builtin.Type)) RuleFn { + return struct { + pub fn rule(comptime target: type) anyerror!bool { + const lookup = try Common.FindInType(target, lookup_data.lookup_name, lookup_data.lookup_mode); + return std.meta.activeTag(@typeInfo(lookup.GetType())) == archetype; + } + }.rule; +} + +/// Checks whether a looked-up member is of a function archetype +/// This is a specialised case of "IsArchetype". +pub fn IsFunction(comptime lookup_data: Common.LookupData) RuleFn { + return struct { + pub fn rule(comptime target: type) anyerror!bool { + return IsArchetype(lookup_data, .Fn)(target); + } + }.rule; +} + +/// Checks whether a looked-up member is variable. +/// eg: Whether a struct has a declaration "check" whose value can be changed. +pub fn IsVariable(comptime lookup_data: Common.LookupData) RuleFn { + return struct { + pub fn rule(comptime target: type) anyerror!bool { + const lookup = try Common.FindInType(target, lookup_data.lookup_name, lookup_data.lookup_mode); return lookup.GetIsVariable(); } - }; - - pub const IsConstantRule = struct { - lookup_data: Common.LookupData, - - pub fn Check(comptime self: @This(), comptime target: type) !bool { - const lookup = try Common.FindInType(target, self.lookup_data.lookup_name, self.lookup_data.lookup_mode); + }.rule; +} + +/// Checks whether a looked-up member is constant. +/// eg: Whether a struct has a declaration "check" whose value can't be changed. +pub fn IsConstant(comptime lookup_data: Common.LookupData) RuleFn { + return struct { + pub fn rule(comptime target: type) anyerror!bool { + const lookup = try Common.FindInType(target, lookup_data.lookup_name, lookup_data.lookup_mode); return lookup.GetIsConstant(); } - }; - - pub const CustomDefinitionRule = struct { - check_fn: *const fn (comptime type, comptime *anyopaque) anyerror!bool, - rule_data: *anyopaque, - - pub fn Check(comptime self: @This(), comptime target: type) !bool { - return self.check_fn(target, self.rule_data); - } - }; -}; + }.rule; +} diff --git a/src/rule_tests.zig b/src/rule_tests.zig index 3963ab2..cb88321 100644 --- a/src/rule_tests.zig +++ b/src/rule_tests.zig @@ -8,7 +8,19 @@ /// "[FeatureName] [Scenario]" pub const RuleTests = @This(); const LookupData = @import("common.zig").LookupData; -const Rule = @import("rule.zig").Rule; +const Rule = @import("rule.zig"); +const RuleSet = Rule.RuleSet; +const Blank = RuleSet.Blank; +const From = RuleSet.From; +const Inject = RuleSet.Inject; +const And = RuleSet.And; +const Or = RuleSet.Or; +const IsInType = Rule.IsInType; +const IsType = Rule.IsType; +const IsArchetype = Rule.IsArchetype; +const IsFunction = Rule.IsFunction; +const IsVariable = Rule.IsVariable; +const IsConstant = Rule.IsConstant; const std = @import("std"); const testing = std.testing; const expect = testing.expect; @@ -17,93 +29,20 @@ const expectError = testing.expectError; const activeTag = std.meta.activeTag; const eql = std.mem.eql; -// Check tests +test "And" { + const lookup_a = LookupData{ .lookup_mode = .Field, .lookup_name = "a" }; + const lookup_b = LookupData{ .lookup_mode = .Field, .lookup_name = "b" }; -test "Check" { - const test_struct = struct { + const test_type = struct { a: i32, - c: *const fn (void) void, - pub const b: i64 = 10; + b: i32, }; + _ = test_type; - // Example valid look-up data. - const lookup_a_any = LookupData{ - .lookup_name = "a", - .lookup_mode = .Any, - }; - const lookup_b_decl = LookupData{ - .lookup_name = "b", - .lookup_mode = .Declaration, - }; - const lookup_c_fn_field = LookupData{ - .lookup_name = "c", - .lookup_mode = .Field, - }; - - // NOTE: All rules below assume a correct look-up. - - // IsInType - const r_isintype = Rule{ .IsInType = .{ - .lookup_data = lookup_a_any, - } }; - const o_isintype = try r_isintype.Check(test_struct); - try expect(o_isintype == true); - - // IsOfType - const r_isoftype = Rule{ .IsType = .{ - .lookup_data = lookup_a_any, - .lookup_type = i32, - } }; - const o_isoftype = try r_isoftype.Check(test_struct); - try expect(o_isoftype == true); - - // IsFunction - const r_isfunction = Rule{ .IsFunction = .{ - .lookup_data = lookup_a_any, - .strict = false, - } }; - const o_isfunction = try r_isfunction.Check(test_struct); - try expect(o_isfunction == false); - - const r_isfunction_field = Rule{ .IsFunction = .{ - .lookup_data = lookup_c_fn_field, - .strict = false, - } }; - const o_isfunction_field = try r_isfunction_field.Check(test_struct); - try expect(o_isfunction_field == true); - - const r_isfunction_field_strict = Rule{ .IsFunction = .{ - .lookup_data = lookup_c_fn_field, - .strict = true, - } }; - const o_isfunction_field_strict = try r_isfunction_field_strict.Check(test_struct); - try expect(o_isfunction_field_strict == false); - - // IsVariable For Fields - const r_isvariable_field = Rule{ .IsVariable = .{ - .lookup_data = lookup_a_any, - } }; - const o_isvariable_field = try r_isvariable_field.Check(test_struct); - try expect(o_isvariable_field == true); - - // IsConstant For Fields - const r_isconstant_field = Rule{ .IsConstant = .{ - .lookup_data = lookup_a_any, - } }; - const o_isconstant_field = try r_isconstant_field.Check(test_struct); - try expect(o_isconstant_field == false); - - // IsVariable For Declarations - const r_isvariable_decl = Rule{ .IsVariable = .{ - .lookup_data = lookup_b_decl, - } }; - const o_isvariable_decl = try r_isvariable_decl.Check(test_struct); - try expect(o_isvariable_decl == false); + const HasAB = Blank().And(IsInType(lookup_a)) + .And(IsInType(lookup_b)); - // IsConstant For Declarations - const r_isconstant_decl = Rule{ .IsConstant = .{ - .lookup_data = lookup_b_decl, - } }; - const o_isconstant_decl = try r_isconstant_decl.Check(test_struct); - try expect(o_isconstant_decl == true); + const msg = HasAB.Print(0); + std.log.err("\n{s}", .{msg}); + try expectEqual(@TypeOf(HasAB), *const RuleSet); } From 1b2d509ae5bbc048136bb3c54d3bcff964c30427 Mon Sep 17 00:00:00 2001 From: Nylvon Date: Tue, 2 Jan 2024 20:15:14 +0200 Subject: [PATCH 16/16] Add rule-checking and some wrappers. Simple interfaces now possible. --- src/common.zig | 28 ++++++++++++++++++++++++++++ src/rule.zig | 43 ++++++++++++++++++++++++++++++++++++++++++- src/rule_tests.zig | 43 ++++++++++++++++++++++++++++++++----------- 3 files changed, 102 insertions(+), 12 deletions(-) diff --git a/src/common.zig b/src/common.zig index 0610162..c07df68 100644 --- a/src/common.zig +++ b/src/common.zig @@ -16,6 +16,34 @@ pub const LookupData = struct { /// If Any is selected, fields are prioritised over declarations in the look-up order. pub const LookupMode = enum { Field, Declaration, Any }; +// +// Convenience wrappers +// + +/// Generates lookup info for a field given a name. +pub fn FindField(name: []const u8) LookupData { + return LookupData{ + .lookup_name = name, + .lookup_mode = .Field, + }; +} + +/// Generates lookup info for a declaration given a name. +pub fn FindDeclaration(name: []const u8) LookupData { + return LookupData{ + .lookup_name = name, + .lookup_mode = .Declaration, + }; +} + +/// Generates lookup info for a member given a name. +pub fn FindAny(name: []const u8) LookupData { + return LookupData{ + .lookup_name = name, + .lookup_mode = .Any, + }; +} + /// Used by FindInType to detail why the function call failed. pub const LookupError = error{ InvalidType, diff --git a/src/rule.zig b/src/rule.zig index 023f5cb..de97c50 100644 --- a/src/rule.zig +++ b/src/rule.zig @@ -28,6 +28,42 @@ pub const RuleSet = struct { // NOTE: Is it necessary? }; + pub fn CheckChildren(comptime self: *const RuleSet, comptime target: type) anyerror!bool { + if (self.children) |children| { + var ok = true; + inline for (children) |*child| { + const child_outcome = try child.Check(target); + switch (child.link) { + .And => { + ok = ok and child_outcome; + }, + .Or => { + ok = ok or child_outcome; + }, + .Root => { + return error.ChildIsRoot; + }, + } + } + return ok; + } else return error.NoChildren; + } + + pub fn Check(comptime self: *const RuleSet, comptime target: type) anyerror!bool { + // If there is no rule, it passes the check, otherwise evaluate. + const ok_rule = if (self.rule) |r| try r(target) else true; + const ok_children = self.CheckChildren(target) catch |err| { + if (err == error.NoChildren) { + // If there's no children, just return the rule's result. + return ok_rule; + } else return err; + }; + // If we're here, ok_children is a bool. + // A root node is an "and" node with children. + // self.outcome = ok_children and ok_rule; + return ok_children and ok_rule; + } + // // Rule-set generation utilities // @@ -127,7 +163,12 @@ pub const RuleSet = struct { pub fn IsInType(comptime lookup_data: Common.LookupData) RuleFn { return struct { pub fn rule(comptime target: type) anyerror!bool { - return Common.FindInType(target, lookup_data.lookup_name, lookup_data.lookup_mode); + _ = Common.FindInType(target, lookup_data.lookup_name, lookup_data.lookup_mode) catch |err| { + if (err == Common.LookupError.NotFound) { + return false; + } else return err; + }; + return true; } }.rule; } diff --git a/src/rule_tests.zig b/src/rule_tests.zig index cb88321..e98ce6c 100644 --- a/src/rule_tests.zig +++ b/src/rule_tests.zig @@ -7,7 +7,10 @@ /// Each test name is formatted as follows: /// "[FeatureName] [Scenario]" pub const RuleTests = @This(); -const LookupData = @import("common.zig").LookupData; +const Common = @import("common.zig"); +const FindField = Common.FindField; +const FindDeclaration = Common.FindDeclaration; +const FindAny = Common.FindAny; const Rule = @import("rule.zig"); const RuleSet = Rule.RuleSet; const Blank = RuleSet.Blank; @@ -29,20 +32,38 @@ const expectError = testing.expectError; const activeTag = std.meta.activeTag; const eql = std.mem.eql; -test "And" { - const lookup_a = LookupData{ .lookup_mode = .Field, .lookup_name = "a" }; - const lookup_b = LookupData{ .lookup_mode = .Field, .lookup_name = "b" }; - +test "Interfaces at last" { const test_type = struct { a: i32, b: i32, }; - _ = test_type; - const HasAB = Blank().And(IsInType(lookup_a)) - .And(IsInType(lookup_b)); + // A simple interface definition + const HasBoth = + Blank() + .And(IsInType(FindField("a"))) + .And(IsInType(FindField("b"))); + + const HasEither = + Blank() + .Or(IsInType(FindField("a"))) + .Or(IsInType(FindField("b"))); + + const checkBoth = try HasBoth.Check(test_type); + const checkEither = try HasEither.Check(test_type); + try expect(checkBoth); + try expect(checkEither); + + const test_type_2 = struct { + a: i32, + }; + + const checkBoth2 = try HasBoth.Check(test_type_2); + const checkEither2 = try HasEither.Check(test_type_2); + try expect(!checkBoth2); + try expect(checkEither2); + + const test_type_3 = struct {}; - const msg = HasAB.Print(0); - std.log.err("\n{s}", .{msg}); - try expectEqual(@TypeOf(HasAB), *const RuleSet); + try expectError(Common.LookupError.TypeHasNoFields, HasBoth.Check(test_type_3)); }