write the rest of the formatter, but compiler wants to kill me
This commit is contained in:
parent
73184afea4
commit
15f1351049
|
@ -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}`
|
||||
|
|
221
src/lib/fmt.hb
221
src/lib/fmt.hb
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue