From: Brendan Hansen Date: Thu, 30 Dec 2021 00:28:37 +0000 (-0600) Subject: added custom formatters to core libs X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=74986695fb89d8cc4c6470602d422b240ea5e82a;p=onyx.git added custom formatters to core libs --- diff --git a/core/container/map.onyx b/core/container/map.onyx index 76538438..d1df1e70 100644 --- a/core/container/map.onyx +++ b/core/container/map.onyx @@ -5,6 +5,7 @@ package core.map hash :: package core.hash memory :: package core.memory math :: package core.math + conv :: package core.conv use package core.intrinsics.onyx { __zero_value, __initialize } } @@ -144,6 +145,18 @@ empty :: (use map: ^Map($K, $V)) -> bool { return entries.count == 0; } +format_map :: (output: ^conv.Format_Output, format: ^conv.Format, x: ^Map($K, $V)) { + output->write("{\n"); + for^ e: x.entries { + output->write(" "); + conv.format_any(output, format, .{^e.key, K}); + output->write(" => "); + conv.format_any(output, format, .{^e.value, V}); + output->write("\n"); + } + output->write("}"); +} + // // Private symbols // diff --git a/core/conv.onyx b/core/conv.onyx index 49b4c3f4..af4f42fa 100644 --- a/core/conv.onyx +++ b/core/conv.onyx @@ -1,5 +1,18 @@ package core.conv +#local { + map :: package core.map + custom_formatters: Map(type_expr, (^Format_Output, ^Format, rawptr) -> void); +} + +custom_formatters_initialized :: #init () { + map.init(^custom_formatters, default=null_proc); +} + +register_custom_formatter :: (formatter: (^Format_Output, ^Format, ^$T) -> void) { + custom_formatters[T] = formatter; +} + str_to_i64 :: (s: str) -> i64 { use package core @@ -158,34 +171,7 @@ i64_to_str :: (n: i64, base: u64, buf: [] u8, min_length := 0, prefix := false) return str.{ data = c + 1, count = len }; } -#if false { -@Hack @Cleanup // This is a big hack but it will work for now -f64_to_str :: (f: f64, buf: [] u8) -> str { - f *= 10000.0; - v := cast(i64) f; - - len := 0; - if v < ~~0 { - v = -v; - - buf[0] = #char "-"; - len += 1; - } - - s1 := i64_to_str(v / 10000, 10, buf); - for i: 0 .. s1.count do buf.data[i + len] = s1.data[i]; - buf.data[s1.count + len] = #char "."; - len += s1.count + 1; - - s2 := i64_to_str(v % 10000, 10, buf, min_length = 4); - for i: 0 .. s2.count do buf.data[len + i] = s2.data[i]; - len += s2.count; - - return str.{ buf.data, len }; -} -} - -// This is better than the above version, but still relies on converting the integer +// This is better than what used to be, but still relies on converting the integer // part of the float to an integer, which could overflow. f64_to_str :: (f: f64, buf: [] u8, digits_after_decimal := 4) -> str { math :: package core.math @@ -221,48 +207,52 @@ f64_to_str :: (f: f64, buf: [] u8, digits_after_decimal := 4) -> str { return str.{ buf.data, len }; } -str_format :: (buffer: [] u8, format: str, va: ..any) -> str { - return str_format_va(buffer, format, ~~va); -} +@Remove // old aliases to not break old programs +str_format :: format +str_format_va :: format_va + +Format_Output :: struct { + data: ^u8; + count: u32; + capacity: u32; + + write :: #match { + (use output: ^Format_Output, c: u8) { + if count >= capacity do return; -str_format_va :: (buffer: [] u8, format: str, va: [] any) -> str { - Output :: struct { - data: ^u8; - count: u32; - capacity: u32; + data[count] = c; + count += 1; + }, - write :: #match { - (use output: ^Output, c: u8) { + (use output: ^Format_Output, s: str) { + for c: s { if count >= capacity do return; data[count] = c; count += 1; - }, - - (use output: ^Output, s: str) { - for c: s { - if count >= capacity do return; - - data[count] = c; - count += 1; - } } } } +} - output := Output.{ buffer.data, 0, buffer.count }; +Format :: struct { + pretty_printing := false; + quote_strings := false; + dereference := false; + digits_after_decimal := cast(u32) 4; - Format :: struct { - pretty_printing := false; - quote_strings := false; - dereference := false; - digits_after_decimal := cast(u32) 4; + indentation := cast(u32) 0; + base := cast(u64) 10; + minimum_width := cast(u32) 0; +} - indentation := cast(u32) 0; - base := cast(u64) 10; - minimum_width := cast(u32) 0; - } +format :: (buffer: [] u8, format: str, va: ..any) -> str { + return format_va(buffer, format, ~~va); +} + +format_va :: (buffer: [] u8, format: str, va: [] any) -> str { + output := Format_Output.{ buffer.data, 0, buffer.count }; vararg_index := 0; while i := 0; i < format.count { @@ -341,7 +331,7 @@ str_format_va :: (buffer: [] u8, format: str, va: [] any) -> str { case #char "}" { arg := va[vararg_index]; vararg_index += 1; - print_any(^output, formatting, arg); + format_any(^output, ^formatting, arg); break break; } @@ -365,417 +355,292 @@ str_format_va :: (buffer: [] u8, format: str, va: [] any) -> str { } return .{ output.data, output.count }; +} - print_any :: (output: ^Output, formatting: Format, v: any) { - use package builtin.type_info - array :: package core.array; +format_any :: (output: ^Format_Output, formatting: ^Format, v: any) { + use package builtin.type_info + array :: package core.array; - if formatting.dereference { - ti := get_type_info(v.type); - if ti.kind == .Pointer { - formatting.dereference = false; + if formatting.dereference { + ti := get_type_info(v.type); + if ti.kind == .Pointer { + formatting.dereference = false; - new_any: any; - new_any.type = (cast(^Type_Info_Pointer) ti).to; - new_any.data = *(cast(^rawptr) v.data); - print_any(output, formatting, new_any); - return; - } + new_any: any; + new_any.type = (cast(^Type_Info_Pointer) ti).to; + new_any.data = *(cast(^rawptr) v.data); + format_any(output, formatting, new_any); + return; } + } - switch v.type { - case bool { - value := *(cast(^bool) v.data); - if value do output->write("true"); - else do output->write("false"); - } - - case u8 { - value := *(cast(^u8) v.data); - - if value > 31 { - output->write(value); - - } else { - ibuf : [128] u8; - istr := i64_to_str(~~value, 16, ~~ibuf, prefix=true); - output->write(istr); - } - } + if custom_formatters[v.type] != null_proc { + custom_formatters[v.type](output, formatting, v.data); + return; + } - case i16, u16 { - value := *(cast(^i16) v.data); + switch v.type { + case bool { + value := *(cast(^bool) v.data); + if value do output->write("true"); + else do output->write("false"); + } - ibuf : [128] u8; - istr := i64_to_str(~~value, formatting.base, ~~ibuf, min_length=formatting.minimum_width); - output->write(istr); - } + case u8 { + value := *(cast(^u8) v.data); - case i32, u32 { - value := *(cast(^i32) v.data); + if value > 31 { + output->write(value); + } else { ibuf : [128] u8; - istr := i64_to_str(~~value, formatting.base, ~~ibuf, min_length=formatting.minimum_width); + istr := i64_to_str(~~value, 16, ~~ibuf, prefix=true); output->write(istr); } + } - case i64, u64 { - value := *(cast(^i64) v.data); - - ibuf : [128] u8; - istr := i64_to_str(~~value, formatting.base, ~~ibuf); - output->write(istr); - } + case i16, u16 { + value := *(cast(^i16) v.data); - case f32 { - value := *(cast(^f32) v.data); + ibuf : [128] u8; + istr := i64_to_str(~~value, formatting.base, ~~ibuf, min_length=formatting.minimum_width); + output->write(istr); + } - fbuf : [128] u8; - fstr := f64_to_str(~~value, ~~fbuf, formatting.digits_after_decimal); - output->write(fstr); - } + case i32, u32 { + value := *(cast(^i32) v.data); - case f64 { - value := *(cast(^f64) v.data); + ibuf : [128] u8; + istr := i64_to_str(~~value, formatting.base, ~~ibuf, min_length=formatting.minimum_width); + output->write(istr); + } - fbuf : [128] u8; - fstr := f64_to_str(~~value, ~~fbuf, formatting.digits_after_decimal); - output->write(fstr); - } + case i64, u64 { + value := *(cast(^i64) v.data); - case str { - if formatting.quote_strings do output->write("\""); - @Todo // escape '"' when quote_strings is enabled. - output->write(*(cast(^str) v.data)); - if formatting.quote_strings do output->write("\""); - } + ibuf : [128] u8; + istr := i64_to_str(~~value, formatting.base, ~~ibuf); + output->write(istr); + } - case rawptr { - value := *(cast(^rawptr) v.data); + case f32 { + value := *(cast(^f32) v.data); - if value == null { - output->write("(null)"); - } else { - ibuf : [128] u8; - istr := i64_to_str(~~value, 16, ~~ibuf, prefix=true); - output->write(istr); - } - } + fbuf : [128] u8; + fstr := f64_to_str(~~value, ~~fbuf, formatting.digits_after_decimal); + output->write(fstr); + } - case type_expr { - value := *(cast(^type_expr) v.data); + case f64 { + value := *(cast(^f64) v.data); - io :: package core.io + fbuf : [128] u8; + fstr := f64_to_str(~~value, ~~fbuf, formatting.digits_after_decimal); + output->write(fstr); + } - buf : [256] u8; + case str { + if formatting.quote_strings do output->write("\""); + @Todo // escape '"' when quote_strings is enabled. + output->write(*(cast(^str) v.data)); + if formatting.quote_strings do output->write("\""); + } - // This is a little gross but the only way to output the type name for a type_expr - // is through a io.Writer. That should maybe be changed in the future? Also, I think - // 256 bytes is enough for the name of a type but I'm not entirely sure... - stream := io.string_stream_make(~~buf); - writer := io.writer_make(^stream); - type_info.write_type_name(^writer, value); + case rawptr { + value := *(cast(^rawptr) v.data); - output->write(io.string_stream_to_str(^stream)); + if value == null { + output->write("(null)"); + } else { + ibuf : [128] u8; + istr := i64_to_str(~~value, 16, ~~ibuf, prefix=true); + output->write(istr); } + } - case #default { - info := get_type_info(v.type); + case type_expr { + value := *(cast(^type_expr) v.data); - if info.kind == .Struct { - s := cast(^Type_Info_Struct) info; + io :: package core.io - if s.name.count > 0 { - output->write(s.name); - output->write(" { "); - } else { - output->write("{ "); - } + buf : [256] u8; - { - format := formatting; - format.quote_strings = true; - if format.pretty_printing { - format.indentation += 4; - } - - for ^member: s.members { - if member != s.members.data do output->write(", "); + // This is a little gross but the only way to output the type name for a type_expr + // is through a io.Writer. That should maybe be changed in the future? Also, I think + // 256 bytes is enough for the name of a type but I'm not entirely sure... + stream := io.string_stream_make(~~buf); + writer := io.writer_make(^stream); + type_info.write_type_name(^writer, value); - if formatting.pretty_printing { - output->write(#char "\n"); - for i: format.indentation do output->write(#char " "); - } - - output->write(member.name); - output->write(" = "); - - print_any(output, format, .{ ~~(cast(^u8) v.data + member.offset), member.type }); - } - } - - if formatting.pretty_printing { - output->write(#char "\n"); - for i: formatting.indentation do output->write(#char " "); - output->write("}"); - - } else { - output->write(" }"); - } - } - - if info.kind == .Function { - output->write("func["); - - value := *(cast(^i32) v.data); - - ibuf : [128] u8; - istr := i64_to_str(~~value, 10, ~~ibuf); - output->write(istr); + output->write(io.string_stream_to_str(^stream)); + } - output->write("]"); - } + case #default { + info := get_type_info(v.type); - if info.kind == .Pointer { - value := *(cast(^rawptr) v.data); + if info.kind == .Struct { + s := cast(^Type_Info_Struct) info; - ibuf : [128] u8; - istr := i64_to_str(~~value, 16, ~~ibuf, prefix=true); - output->write(istr); + if s.name.count > 0 { + output->write(s.name); + output->write(" { "); + } else { + output->write("{ "); } - // This assumes that the following type_info kinds are basically the same. - if info.kind == .Dynamic_Array || info.kind == .Slice || info.kind == .Variadic_Argument { - if formatting.pretty_printing { - output->write("["); - } else { - output->write("[ "); - } - - a := cast(^Type_Info_Dynamic_Array) info; - arr := cast(^array.Untyped_Array) v.data; - data := arr.data; - count := arr.count; - - format := formatting; + { + format := *formatting; format.quote_strings = true; - if format.pretty_printing do format.indentation += 4; - - for i: count { - if i != 0 do output->write(", "); + if format.pretty_printing { + format.indentation += 4; + } + + for ^member: s.members { + if member != s.members.data do output->write(", "); if formatting.pretty_printing { - output->write("\n"); - for _: format.indentation do output->write(#char " "); + output->write(#char "\n"); + for i: format.indentation do output->write(#char " "); } - print_any(output, format, .{ ~~(cast(^u8) data + get_type_info(a.of).size * i), a.of }); - } - - - if formatting.pretty_printing { - format.indentation -= 4; - output->write("\n"); - for _: format.indentation do output->write(#char " "); - output->write(#char "]"); + output->write(member.name); + output->write(" = "); - } else { - output->write(" ]"); + format_any(output, ^format, .{ ~~(cast(^u8) v.data + member.offset), member.type }); } } - - if info.kind == .Array { - output->write("[ "); - - a := cast(^Type_Info_Array) info; - data := v.data; - - for i: a.count { - if i != 0 do output->write(", "); - - print_any(output, formatting, .{ ~~(cast(^u8) data + get_type_info(a.of).size * i), a.of }); - } - - output->write(" ]"); + + if formatting.pretty_printing { + output->write(#char "\n"); + for i: formatting.indentation do output->write(#char " "); + output->write("}"); + + } else { + output->write(" }"); } + } - if info.kind == .Enum { - e := cast(^Type_Info_Enum) info; - - value: u64; - switch e.backing_type { - case i8, u8 do value = cast(u64) *(cast(^u8) v.data); - case i16, u16 do value = cast(u64) *(cast(^u16) v.data); - case i32, u32 do value = cast(u64) *(cast(^u32) v.data); - case i64, u64 do value = cast(u64) *(cast(^u64) v.data); - case #default do assert(false, "Bad enum backing type"); - } + if info.kind == .Function { + output->write("func["); - if !e.is_flags { - for ^member: e.members { - if value == member.value { - output->write(member.name); - break break; - } - } + value := *(cast(^i32) v.data); - output->write("UNKNOWN"); + ibuf : [128] u8; + istr := i64_to_str(~~value, 10, ~~ibuf); + output->write(istr); - } else { - first := true; - for ^member: e.members { - if value & member.value != 0 { - if !first do output->write(" | "); - output->write(member.name); - first = false; - } - } + output->write("]"); + } - if first { - output->write("None"); - } - } - } + if info.kind == .Pointer { + value := *(cast(^rawptr) v.data); - if info.kind == .Distinct { - d := cast(^Type_Info_Distinct) info; + ibuf : [128] u8; + istr := i64_to_str(~~value, 16, ~~ibuf, prefix=true); + output->write(istr); + } - output->write(d.name); + // This assumes that the following type_info kinds are basically the same. + if info.kind == .Dynamic_Array || info.kind == .Slice || info.kind == .Variadic_Argument { + if formatting.pretty_printing { output->write("["); - print_any(output, formatting, any.{ v.data, d.base_type }); - output->write("]"); + } else { + output->write("[ "); } - } - } - } -} - - -// Old % style print formatting -#if false { -str_format :: (format: str, buffer: [] u8, va: ...) -> str { - return str_format_va(format, buffer, va); -} - -str_format_va :: (format: str, buffer: [] u8, va: vararg) -> str { - len := 0; - state := 0; - - for ch: format do switch (state) { - case 0 { - if ch == #char "%" do state = 1; - else { - buffer[len] = ch; - len += 1; - } - } - case #default { - switch (ch) { - case #char "%" { buffer[len] = ch; len += 1; } + a := cast(^Type_Info_Dynamic_Array) info; + arr := cast(^array.Untyped_Array) v.data; + data := arr.data; + count := arr.count; - case #char "i" { - n : i32; - if !vararg_get(va, ^n) do return buffer.data[0 .. 0]; + format := *formatting; + format.quote_strings = true; + if format.pretty_printing do format.indentation += 4; - ibuf : [128] u8; - istr := i64_to_str(~~n, 10, ibuf[0 .. 128]); + for i: count { + if i != 0 do output->write(", "); - for a: istr { - buffer[len] = a; - len += 1; + if formatting.pretty_printing { + output->write("\n"); + for _: format.indentation do output->write(#char " "); } - } - - case #char "l" { - n : i64; - if !vararg_get(va, ^n) do return buffer.data[0 .. 0]; - ibuf : [128] u8; - istr := i64_to_str(n, 10, ibuf[0 .. 128]); - - for a: istr { - buffer[len] = a; - len += 1; - } + format_any(output, ^format, .{ ~~(cast(^u8) data + get_type_info(a.of).size * i), a.of }); } - case #char "f" { - n : f32; - if !vararg_get(va, ^n) do return buffer.data[0 .. 0]; - fbuf : [128] u8; - fstr := f64_to_str(~~n, fbuf[0 .. 128]); + if formatting.pretty_printing { + format.indentation -= 4; + output->write("\n"); + for _: format.indentation do output->write(#char " "); + output->write(#char "]"); - for a: fstr { - buffer[len] = a; - len += 1; - } + } else { + output->write(" ]"); } + } - case #char "d" { - n : f64; - if !vararg_get(va, ^n) do return buffer.data[0 .. 0]; - - fbuf : [128] u8; - fstr := f64_to_str(n, fbuf[0 .. 128]); + if info.kind == .Array { + output->write("[ "); - for a: fstr { - buffer[len] = a; - len += 1; - } - } + a := cast(^Type_Info_Array) info; + data := v.data; - case #char "s" { - s : str; - if !vararg_get(va, ^s) do return buffer.data[0 .. 0]; + for i: a.count { + if i != 0 do output->write(", "); - for a: s { - buffer[len] = a; - len += 1; - } + format_any(output, formatting, .{ ~~(cast(^u8) data + get_type_info(a.of).size * i), a.of }); } - case #char "p" { - n : rawptr; - if !vararg_get(va, ^n) do return buffer.data[0 .. 0]; + output->write(" ]"); + } - ibuf : [128] u8; - istr := i64_to_str(~~n, 16, ibuf[0 .. 128]); + if info.kind == .Enum { + e := cast(^Type_Info_Enum) info; - for a: istr { - buffer[len] = a; - len += 1; - } + value: u64; + switch e.backing_type { + case i8, u8 do value = cast(u64) *(cast(^u8) v.data); + case i16, u16 do value = cast(u64) *(cast(^u16) v.data); + case i32, u32 do value = cast(u64) *(cast(^u32) v.data); + case i64, u64 do value = cast(u64) *(cast(^u64) v.data); + case #default do assert(false, "Bad enum backing type"); } - - case #char "c" { - c : u8; - if !vararg_get(va, ^c) do return buffer.data[0 .. 0]; - buffer[len] = c; - len += 1; - } + if !e.is_flags { + for ^member: e.members { + if value == member.value { + output->write(member.name); + break break; + } + } - case #char "b" { - b : bool; - if !vararg_get(va, ^b) do return buffer.data[0 .. 0]; + output->write("UNKNOWN"); - s := "false"; - if b do s = "true"; + } else { + first := true; + for ^member: e.members { + if value & member.value != 0 { + if !first do output->write(" | "); + output->write(member.name); + first = false; + } + } - for a: s { - buffer[len] = a; - len += 1; + if first { + output->write("None"); } } } - state = 0; + if info.kind == .Distinct { + d := cast(^Type_Info_Distinct) info; + + output->write(d.name); + output->write("["); + format_any(output, formatting, any.{ v.data, d.base_type }); + output->write("]"); + } } } - - return str.{ ~~buffer.data, len }; } -} - diff --git a/core/hash.onyx b/core/hash.onyx index 6e1d6331..a0e05e1f 100644 --- a/core/hash.onyx +++ b/core/hash.onyx @@ -10,6 +10,7 @@ to_u32 :: #match { for ch: key do hash += (hash << 5) + ~~ch; return hash; }, + (key: type_expr) -> u32 do return to_u32(cast(u32) key); } Hashable :: interface (T: type_expr) { diff --git a/src/checker.c b/src/checker.c index cd2b9a5f..f9ffff11 100644 --- a/src/checker.c +++ b/src/checker.c @@ -1443,8 +1443,10 @@ CheckStatus check_subscript(AstSubscript** psub) { CHECK(expression, &sub->addr); CHECK(expression, &sub->expr); + if (sub->addr->type == NULL) YIELD(sub->token->pos, "Waiting to know type of left-hand side of subscript."); + // NOTE: Try operator overloading before checking everything else. - if ((sub->addr->type != NULL && sub->expr->type != NULL) && + if (sub->expr->type != NULL && (sub->addr->type->kind != Type_Kind_Basic || sub->expr->type->kind != Type_Kind_Basic)) { // AstSubscript is the same as AstBinaryOp for the first sizeof(AstBinaryOp) bytes AstBinaryOp* binop = (AstBinaryOp *) sub; diff --git a/src/types.c b/src/types.c index bc2a42f9..ce1ad2a5 100644 --- a/src/types.c +++ b/src/types.c @@ -932,7 +932,7 @@ const char* type_get_unique_name(Type* type) { return bh_aprintf(global_scratch_allocator, "%s@%l", type->Distinct.name, type->id); } - default: return "unknown"; + default: return "unknown (not null)"; } } diff --git a/tests/aoc-2021/day09.onyx b/tests/aoc-2021/day09.onyx index 6121a75d..2142a042 100644 --- a/tests/aoc-2021/day09.onyx +++ b/tests/aoc-2021/day09.onyx @@ -3,7 +3,7 @@ use package core Pos :: struct {x,y: i32;} -#match hash.to_u32 (use p: Pos) => x * 5039473 + 59362447 * y; +#match hash.to_u32 (use p: Pos) => x * 13 ^ 17 * y; #operator == (p1, p2: Pos) => p1.x == p2.x && p1.y == p2.y; Cell :: struct { diff --git a/tests/i32map b/tests/i32map index 092cdba4..0e86e6ab 100644 --- a/tests/i32map +++ b/tests/i32map @@ -1,6 +1,10 @@ true false Hello World! +{ + 50 => Hello + 1234 => World! +} false false Freeing map diff --git a/tests/i32map.onyx b/tests/i32map.onyx index 09e62c62..a43af906 100644 --- a/tests/i32map.onyx +++ b/tests/i32map.onyx @@ -5,6 +5,10 @@ package main use package core main :: (args: [] cstr) { + conv.register_custom_formatter( + #solidify map.format_map { K=i32, V=str } + ); + imap : Map(i32, str); map.init(^imap, ""); defer { @@ -19,6 +23,8 @@ main :: (args: [] cstr) { printf("{}{}\n", map.get(^imap, 50), map.get(^imap, 1234)); + printf("{*}\n", ^imap); + map.delete(^imap, 50); println(map.has(^imap, 50));