Skip to content

Instantly share code, notes, and snippets.

@JoshuaManton
Created May 5, 2020 09:27
Show Gist options
  • Save JoshuaManton/c5f9fb87047cbcdda8f0cf6e1c47eed7 to your computer and use it in GitHub Desktop.
Save JoshuaManton/c5f9fb87047cbcdda8f0cf6e1c47eed7 to your computer and use it in GitHub Desktop.
import "core:fmt"
import "core:mem"
import "core:reflect"
import "core:strings"
import rt "core:runtime"
pretty_print :: proc(thing: any) {
sb: strings.Builder;
defer strings.destroy_builder(&sb);
print_value(&sb, thing.data, type_info_of(thing.id), 0);
println(strings.to_string(sb));
print_value :: proc(sb: ^strings.Builder, data: rawptr, ti: ^rt.Type_Info, indent_level: int) {
do_indent :: proc(sb: ^strings.Builder, indent_level: int) {
for i in 0..<indent_level do sbprint(sb, " ");
}
indent_level := indent_level;
switch kind in ti.variant {
case rt.Type_Info_Named: print_value(sb, data, kind.base, indent_level);
case rt.Type_Info_Integer: {
if kind.signed {
switch kind.endianness {
case .Little: {
switch ti.size {
case 1: fmt.sbprint(sb, (cast(^i8 )data)^);
case 2: fmt.sbprint(sb, (cast(^i16le)data)^);
case 4: fmt.sbprint(sb, (cast(^i32le)data)^);
case 8: fmt.sbprint(sb, (cast(^i64le)data)^);
case: panic(tprint(ti.size));
}
}
case .Big: {
switch ti.size {
case 1: fmt.sbprint(sb, (cast(^i8 )data)^);
case 2: fmt.sbprint(sb, (cast(^i16be)data)^);
case 4: fmt.sbprint(sb, (cast(^i32be)data)^);
case 8: fmt.sbprint(sb, (cast(^i64be)data)^);
case: panic(tprint(ti.size));
}
}
case .Platform: {
switch ti.size {
case 1: fmt.sbprint(sb, (cast(^i8 )data)^);
case 2: fmt.sbprint(sb, (cast(^i16)data)^);
case 4: fmt.sbprint(sb, (cast(^i32)data)^);
case 8: fmt.sbprint(sb, (cast(^i64)data)^);
case: panic(tprint(ti.size));
}
}
case: panic(tprint(kind.endianness));
}
}
else {
switch kind.endianness {
case .Little: {
switch ti.size {
case 1: fmt.sbprint(sb, (cast(^u8 )data)^);
case 2: fmt.sbprint(sb, (cast(^u16le)data)^);
case 4: fmt.sbprint(sb, (cast(^u32le)data)^);
case 8: fmt.sbprint(sb, (cast(^u64le)data)^);
case: panic(tprint(ti.size));
}
}
case .Big: {
switch ti.size {
case 1: fmt.sbprint(sb, (cast(^u8 )data)^);
case 2: fmt.sbprint(sb, (cast(^u16be)data)^);
case 4: fmt.sbprint(sb, (cast(^u32be)data)^);
case 8: fmt.sbprint(sb, (cast(^u64be)data)^);
case: panic(tprint(ti.size));
}
}
case .Platform: {
switch ti.size {
case 1: fmt.sbprint(sb, (cast(^u8 )data)^);
case 2: fmt.sbprint(sb, (cast(^u16)data)^);
case 4: fmt.sbprint(sb, (cast(^u32)data)^);
case 8: fmt.sbprint(sb, (cast(^u64)data)^);
case: panic(tprint(ti.size));
}
}
case: panic(tprint(kind.endianness));
}
}
}
case rt.Type_Info_Rune: fmt.sbprint(sb, (cast(^rune)data)^);
case rt.Type_Info_Float: {
switch ti.size {
case 4: sbprint(sb, (cast(^f32)data)^);
case 8: sbprint(sb, (cast(^f64)data)^);
case: panic(tprint(ti.size));
}
}
case rt.Type_Info_Struct: {
sbprint(sb, "{\n");
indent_level += 1;
for name, idx in kind.names {
type := kind.types[idx];
offset := kind.offsets[idx];
do_indent(sb, indent_level);
sbprint(sb, name, ": ", type, " = ");
print_value(sb, mem.ptr_offset(cast(^byte)data, cast(int)offset), type, indent_level);
sbprint(sb, ",\n");
}
indent_level -= 1;
do_indent(sb, indent_level);
sbprint(sb, "}");
}
case rt.Type_Info_Boolean: {
switch ti.size {
case 1: sbprint(sb, (cast(^b8 )data)^);
case 2: sbprint(sb, (cast(^b16)data)^);
case 4: sbprint(sb, (cast(^b32)data)^);
case 8: sbprint(sb, (cast(^b64)data)^);
case: panic(tprint(ti.size));
}
}
case rt.Type_Info_String: {
if kind.is_cstring do sbprint(sb, '"', (cast(^cstring)data)^, '"');
else do sbprint(sb, '"', (cast(^string )data)^, '"');
}
case rt.Type_Info_Pointer: {
// todo(josh): do we want to recurse maybe just once?
sbprint(sb, (cast(^rawptr)data)^);
}
case rt.Type_Info_Procedure: sbprint(sb, (cast(^rawptr)data)^);
case rt.Type_Info_Type_Id: sbprint(sb, (cast(^typeid)data)^);
case rt.Type_Info_Array: {
sbprint(sb, "[\n");
indent_level += 1;
for idx in 0..<kind.count {
offset := idx * kind.elem.size;
do_indent(sb, indent_level);
sbprint(sb, "[", idx, "]", " = ");
print_value(sb, mem.ptr_offset(cast(^byte)data, offset), kind.elem, indent_level);
sbprint(sb, ",\n");
}
indent_level -= 1;
do_indent(sb, indent_level);
sbprint(sb, "]");
}
case rt.Type_Info_Slice: {
sbprint(sb, "[\n");
indent_level += 1;
raw_slice := cast(^mem.Raw_Slice)data;
for idx in 0..<raw_slice.len {
offset := idx * kind.elem.size;
do_indent(sb, indent_level);
sbprint(sb, "[", idx, "]", " = ");
print_value(sb, mem.ptr_offset(cast(^byte)raw_slice.data, offset), kind.elem, indent_level);
sbprint(sb, ",\n");
}
indent_level -= 1;
do_indent(sb, indent_level);
sbprint(sb, "]");
}
case rt.Type_Info_Enumerated_Array: {
sbprint(sb, "[\n");
indent_level += 1;
for idx in 0..<kind.count {
offset := idx * kind.elem.size;
do_indent(sb, indent_level);
// todo(josh): the enum string thing doesn't work
sbprint(sb, "[", reflect.enum_string(any{data, kind.index.id}), "]", " = ");
print_value(sb, mem.ptr_offset(cast(^byte)data, offset), kind.elem, indent_level);
sbprint(sb, ",\n");
}
indent_level -= 1;
do_indent(sb, indent_level);
sbprint(sb, "]");
}
case rt.Type_Info_Dynamic_Array: {
sbprint(sb, "[\n");
indent_level += 1;
raw_dyn := cast(^mem.Raw_Dynamic_Array)data;
for idx in 0..<raw_dyn.len {
offset := idx * kind.elem.size;
do_indent(sb, indent_level);
sbprint(sb, "[", idx, "]", " = ");
print_value(sb, mem.ptr_offset(cast(^byte)raw_dyn.data, offset), kind.elem, indent_level);
sbprint(sb, ",\n");
}
indent_level -= 1;
do_indent(sb, indent_level);
sbprint(sb, "]");
}
case rt.Type_Info_Union: {
// todo(josh): union types get printed wrong
tag_ti := get_union_type_info(any{data, ti.id});
print_value(sb, data, tag_ti, indent_level);
}
case rt.Type_Info_Enum: sbprint(sb, reflect.enum_string(any{data, ti.id}));
case rt.Type_Info_Any: sbprint(sb, "(", (cast(^any)data)^.id, ") ", (cast(^any)data)^);
case rt.Type_Info_Map: panic("Type_Info_Map");
case rt.Type_Info_Bit_Field: panic("Type_Info_Bit_Field");
case rt.Type_Info_Bit_Set: panic("Type_Info_Bit_Set");
case rt.Type_Info_Complex: panic("Type_Info_Complex");
case rt.Type_Info_Quaternion: panic("Type_Info_Quaternion");
case rt.Type_Info_Opaque: panic("Type_Info_Opaque");
case rt.Type_Info_Simd_Vector: panic("Type_Info_Simd_Vector");
case rt.Type_Info_Tuple: panic("Type_Info_Tuple");
}
get_union_type_info :: proc(v : any) -> ^rt.Type_Info {
if tag := get_union_tag(v); tag > 0 {
info := rt.type_info_base(type_info_of(v.id)).variant.(rt.Type_Info_Union);
return info.variants[tag - 1];
}
return nil;
}
get_union_tag :: proc(v : any) -> i64 {
info, ok := rt.type_info_base(type_info_of(v.id)).variant.(rt.Type_Info_Union);
assert(ok, tprint(v));
tag_ptr := uintptr(v.data) + info.tag_offset;
tag_any := any{rawptr(tag_ptr), info.tag_type.id};
tag: i64 = -1;
switch i in tag_any {
case u8: tag = i64(i);
case u16: tag = i64(i);
case u32: tag = i64(i);
case u64: tag = i64(i);
case i8: tag = i64(i);
case i16: tag = i64(i);
case i32: tag = i64(i);
case i64: tag = i64(i);
case: panic(fmt.tprint("Invalid union tag type: ", i));
}
assert(tag >= 0);
return tag;
}
}
}
Foo :: struct {
i: int,
str: string,
tagged_union: union {
string,
int,
Foo_Enum,
},
nested: Nested,
ptr: ^int,
procedure: proc(int) -> f32,
empty: Empty,
};
Nested :: struct {
aaa: any,
things: [3]string,
blahblah: []int,
dyn_blah: [dynamic]f32,
foo: Foo_Enum,
enumerated: [Foo_Enum]bool,
wow_so_nested: string,
booooooool: bool,
};
Empty :: struct {
};
Foo_Enum :: enum {
Bar, Baz, Qux,
};
the_procedure :: proc(x: int) -> f32 { return 1337; }
foozle: int = 4289;
afoozle : any = foozle;
pretty_print(Foo{149, "Henlo", .Qux, Nested{afoozle, {"oh", "my", "wowzers"}, {1, 4, 9}, {9, 3, 2, 4}, .Baz, {.Bar = true, .Baz = false, .Qux = true}, "pachooooo", true}, &foozle, the_procedure, {}});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment