2024-12-16 20:06:48 -06:00
|
|
|
.{string, primitive, unsigned_int, signed_int, float, integer, memory, panic} := @use("stn")
|
|
|
|
|
|
|
|
format_int := fn($T: type, v: T, str: ^u8): uint {
|
|
|
|
if integer(T) {
|
|
|
|
is_negative := signed_int(T) & v < 0
|
|
|
|
i := 0
|
|
|
|
if is_negative v = -v
|
|
|
|
|
|
|
|
if v == 0 {
|
|
|
|
*str = '0'
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
loop if v == 0 break else {
|
|
|
|
remainder := v % 10
|
|
|
|
v /= 10;
|
|
|
|
*(str + i) = @intcast(remainder + '0')
|
|
|
|
i += 1
|
|
|
|
}
|
|
|
|
|
|
|
|
if is_negative {
|
|
|
|
*(str + i) = '-'
|
|
|
|
i += 1
|
|
|
|
}
|
|
|
|
|
|
|
|
@inline(string.reverse, str)
|
|
|
|
return i
|
|
|
|
} else {
|
|
|
|
panic("Type is not an integer\0")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
format_bool := fn(v: bool, str: ^u8): uint {
|
|
|
|
if v {
|
|
|
|
memory.copy(u8, "true\0", str, 4)
|
|
|
|
return 4
|
|
|
|
} else {
|
|
|
|
memory.copy(u8, "false\0", str, 5)
|
|
|
|
return 5
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
format_float := fn($T: type, v: T, str: ^u8, precision: uint): uint {
|
|
|
|
if float(T) {
|
|
|
|
is_negative := v < 0
|
|
|
|
i := 0
|
|
|
|
|
|
|
|
if is_negative v = -v
|
|
|
|
|
|
|
|
integer_part := @fti(v)
|
|
|
|
fractional_part := v - @itf(integer_part)
|
|
|
|
|
|
|
|
loop if integer_part == 0 & i > 0 break else {
|
|
|
|
remainder := integer_part % 10
|
|
|
|
integer_part /= 10;
|
|
|
|
*(str + i) = @intcast(remainder + '0')
|
|
|
|
i += 1
|
|
|
|
}
|
|
|
|
|
|
|
|
if is_negative {
|
|
|
|
*(str + i) = '-'
|
|
|
|
i += 1
|
|
|
|
}
|
|
|
|
|
|
|
|
@inline(string.reverse, str);
|
|
|
|
*(str + i) = '.'
|
|
|
|
i += 1
|
|
|
|
|
|
|
|
tolerance := @as(T, 0.000000001)
|
|
|
|
p := precision
|
|
|
|
loop if p <= 0 | fractional_part < tolerance break else {
|
|
|
|
fractional_part *= 10.0
|
|
|
|
digit := @fti(fractional_part);
|
|
|
|
*(str + i) = @intcast(digit + '0')
|
|
|
|
i += 1
|
|
|
|
fractional_part -= @itf(digit)
|
|
|
|
p -= 1
|
|
|
|
}
|
|
|
|
|
|
|
|
return i
|
|
|
|
} else {
|
|
|
|
panic("Type is not a floating point\0")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
format_inner := fn($T: type, v: T, str: ^u8, opts: FormatOptions): uint {
|
|
|
|
if integer(T) {
|
|
|
|
return @inline(format_int, T, v, str)
|
|
|
|
} else if T == bool {
|
|
|
|
return @inline(format_bool, v, str)
|
|
|
|
} else if float(T) {
|
|
|
|
return @inline(format_float, T, v, str, opts.decimal_digits)
|
2024-12-16 20:10:30 -06:00
|
|
|
} else if !primitive(T) {
|
2024-12-16 20:06:48 -06:00
|
|
|
i := 0
|
|
|
|
// name := @nameof(T)
|
|
|
|
// len := string.length(name)
|
|
|
|
// memory.copy(u8, name, str, len)
|
|
|
|
len := 0
|
|
|
|
memory.copy(u8, ".(\0", str + len, 2)
|
|
|
|
len += 2
|
|
|
|
$loop if i == @len(T) break else {
|
|
|
|
v_sub := v[i]
|
|
|
|
TSub := @TypeOf(v_sub)
|
|
|
|
len += @inline(format_inner, TSub, v_sub, str + len, opts)
|
|
|
|
if i != @len(T) - 1 {
|
|
|
|
memory.copy(u8, ", \0", str + len, 2)
|
|
|
|
len += 2
|
|
|
|
} else {
|
|
|
|
memory.copy(u8, ")\0", str + len, 1)
|
|
|
|
len += 1
|
|
|
|
}
|
|
|
|
i += 1
|
|
|
|
}
|
|
|
|
return len
|
2024-12-16 20:10:30 -06:00
|
|
|
} else {
|
|
|
|
panic("Unsupported formatter type\0")
|
2024-12-16 20:06:48 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO:
|
|
|
|
* Custom formatters using struct methods (T._fmt(self, str): uint),
|
|
|
|
* Format struct names "Name.(x, y, z)"
|
|
|
|
* Format struct fields "Name.{a: x, b: y, c: z}"
|
|
|
|
* Optimise (so many instructions...)
|
|
|
|
* -> Consider switching `memory.copy` to loop for comptime stuff
|
|
|
|
* Optionally tabulate
|
|
|
|
* Add more FormatOption fields
|
|
|
|
* Support radices for integers
|
|
|
|
* Support scientific notation for floating point
|
|
|
|
* Support format string (impossible right now)
|
|
|
|
* Support nullables (impossible right now)
|
|
|
|
* Support pointers
|
|
|
|
*/
|
|
|
|
|
|
|
|
FormatOptions := struct {
|
|
|
|
decimal_digits: uint,
|
|
|
|
}
|
|
|
|
|
|
|
|
$DEFAULT_OPTS := FormatOptions.(2)
|
|
|
|
|
|
|
|
format := fn($T: type, v: T, str: ^u8): ^u8 return @inline(format_args, T, v, str, DEFAULT_OPTS)
|
|
|
|
|
|
|
|
format_args := fn($T: type, v: T, str: ^u8, opts: FormatOptions): ^u8 {
|
|
|
|
@inline(string.clear, str)
|
|
|
|
_ = @inline(format_inner, T, v, str, opts)
|
|
|
|
return str
|
|
|
|
}
|