From e72a2444fc2bdd289a0ded9c674bec0c3d9e7213 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Sun, 3 Dec 2023 01:45:46 +0100 Subject: [PATCH] add a degibberish function for Zig types We may use this in the future to explain types to new Zig users. --- src/analyser/InternPool.zig | 6 +- src/analyser/analyser.zig | 1 + src/analyser/degibberish.zig | 279 +++++++++++++++++++++++++++++++++++ 3 files changed, 283 insertions(+), 3 deletions(-) create mode 100644 src/analyser/degibberish.zig diff --git a/src/analyser/InternPool.zig b/src/analyser/InternPool.zig index 128163d9ca..252346597c 100644 --- a/src/analyser/InternPool.zig +++ b/src/analyser/InternPool.zig @@ -3648,14 +3648,14 @@ fn formatId( writer: anytype, ) @TypeOf(writer).Error!void { _ = options; - if (fmt.len != 0) std.fmt.invalidFmtError(fmt, InternPool.Struct.Field); + if (fmt.len != 0) std.fmt.invalidFmtError(fmt, ctx.string); ctx.ip.string_pool.mutex.lock(); defer ctx.ip.string_pool.mutex.unlock(); try writer.print("{}", .{std.zig.fmtId(ctx.ip.string_pool.stringToSliceUnsafe(ctx.string))}); } -pub fn fmtId(ip: *InternPool, field: String) std.fmt.Formatter(formatId) { - return .{ .data = .{ .ip = ip, .string = field } }; +pub fn fmtId(ip: *InternPool, string: String) std.fmt.Formatter(formatId) { + return .{ .data = .{ .ip = ip, .string = string } }; } // --------------------------------------------- diff --git a/src/analyser/analyser.zig b/src/analyser/analyser.zig index 9603aee363..b0e2084f0b 100644 --- a/src/analyser/analyser.zig +++ b/src/analyser/analyser.zig @@ -2,6 +2,7 @@ pub const completions = @import("completions.zig"); pub const InternPool = @import("InternPool.zig"); pub const StringPool = @import("string_pool.zig").StringPool; pub const encoding = @import("encoding.zig"); +pub const degibberish = @import("degibberish.zig"); comptime { const std = @import("std"); diff --git a/src/analyser/degibberish.zig b/src/analyser/degibberish.zig new file mode 100644 index 0000000000..26b888cd60 --- /dev/null +++ b/src/analyser/degibberish.zig @@ -0,0 +1,279 @@ +const std = @import("std"); +const InternPool = @import("InternPool.zig"); + +const FormatDegibberishData = struct { + ip: *InternPool, + ty: InternPool.Index, +}; + +pub fn fmtDegibberish(ip: *InternPool, ty: InternPool.Index) std.fmt.Formatter(formatDegibberish) { + const data = FormatDegibberishData{ .ip = ip, .ty = ty }; + return .{ .data = data }; +} + +fn formatDegibberish(data: FormatDegibberishData, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) @TypeOf(writer).Error!void { + if (fmt.len != 0) std.fmt.invalidFmtError(fmt, data.ty); + _ = options; + + const ip = data.ip; + var ty = data.ty; + + while (ty != .none) { + switch (ip.indexToKey(ty)) { + .simple_type, + .int_type, + => try writer.print("{}", .{ty.fmt(ip)}), + + .pointer_type => |pointer_info| { + // ignored attributes: + // - address_space + // - is_allowzero + // - is_volatile + // - packed_offset + + if (pointer_info.sentinel != .none) { + try writer.print("{} terminated ", .{pointer_info.sentinel.fmt(ip)}); + } + + // single pointer + const size_prefix = switch (pointer_info.flags.size) { + .One => "single-item pointer", + .Many => "many-item pointer", + .Slice => "slice (pointer + length)", + .C => "C pointer", + }; + + try writer.writeAll(size_prefix); + + if (pointer_info.flags.alignment != 0) { + try writer.print(" with alignment {d}", .{pointer_info.flags.alignment}); + } + + try writer.writeAll(" to "); + + if (pointer_info.flags.is_const) { + try writer.writeAll("const "); + } + + ty = pointer_info.elem_type; + continue; + }, + .array_type => |array_info| { + if (array_info.sentinel != .none) { + try writer.print("{} terminated ", .{array_info.sentinel.fmt(ip)}); + } + try writer.print("array {d} of ", .{array_info.len}); + ty = array_info.child; + continue; + }, + .struct_type => try writer.print("struct {}", .{ty.fmt(ip)}), + .optional_type => |optional_info| { + try writer.writeAll("optional of "); + ty = optional_info.payload_type; + continue; + }, + .error_union_type => |error_union_info| { + try writer.writeAll("error union with "); + try writer.print("{}", .{fmtDegibberish(ip, error_union_info.error_set_type)}); + try writer.writeAll(" and payload "); + ty = error_union_info.payload_type; + continue; + }, + .error_set_type => |error_set_info| { + try writer.writeAll("error set of ("); + for (0..error_set_info.names.len) |i| { + if (i != 0) try writer.writeByte(','); + const name = error_set_info.names.at(@intCast(i), ip); + try writer.print("{}", .{InternPool.fmtId(ip, name)}); + } + try writer.writeAll(")"); + }, + .enum_type => try writer.print("enum {}", .{ty.fmt(ip)}), + .function_type => |function_info| { + try writer.writeAll("function ("); + for (0..function_info.args.len) |i| { + if (i != 0) try writer.writeAll(", "); + const arg_ty = function_info.args.at(@intCast(i), ip); + try writer.print("{}", .{fmtDegibberish(ip, arg_ty)}); + } + try writer.writeAll(") returning "); + ty = function_info.return_type; + continue; + }, + .union_type => try writer.print("union {}", .{ty.fmt(ip)}), + .tuple_type => |tuple_info| { + std.debug.assert(tuple_info.types.len == tuple_info.values.len); + try writer.writeAll("tuple of ("); + for (0..tuple_info.types.len) |i| { + if (i != 0) try writer.writeAll(", "); + const field_ty = tuple_info.types.at(@intCast(i), ip); + try writer.print("{}", .{fmtDegibberish(ip, field_ty)}); + } + try writer.writeAll(")"); + }, + .vector_type => |vector_info| { + try writer.print("vector {d} of ", .{vector_info.len}); + ty = vector_info.child; + continue; + }, + .anyframe_type => |anyframe_info| { + try writer.writeAll("function frame returning "); + ty = anyframe_info.child; + continue; + }, + + .simple_value, + .int_u64_value, + .int_i64_value, + .int_big_value, + .float_16_value, + .float_32_value, + .float_64_value, + .float_80_value, + .float_128_value, + .float_comptime_value, + .optional_value, + .slice, + .aggregate, + .union_value, + .error_value, + .null_value, + .undefined_value, + .unknown_value, + => unreachable, + } + break; + } +} + +test "degibberish - simple types" { + const gpa = std.testing.allocator; + var ip = try InternPool.init(gpa); + defer ip.deinit(gpa); + + try std.testing.expectFmt("u32", "{}", .{fmtDegibberish(&ip, .u32_type)}); + try std.testing.expectFmt("comptime_float", "{}", .{fmtDegibberish(&ip, .comptime_float_type)}); +} + +test "degibberish - pointer types" { + const gpa = std.testing.allocator; + var ip = try InternPool.init(gpa); + defer ip.deinit(gpa); + + try std.testing.expectFmt("many-item pointer to u8", "{}", .{fmtDegibberish(&ip, .manyptr_u8_type)}); + try std.testing.expectFmt("many-item pointer to const u8", "{}", .{fmtDegibberish(&ip, .manyptr_const_u8_type)}); + try std.testing.expectFmt("0 terminated many-item pointer to const u8", "{}", .{fmtDegibberish(&ip, .manyptr_const_u8_sentinel_0_type)}); + try std.testing.expectFmt("single-item pointer to const comptime_int", "{}", .{fmtDegibberish(&ip, .single_const_pointer_to_comptime_int_type)}); + try std.testing.expectFmt("slice (pointer + length) to const u8", "{}", .{fmtDegibberish(&ip, .slice_const_u8_type)}); + try std.testing.expectFmt("0 terminated slice (pointer + length) to const u8", "{}", .{fmtDegibberish(&ip, .slice_const_u8_sentinel_0_type)}); +} + +test "degibberish - array types" { + const gpa = std.testing.allocator; + var ip = try InternPool.init(gpa); + defer ip.deinit(gpa); + + const @"[3:0]u8" = try ip.get(gpa, .{ .array_type = .{ .len = 3, .child = .u8_type, .sentinel = .zero_u8 } }); + const @"[0]u32" = try ip.get(gpa, .{ .array_type = .{ .len = 0, .child = .u32_type } }); + + try std.testing.expectFmt("0 terminated array 3 of u8", "{}", .{fmtDegibberish(&ip, @"[3:0]u8")}); + try std.testing.expectFmt("array 0 of u32", "{}", .{fmtDegibberish(&ip, @"[0]u32")}); +} + +test "degibberish - optional types" { + const gpa = std.testing.allocator; + var ip = try InternPool.init(gpa); + defer ip.deinit(gpa); + + const @"?u32" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = .u32_type } }); + + try std.testing.expectFmt("optional of u32", "{}", .{fmtDegibberish(&ip, @"?u32")}); +} + +test "degibberish - error union types" { + const gpa = std.testing.allocator; + var ip = try InternPool.init(gpa); + defer ip.deinit(gpa); + + const foo_string = try ip.string_pool.getOrPutString(gpa, "foo"); + const bar_string = try ip.string_pool.getOrPutString(gpa, "bar"); + const baz_string = try ip.string_pool.getOrPutString(gpa, "baz"); + + const @"error{foo,bar,baz}" = try ip.get(gpa, .{ .error_set_type = .{ + .names = try ip.getStringSlice(gpa, &.{ foo_string, bar_string, baz_string }), + .owner_decl = .none, + } }); + + const @"error{foo,bar,baz}!u32" = try ip.get(gpa, .{ .error_union_type = .{ + .error_set_type = @"error{foo,bar,baz}", + .payload_type = .u32_type, + } }); + + try std.testing.expectFmt("error union with error set of (foo,bar,baz) and payload u32", "{}", .{fmtDegibberish(&ip, @"error{foo,bar,baz}!u32")}); +} + +test "degibberish - error set types" { + const gpa = std.testing.allocator; + var ip = try InternPool.init(gpa); + defer ip.deinit(gpa); + + const foo_string = try ip.string_pool.getOrPutString(gpa, "foo"); + const bar_string = try ip.string_pool.getOrPutString(gpa, "bar"); + const baz_string = try ip.string_pool.getOrPutString(gpa, "baz"); + + const @"error{foo,bar,baz}" = try ip.get(gpa, .{ .error_set_type = .{ + .names = try ip.getStringSlice(gpa, &.{ foo_string, bar_string, baz_string }), + .owner_decl = .none, + } }); + + try std.testing.expectFmt("error set of (foo,bar,baz)", "{}", .{fmtDegibberish(&ip, @"error{foo,bar,baz}")}); +} + +test "degibberish - function types" { + const gpa = std.testing.allocator; + var ip = try InternPool.init(gpa); + defer ip.deinit(gpa); + + const @"fn(u32, void) type" = try ip.get(gpa, .{ .function_type = .{ + .args = try ip.getIndexSlice(gpa, &.{ .u32_type, .void_type }), + .return_type = .type_type, + } }); + + try std.testing.expectFmt("function () returning noreturn", "{}", .{fmtDegibberish(&ip, .fn_noreturn_no_args_type)}); + try std.testing.expectFmt("function () returning void", "{}", .{fmtDegibberish(&ip, .fn_void_no_args_type)}); + try std.testing.expectFmt("function (u32, void) returning type", "{}", .{fmtDegibberish(&ip, @"fn(u32, void) type")}); +} + +test "degibberish - tuple types" { + const gpa = std.testing.allocator; + var ip = try InternPool.init(gpa); + defer ip.deinit(gpa); + + const @"struct{u32, comptime_float, c_int}" = try ip.get(gpa, .{ .tuple_type = .{ + .types = try ip.getIndexSlice(gpa, &.{ .u32_type, .comptime_float_type, .c_int_type }), + .values = try ip.getIndexSlice(gpa, &.{ .none, .none, .none }), + } }); + + try std.testing.expectFmt("tuple of (u32, comptime_float, c_int)", "{}", .{fmtDegibberish(&ip, @"struct{u32, comptime_float, c_int}")}); +} + +test "degibberish - vector types" { + const gpa = std.testing.allocator; + var ip = try InternPool.init(gpa); + defer ip.deinit(gpa); + + const @"@Vector(3, u8)" = try ip.get(gpa, .{ .vector_type = .{ .len = 3, .child = .u8_type } }); + const @"@Vector(0, u32)" = try ip.get(gpa, .{ .vector_type = .{ .len = 0, .child = .u32_type } }); + + try std.testing.expectFmt("vector 3 of u8", "{}", .{fmtDegibberish(&ip, @"@Vector(3, u8)")}); + try std.testing.expectFmt("vector 0 of u32", "{}", .{fmtDegibberish(&ip, @"@Vector(0, u32)")}); +} + +test "degibberish - anyframe types" { + const gpa = std.testing.allocator; + var ip = try InternPool.init(gpa); + defer ip.deinit(gpa); + + const @"anyframe->u32" = try ip.get(gpa, .{ .anyframe_type = .{ .child = .u32_type } }); + try std.testing.expectFmt("function frame returning u32", "{}", .{fmtDegibberish(&ip, @"anyframe->u32")}); +}