write the rest of the formatter, but compiler wants to kill me

This commit is contained in:
koniifer 2025-01-06 00:10:12 +00:00
parent 73184afea4
commit 15f1351049
3 changed files with 212 additions and 22 deletions

View file

@ -29,8 +29,8 @@ currently available targets:
### Currently "in progress" features
- `lily.rand.SimpleRandom`
- `lily.log.{print, printf}`
### Currently "broken due to compiler" features
- `lily.log.{print, printf}`
- `lily.collections.SparseVec`
- `lily.alloc.{SimpleAllocator, RawAllocator}`

View file

@ -1,28 +1,30 @@
.{target, Type, TypeOf, string, memcpy} := @use("lib.hb")
.{target, Type, TypeOf, string, memcpy, panic} := @use("lib.hb")
$FP_TOLERANCE := 0.00000001
// ! (c_native) (compiler) bug: caused by: `lily.log.print(100)`
fmt_int := fn(v: @Any(), str: []u8, radix: @TypeOf(v)): uint {
fmt_int := fn(buf: []u8, v: @Any(), radix: @TypeOf(v)): uint {
is_negative := TypeOf(v).is_signed_int() & v < 0
prefix_len := 0
if is_negative {
v = -v
str[0] = '-'
buf[0] = '-'
prefix_len += 1
}
if radix == 16 {
*@as(^[2]u8, @bitcast(str.ptr + prefix_len)) = *@bitcast("0x".ptr)
memcpy(buf.ptr + prefix_len, "0x".ptr, 2)
prefix_len += 2
} else if radix == 2 {
*@as(^[2]u8, @bitcast(str.ptr + prefix_len)) = *@bitcast("0b".ptr)
memcpy(buf.ptr + prefix_len, "0b".ptr, 2)
prefix_len += 2
} else if radix == 8 {
*@as(^[2]u8, @bitcast(str.ptr + prefix_len)) = *@bitcast("0o".ptr)
memcpy(buf.ptr + prefix_len, "0o".ptr, 2)
prefix_len += 2
}
if v == 0 {
str[prefix_len] = '0'
buf[prefix_len] = '0'
return prefix_len + 1
}
@ -31,41 +33,224 @@ fmt_int := fn(v: @Any(), str: []u8, radix: @TypeOf(v)): uint {
remainder := v % radix
v /= radix
if remainder > 9 {
str[i] = @intcast(remainder - 10 + 'A')
buf[i] = @intcast(remainder - 10 + 'A')
} else {
str[i] = @intcast(remainder + '0')
buf[i] = @intcast(remainder + '0')
}
i += 1
}
string.reverse(str[prefix_len..i])
string.reverse(buf[prefix_len..i])
return i
}
fmt_bool := fn(v: bool, str: []u8): uint {
fmt_float := fn(buf: []u8, v: @Any(), precision: uint, radix: int): uint {
is_negative := v < 0
prefix_len := 0
if is_negative {
v = -v
buf[0] = '-'
prefix_len += 1
}
if radix == 16 {
memcpy(buf.ptr + prefix_len, "0x".ptr, 2)
prefix_len += 2
} else if radix == 2 {
memcpy(buf.ptr + prefix_len, "0b".ptr, 2)
prefix_len += 2
} else if radix == 8 {
memcpy(buf.ptr + prefix_len, "0o".ptr, 2)
prefix_len += 2
}
integer_part := @fti(v)
fractional_part := v - @itf(integer_part)
i := prefix_len
loop if integer_part == 0 & i > prefix_len break else {
remainder := integer_part % radix
integer_part /= radix
if remainder > 9 {
buf[i] = @intcast(remainder - 10 + 'A')
} else {
buf[i] = @intcast(remainder + '0')
}
i += 1
}
string.reverse(buf[prefix_len..i])
if fractional_part > FP_TOLERANCE {
buf[i] = '.'
i += 1
p := precision
loop if p <= 0 | fractional_part < FP_TOLERANCE break else {
fractional_part *= @itf(radix)
digit := @fti(fractional_part)
if digit > 9 {
buf[i] = @intcast(digit - 10 + 'A')
} else {
buf[i] = @intcast(digit + '0')
}
i += 1
fractional_part -= @itf(digit)
p -= 1
}
}
return i
}
fmt_bool := fn(buf: []u8, v: bool): uint {
if v {
memcpy(str.ptr, "true".ptr, 4)
memcpy(buf.ptr, "true".ptr, 4)
return 4
} else {
memcpy(str.ptr, "false".ptr, 5)
memcpy(buf.ptr, "false".ptr, 5)
return 5
}
}
fmt_container := fn(buf: []u8, v: @Any()): uint {
T := TypeOf(v)
i := 0
len := 0
if T.kind() == .Struct {
memcpy(buf.ptr + len, T.name().ptr, T.name().len)
len += T.name().len
memcpy(buf.ptr + len, ".(".ptr, 2)
len += 2
} else if T.kind() == .Slice | T.kind() == .Array {
memcpy(buf.ptr + len, T.Child().name().ptr, T.Child().name().len)
len += T.Child().name().len
memcpy(buf.ptr + len, ".[".ptr, 2)
len += 2
} else if T.kind() == .Tuple {
memcpy(buf.ptr + len, ".(".ptr, 2)
len += 2
}
if T.kind() == .Slice {
loop {
len += @inline(format, buf[len..], v[i])
i += 1
if i == v.len break else {
memcpy(buf.ptr + len, ", ".ptr, 2)
len += 2
}
}
} else {
$loop {
len += @inline(format, buf[len..], v[i])
i += 1
if i == T.len() break else {
memcpy(buf.ptr + len, ", ".ptr, 2)
len += 2
}
}
}
if T.kind() == .Struct | T.kind() == .Tuple {
*(buf.ptr + len) = ')'
len += 1
} else if T.kind() == .Slice | T.kind() == .Array {
*(buf.ptr + len) = ']'
len += 1
}
return len
}
fmt_optional := fn(buf: []u8, v: @Any()): uint {
if v != null return format(buf, @as(@ChildOf(@TypeOf(v)), v))
memcpy(buf.ptr, @nameof(@TypeOf(v)).ptr, @nameof(@TypeOf(v)).len)
memcpy(buf.ptr + @nameof(@TypeOf(v)).len, ".null".ptr, 5)
return @nameof(@TypeOf(v)).len + 5
}
// todo: clean up this and other functions
fmt_enum := fn(buf: []u8, v: @Any()): uint {
T := @TypeOf(v)
len := @nameof(T).len;
memcpy(buf.ptr, @nameof(T).ptr, len)
memcpy(buf.ptr + len, ".(".ptr, 2)
len += 2
len += fmt_int(buf[len..], @as(Type(T).USize(), @bitcast(v)), 10);
memcpy(buf.ptr + len, ")".ptr, 1)
return len + 1
}
format := fn(buf: []u8, v: @Any()): uint {
T := TypeOf(v)
match T.kind() {
.Pointer => return fmt_int(@as(uint, @bitcast(v)), buf, 16),
.Pointer => return fmt_int(buf, @as(uint, @bitcast(v)), 16),
.Builtin => {
if T.is_int() return fmt_int(v, buf, 10)
if T.is_bool() return fmt_bool(v, buf)
if T.is_int() return fmt_int(buf, v, 10)
if T.is_bool() return fmt_bool(buf, v)
if T.is_float() return fmt_float(buf, v, 1 << 32, 10)
},
.Struct => return fmt_container(buf, v),
.Tuple => return fmt_container(buf, v),
.Slice => return fmt_container(buf, v),
.Array => return fmt_container(buf, v),
.Optional => return fmt_optional(buf, v),
.Enum => return fmt_enum(buf, v),
_ => @error("format(", T, ") is not supported."),
}
return 0
}
format_with_str := fn(str: []u8, buf: []u8, v: @Any()): uint {
memcpy(buf.ptr, str.ptr, str.len)
return str.len
T := TypeOf(v)
n := string.count(str, '{')
if n != string.count(str, '}') panic("Missing closing '}' in format string.")
if T.kind() == .Tuple {
if T.len() != n panic("Format string has different number of '{}' than args given.")
m := 0
i := 0
j := 0
$loop if m > T.len() break else {
if m == T.len() {
loop if i == str.len break else {
buf[j] = str[i]
i += 1
j += 1
}
m += 1
} else {
v2 := v[m]
loop if i == str.len break else {
if str[i] == '{' & str[i + 1] == '}' {
j += format(buf[j..], v2)
i += 2
break
} else {
buf[j] = str[i]
i += 1
j += 1
}
}
m += 1
}
}
return j
} else if n > 1 {
panic("Format string has multiple '{}' but value provided is not a tuple.")
} else {
i := 0
j := 0
loop if i == str.len break else {
if str[i] == '{' & str[i + 1] == '}' {
j += format(buf[j..], v)
i += 2
} else {
buf[j] = str[i]
i += 1
j += 1
}
}
return j
}
}

View file

@ -18,12 +18,14 @@ target_hbvm_ableos := @use("target/hbvm_ableos.hb")
target_c_native := @use("target/c_native.hb")
target := @use("target/target.hb").target
$exit := fn(code: int): void {
_ = target.exit(code)
$exit := fn(code: int): never {
target.exit(code)
die
}
$panic := fn(message: ?[]u8): void {
// ! (compiler) bug: inlining here crashes the parser. nice.
// ! (c_native) (compiler) bug: NOT inlining here makes it sometimes not work
panic := fn(message: ?[]u8): never {
if message != null log.error(message) else log.error("The program called panic.")
exit(1)
}
@ -86,6 +88,9 @@ Type := fn($T: type): type return struct {
This := fn(): type {
return T
}
$name := fn(): []u8 {
return @nameof(T)
}
$is_bool := fn(): bool {
return T == bool
}