#module_parameters(RUN_TESTS := false); // These return the bool first so you can check in a conditional, // rather than having to do '_, ok := ...' check_type_tag :: ($$T: Type, tag: Type_Info_Tag) -> bool, *Type_Info { #if is_constant(T) { info :: type_info(T); if info.type == tag return true, info; } else { info := T.(*Type_Info); if info.type == tag return true, info; } return false, null; } type_is_integer :: ($$T: Type) -> bool, *Type_Info_Integer { ok, info := check_type_tag(T, .INTEGER); return ok, info.(*Type_Info_Integer); } type_is_float :: ($$T: Type) -> bool, *Type_Info_Float { ok, info := check_type_tag(T, .FLOAT); return ok, info.(*Type_Info_Float); } type_is_scalar :: (t: Type) -> bool { return type_is_integer(t) || type_is_float(t); } type_is_array :: ($$T: Type) -> bool, *Type_Info_Array { ok, info := check_type_tag(T, .ARRAY); return ok, info.(*Type_Info_Array); } type_is_struct :: ($$T: Type) -> bool, *Type_Info_Struct { ok, info := check_type_tag(T, .STRUCT); return ok, info.(*Type_Info_Struct); } type_is_enum :: ($$T: Type) -> bool, *Type_Info_Enum { ok, info := check_type_tag(T, .ENUM); return ok, info.(*Type_Info_Enum); } // Returns the lowest and highest values T can represent. // Note: T must be an integer, float, or enum type. range_for :: ($T: Type, loc := #caller_location) -> (T, T) #expand { // @note(judah): we need to runs here because jai is weird. return #run lo_for(T, loc = loc), #run hi_for(T, loc = loc); } // Returns the lowest value T can represent. // Note: T must be an integer, float, or enum type. lo_for :: ($T: Type, loc := #caller_location) -> T #expand { return #run -> T { info := T.(*Type_Info); if info.type == { case .INTEGER; i := info.(*Type_Info_Integer); if i.runtime_size == { case 1; return (ifx i.signed then -0x80 else 0).(T, no_check); case 2; return (ifx i.signed then -0x8000 else 0).(T, no_check); case 4; return (ifx i.signed then -0x8000_0000 else 0).(T, no_check); case 8; return (ifx i.signed then -0x8000_0000_0000_0000 else 0).(T, no_check); case; compiler.compiler_report("unhandled integer type", loc = loc); } case .FLOAT; if info.runtime_size == { case 4; return (0h00800000).(T, no_check); case 8; return (0h00100000_00000000).(T, no_check); case; compiler.compiler_report("unhandled float type", loc = loc); } case .ENUM; i := info.(*Type_Info_Enum); if i.values.count == 0 { return 0; } min: T = i.values[0].(T, no_check); if i.internal_type.signed { for i.values if it.(T) < min { min = it.(T); } } else { for i.values if it.(T) < min { min = it.(T); } } return min; case; compiler.compiler_report("min requires an enum, integer, or float type", loc = loc); } return 0; }; } // Returns the highest value T can represent. // Note: T must be an integer, float, or enum type. hi_for :: ($T: Type, loc := #caller_location) -> T #expand { return #run -> T { info := T.(*Type_Info); if info.type == { case .INTEGER; i := info.(*Type_Info_Integer); if i.runtime_size == { case 1; return (ifx i.signed then 0x7f else 0xff).(T, no_check); case 2; return (ifx i.signed then 0x7fff else 0xffff).(T, no_check); case 4; return (ifx i.signed then 0x7fff_ffff else 0xffff_ffff).(T, no_check); case 8; return (ifx i.signed then 0x7fff_ffff_ffff_ffff else 0xffff_ffff_ffff_ffff).(T, no_check); case; compiler.compiler_report("unhandled integer type", loc = loc); } case .FLOAT; if info.runtime_size == { case 4; return (0h7F7FFFFF).(T, no_check); case 8; return (0h7FEFFFFF_FFFFFFFF).(T, no_check); case; compiler.compiler_report("unhandled float type", loc = loc); } case .ENUM; i := info.(*Type_Info_Enum); if i.values.count == 0 { return 0; } max := i.values[0].(T, no_check); if i.internal_type.signed { for i.values if xx it > max { max = xx it; } } else { for i.values if xx it > max { max = xx it; } } return max; case; compiler.compiler_report("max requires an enum, integer, or float type", loc = loc); } return 0; }; } #scope_file; compiler :: #import "Compiler"; // @future // ---------------------------------------------------------- // TESTS // ---------------------------------------------------------- #if RUN_TESTS #run { test :: #import "jc/meta/test"; test.run("lo_for:primitives", t => { test.expect(t, lo_for(u8) == 0); test.expect(t, lo_for(s8) == -128); test.expect(t, lo_for(u16) == 0); test.expect(t, lo_for(s16) == -32768); test.expect(t, lo_for(u32) == 0); test.expect(t, lo_for(s32) == -2147483648); test.expect(t, lo_for(u64) == 0); test.expect(t, lo_for(s64) == -9223372036854775808); test.expect(t, lo_for(float32) == 0h00800000); test.expect(t, lo_for(float64) == 0h00100000_00000000); }); test.run("hi_for:primitives", t => { test.expect(t, hi_for(u8) == 255); test.expect(t, hi_for(s8) == 127); test.expect(t, hi_for(u16) == 65535); test.expect(t, hi_for(s16) == 32767); test.expect(t, hi_for(u32) == 4294967295); test.expect(t, hi_for(s32) == 2147483647); test.expect(t, hi_for(u64) == 18446744073709551615); test.expect(t, hi_for(s64) == 9223372036854775807); test.expect(t, hi_for(float32) == 340282346638528859000000000000000000000.0); test.expect(t, hi_for(float64) == 179769313486231570900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0); }); test.run("lo_for/hi_for:enums", t => { U8_Enum :: enum u8 { lo :: -1; hi :: +1; } S8_Enum :: enum s8 { lo :: -2; hi :: -1; } { test.expect(t, lo_for(U8_Enum) == U8_Enum.lo); test.expect(t, lo_for(S8_Enum) == S8_Enum.lo); test.expect(t, hi_for(U8_Enum) == U8_Enum.hi); test.expect(t, hi_for(S8_Enum) == S8_Enum.hi); } U16_Enum :: enum u16 { lo :: -1; hi :: +1; } S16_Enum :: enum s16 { lo :: -2; hi :: -1; } { test.expect(t, lo_for(U16_Enum) == U16_Enum.lo); test.expect(t, lo_for(S16_Enum) == S16_Enum.lo); test.expect(t, hi_for(U16_Enum) == U16_Enum.hi); test.expect(t, hi_for(S16_Enum) == S16_Enum.hi); } U32_Enum :: enum u32 { lo :: -1; hi :: +1; } S32_Enum :: enum s32 { lo :: -2; hi :: -1; } { test.expect(t, lo_for(U32_Enum) == U32_Enum.lo); test.expect(t, lo_for(S32_Enum) == S32_Enum.lo); test.expect(t, hi_for(U32_Enum) == U32_Enum.hi); test.expect(t, hi_for(S32_Enum) == S32_Enum.hi); } U64_Enum :: enum u64 { lo :: -1; hi :: +1; } S64_Enum :: enum s64 { lo :: -2; hi :: -1; } { test.expect(t, lo_for(U64_Enum) == U64_Enum.lo); test.expect(t, lo_for(S64_Enum) == S64_Enum.lo); test.expect(t, hi_for(U64_Enum) == U64_Enum.hi); test.expect(t, hi_for(S64_Enum) == S64_Enum.hi); } // @note(judah): just making sure this compiles lo, hi := range_for(U64_Enum); test.expect(t, lo == U64_Enum.lo); test.expect(t, hi == U64_Enum.hi); }); }