From 4be08ee0bd806fb1faccf055c5a4d847c3a95314 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sat, 26 Oct 2024 21:00:07 +0200 Subject: [PATCH] `string::new_struct_to_str` and `io::struct_to_format` to dump struct data. `io::print` will now print structs. --- lib/std/core/runtime.c3 | 2 +- lib/std/core/string.c3 | 12 +++++ lib/std/io/formatter.c3 | 45 ++++++++++++++++--- lib/std/io/io.c3 | 8 +++- releasenotes.md | 2 + test/test_suite/debug_symbols/defer_macro.c3t | 2 +- 6 files changed, 62 insertions(+), 9 deletions(-) diff --git a/lib/std/core/runtime.c3 b/lib/std/core/runtime.c3 index d12daccf1..bec99160d 100644 --- a/lib/std/core/runtime.c3 +++ b/lib/std/core/runtime.c3 @@ -4,7 +4,7 @@ module std::core::runtime; import libc, std::time, std::io, std::sort; -struct ReflectedParam @if(!$defined(ReflectedParam)) +struct ReflectedParam (Printable) @if(!$defined(ReflectedParam)) { String name; typeid type; diff --git a/lib/std/core/string.c3 b/lib/std/core/string.c3 index 0f8234c4a..31275f81e 100644 --- a/lib/std/core/string.c3 +++ b/lib/std/core/string.c3 @@ -1,5 +1,6 @@ module std::core::string; import std::ascii; +import std::io; distinct String @if(!$defined(String)) = inline char[]; distinct ZString = inline char*; @@ -749,4 +750,15 @@ fn String! Splitter.next(&self) return remaining; } +macro String new_struct_to_str(x, Allocator allocator = allocator::heap()) +{ + DString s; + @stack_mem(512; Allocator mem) + { + s.new_init(allocator: mem); + io::fprint(&s, x)!!; + }; + return s.copy_str(allocator); +} +macro String temp_struct_to_str(x) => new_struct_to_str(x, allocator::temp()); \ No newline at end of file diff --git a/lib/std/io/formatter.c3 b/lib/std/io/formatter.c3 index 620ca0bf8..f66882057 100644 --- a/lib/std/io/formatter.c3 +++ b/lib/std/io/formatter.c3 @@ -23,6 +23,45 @@ fault PrintFault def OutputFn = fn void!(void* buffer, char c); def FloatType = double; + +macro bool is_struct_with_default_print($Type) +{ + return $Type.kindof == STRUCT + &&& !$defined($Type.to_format) + &&& !$defined($Type.to_new_string); +} + +<* + Introspect a struct and print it to a formatter + + @require @typekind(value) == STRUCT `This macro is only valid on macros` +*> +macro usz! struct_to_format(value, Formatter* f, bool $force_dump) +{ + var $Type = $typeof(value); + usz total = f.print("{ ")!; + $foreach ($i, $member : $Type.membersof) + $if $i > 0: + total += f.print(", ")!; + $endif + $if $member.nameof != "": + total += f.printf("%s: ", $member.nameof)!; + $endif + $if ($force_dump &&& $member.typeid.kindof == STRUCT) ||| + is_struct_with_default_print($typefrom($member.typeid)): + total += struct_to_format($member.get(value), f, $force_dump)!; + $else + total += f.printf("%s", $member.get(value))!; + $endif + $endforeach + return total + f.print(" }"); +} + +fn usz! ReflectedParam.to_format(&self, Formatter* f) @dynamic +{ + return f.printf("[Parameter '%s']", self.name); +} + fn usz! Formatter.printf(&self, String format, args...) { return self.vprintf(format, args) @inline; @@ -159,12 +198,6 @@ fn usz! Formatter.out_str(&self, any arg) @private assert(i < arg.type.names.len, "Illegal enum value found, numerical value was %d.", i); return self.out_substr(arg.type.names[i]); case STRUCT: - if (arg.type == ReflectedParam.typeid) - { - ReflectedParam* param = arg.ptr; - return self.out_substr("[Parameter '") - + self.out_substr(param.name) + self.out_substr("']"); - } return self.out_substr(""); case UNION: return self.out_substr(""); diff --git a/lib/std/io/io.c3 b/lib/std/io/io.c3 index 7a7253c51..080ef6e8c 100644 --- a/lib/std/io/io.c3 +++ b/lib/std/io/io.c3 @@ -122,7 +122,13 @@ macro usz! fprint(out, x) $if $assignable(x, String): return out.write((String)x); $else - return fprintf(out, "%s", x); + $if is_struct_with_default_print($Type): + Formatter formatter; + formatter.init(&out_putstream_fn, &&(OutStream)out); + return struct_to_format(x, &formatter, false); + $else + return fprintf(out, "%s", x); + $endif $endif $endswitch } diff --git a/releasenotes.md b/releasenotes.md index cec8db846..799512652 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -19,6 +19,8 @@ - Adding constants to the Json AST #1540 - Adding info to the globals inside Json AST #1541 - Null-check function pointer invocation #1573. +- `string::new_struct_to_str` and `io::struct_to_format` to dump struct data. +- `io::print` will now print structs. ### Fixes - `Unsupported int[*] $x = { 1, 2, 3, 4 }` #1489. diff --git a/test/test_suite/debug_symbols/defer_macro.c3t b/test/test_suite/debug_symbols/defer_macro.c3t index df5ae8172..ae6a2fca4 100644 --- a/test/test_suite/debug_symbols/defer_macro.c3t +++ b/test/test_suite/debug_symbols/defer_macro.c3t @@ -680,7 +680,7 @@ no_match: ; preds = %compare !90 = !DIFile(filename: "io.c3" !91 = !DILocation(line: 42, column: 7, scope: !78) !92 = !DILocalVariable(name: "len" -!93 = distinct !DISubprogram(name: "fprintn", linkageName: "fprintn", scope: !90, file: !90, line: 166, scopeLine: 166, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !5, retainedNodes: !20) +!93 = distinct !DISubprogram(name: "fprintn", linkageName: "fprintn", scope: !90 !94 = !DILocation !95 = !DILocation !96 = !DILocation