akern-gkgoat-fork/sysdata/libraries/stn/src/fmt.hb

201 lines
4.7 KiB
Plaintext
Raw Normal View History

2024-12-19 11:19:57 -06:00
.{Kind, usize, string, signed_int, float, integer, memory, panic, log: .{LogLevel}} := @use("stn")
2024-12-17 16:30:22 -06:00
fmt_int := fn(v: @Any(), str: ^u8, radix: @TypeOf(v)): uint {
is_negative := signed_int(@TypeOf(v)) & v < 0
if is_negative v = -v
prefix_len := 0
if radix == 16 {
*str = '0';
*(str + 1) = 'x'
prefix_len = 2
} else if radix == 2 {
*str = '0';
*(str + 1) = 'b'
prefix_len = 2
} else if radix == 8 {
*str = '0';
*(str + 1) = 'o'
prefix_len = 2
}
2024-12-17 16:30:22 -06:00
if v == 0 {
*(str + prefix_len) = '0'
return prefix_len + 1
}
2024-12-17 16:30:22 -06:00
i := 0
loop if v <= 0 break else {
remainder := v % radix
v /= radix
if remainder > 9 {
*(str + prefix_len + i) = @intcast(remainder - 10 + 'A')
} else {
*(str + prefix_len + i) = @intcast(remainder + '0')
}
2024-12-17 16:30:22 -06:00
i += 1
}
2024-12-17 16:30:22 -06:00
if is_negative {
*(str + prefix_len + i) = '-'
i += 1
}
2024-12-17 16:30:22 -06:00
@inline(string.reverse, str + prefix_len)
return prefix_len + i
}
2024-12-17 16:30:22 -06:00
fmt_bool := fn(v: bool, str: ^u8): uint {
if v {
2024-12-17 07:50:04 -06:00
*@as(^[u8; 4], @bitcast(str)) = *@bitcast("true\0")
return 4
} else {
2024-12-17 07:50:04 -06:00
*@as(^[u8; 5], @bitcast(str)) = *@bitcast("false\0")
return 5
}
}
2024-12-17 16:30:22 -06:00
fmt_float := fn(v: @Any(), str: ^u8, precision: uint, radix: int): uint {
is_negative := v < 0
i := 0
prefix_len := 0
if radix == 16 {
*str = '0';
*(str + 1) = 'x'
prefix_len = 2
} else if radix == 2 {
*str = '0';
*(str + 1) = 'b'
prefix_len = 2
} else if radix == 8 {
*str = '0';
*(str + 1) = 'o'
prefix_len = 2
}
2024-12-17 07:50:04 -06:00
2024-12-17 16:30:22 -06:00
if is_negative v = -v
2024-12-17 16:30:22 -06:00
integer_part := @fti(v)
fractional_part := v - @itf(integer_part)
2024-12-17 16:30:22 -06:00
loop if integer_part == 0 & i > 0 break else {
remainder := integer_part % radix
integer_part /= radix
if remainder > 9 {
*(str + prefix_len + i) = @intcast(remainder - 10 + 'A')
} else {
*(str + prefix_len + i) = @intcast(remainder + '0')
}
2024-12-17 16:30:22 -06:00
i += 1
}
2024-12-17 16:30:22 -06:00
if is_negative {
*(str + prefix_len + i) = '-'
i += 1
}
2024-12-17 16:30:22 -06:00
@inline(string.reverse, str + prefix_len);
*(str + prefix_len + i) = '.'
i += 1
p := precision
tolerance := @as(@TypeOf(v), 0.00000001)
loop if p <= 0 | fractional_part < tolerance break else {
fractional_part *= @itf(radix)
digit := @fti(fractional_part)
if digit > 9 {
*(str + prefix_len + i) = @intcast(digit - 10 + 'A')
} else {
*(str + prefix_len + i) = @intcast(digit + '0')
}
i += 1
2024-12-17 16:30:22 -06:00
fractional_part -= @itf(digit)
p -= 1
}
2024-12-17 16:30:22 -06:00
return prefix_len + i
}
fmt_container := fn(v: @Any(), str: ^u8, opts: FormatOptions): uint {
T2 := @TypeOf(v)
i := 0;
*@as(^[u8; @len(@nameof(T2))], @bitcast(str)) = *@bitcast(@nameof(T2))
len := @len(@nameof(T2));
*@as(^[u8; 2], @bitcast(str + len)) = *@bitcast(".(\0")
len += 2
$loop {
v_sub := v[i]
2024-12-19 11:19:57 -06:00
len += @inline(format, v_sub, str + len, opts)
2024-12-17 16:30:22 -06:00
i += 1
if i == @len(T2) break else {
*@as(^[u8; 2], @bitcast(str + len)) = *@bitcast(", \0")
len += 2
}
2024-12-17 16:30:22 -06:00
};
*@as(^[u8; 1], @bitcast(str + len)) = *@bitcast(")\0")
len += 1
return len
}
2024-12-17 16:30:22 -06:00
fmt_nullable := fn(v: @Any(), str: ^u8, opts: FormatOptions): uint {
if v == null {
*@as(^[u8; 4], @bitcast(str)) = *@bitcast("null\0")
return 4
} else {
2024-12-19 11:19:57 -06:00
return @inline(format, @as(@ChildOf(@TypeOf(v)), v), str, opts)
}
}
2024-12-19 11:19:57 -06:00
fmt_enum := fn(v: @Any(), str: ^u8, opts: FormatOptions): uint {
T := @TypeOf(v)
2024-12-19 11:29:39 -06:00
len := @len(@nameof(T));
*@as(^[u8; @len(@nameof(T))], @bitcast(str)) = *@bitcast(@nameof(T));
*@as(^[u8; 2], @bitcast(str + len)) = *@bitcast(".(\0")
len += 2
len += @inline(fmt_int, @as(usize(T), @bitcast(v)), str + len, 10);
2024-12-19 13:07:54 -06:00
*@as(^[u8; 2], @bitcast(str + len)) = *@bitcast(")\0")
2024-12-19 11:29:39 -06:00
return len + 2
2024-12-19 11:19:57 -06:00
}
/* SAFETY:
* Assumes the buffer is wide enough for the formatted text and a null char
* Does not clear the buffer for you
*/
format := fn(v: @Any(), str: ^u8, opts: FormatOptions): uint {
2024-12-17 16:30:22 -06:00
T := @TypeOf(v)
match Kind.of(T) {
.Pointer => return @inline(fmt_int, @as(uint, @bitcast(v)), str, 16),
2024-12-17 11:43:00 -06:00
.Builtin => {
if integer(T) {
2024-12-17 16:30:22 -06:00
return @inline(fmt_int, v, str, @intcast(opts.radix))
2024-12-17 11:43:00 -06:00
} else if T == bool {
2024-12-17 16:30:22 -06:00
return @inline(fmt_bool, v, str)
2024-12-17 11:43:00 -06:00
} else if float(T) {
2024-12-17 16:30:22 -06:00
return @inline(fmt_float, v, str, opts.decimal_digits, @intcast(opts.radix))
}
2024-12-17 11:43:00 -06:00
},
2024-12-17 16:30:22 -06:00
.Opt => return @inline(fmt_nullable, v, str, opts),
2024-12-19 11:19:57 -06:00
.Enum => return @inline(fmt_enum, v, str, opts),
2024-12-17 16:30:22 -06:00
.Struct => return @inline(fmt_container, v, str, opts),
.Slice => return @inline(fmt_container, v, str, opts),
_ => @error("Type: \"\0", T, "\" is not supported.\0"),
}
}
/* TODO:
* Custom formatters using struct methods (T._fmt(self, str): uint),
* Format struct fields "Name.{a: x, b: y, c: z}"
* Optionally tabulate
* Add more FormatOption fields
* Support scientific notation for floating point
2024-12-17 16:30:22 -06:00
* Support format string
*/
FormatOptions := struct {
2024-12-17 16:30:22 -06:00
decimal_digits: uint = 2,
2024-12-17 12:20:16 -06:00
radix: uint = 10,
2024-12-19 11:19:57 -06:00
// temporarily here, will change later maybe
log: LogLevel = .Info,
}