diff --git a/.hgignore b/.hgignore deleted file mode 100644 index 1dfcbd2..0000000 --- a/.hgignore +++ /dev/null @@ -1 +0,0 @@ -.zig-cache/ diff --git a/reflection.zig b/reflection.zig index f2fb5af..4bf28ba 100644 --- a/reflection.zig +++ b/reflection.zig @@ -37,7 +37,6 @@ pub fn deinit(self: *Self, allocator: mem.Allocator) void { /// root starting from idx pub fn reflectT(self: *Self, comptime T: type, allocator: mem.Allocator, idx: usize) !T { const Schema = @typeInfo(T); - const flags = self.language.options.flags; if (std.meta.hasFn(T, "toJson")) { return T.toJson(self, allocator, idx); @@ -45,8 +44,7 @@ pub fn reflectT(self: *Self, comptime T: type, allocator: mem.Allocator, idx: us switch (self.language.index.get(idx)) { .null => { - if (Schema == .null) return null; - return error.TypeError; + if (Schema == .null or Schema == .optional) return null; }, .bool => |b| switch (Schema) { .bool => return b, @@ -55,7 +53,7 @@ pub fn reflectT(self: *Self, comptime T: type, allocator: mem.Allocator, idx: us r = @unionInit(T, field.name, b); return r; }, - else => return error.TypeError, + else => unreachable, }, .number => |number| switch (Schema) { .int, .comptime_int => return @intCast(number.int), @@ -72,7 +70,7 @@ pub fn reflectT(self: *Self, comptime T: type, allocator: mem.Allocator, idx: us const int: structInfo.backing_integer.? = @intCast(number.int); return @bitCast(int); }, - else => return error.TypeError, + else => unreachable, // may only cast packed structs }, .@"union" => |unionInfo| { inline for (unionInfo.fields) |field| switch (@typeInfo(field.type)) { @@ -101,13 +99,13 @@ pub fn reflectT(self: *Self, comptime T: type, allocator: mem.Allocator, idx: us }, .array => |arrayInfo| { assert(arrayInfo.child == u8); + const strslice = string.slice(&self.language.strings); assert(arrayInfo.len == strslice.len - 1); var r: T = undefined; - for (strslice, 0..) |char, i| - r[i] = char; + for (strslice, 0..) |char, i| r[i] = char; return r; }, .pointer => |ptrInfo| switch (ptrInfo.size) { @@ -135,11 +133,44 @@ pub fn reflectT(self: *Self, comptime T: type, allocator: mem.Allocator, idx: us } return try arraylist.toOwnedSlice(); }, - else => return error.TypeError, + .many => { + assert(ptrInfo.child == u8); + const strslice = string.slice(&self.language.strings); + var arraylist: std.ArrayList(u8) = .init(allocator); + try arraylist.ensureUnusedCapacity(strslice.len); + + for (strslice) |char| + arraylist.appendAssumeCapacity(char); + + if (ptrInfo.sentinel_ptr) |some| { + return arraylist.toOwnedSliceSentinel(blk: { + const sentinel: *align(1) const ptrInfo.child = @ptrCast(some); + break :blk sentinel.*; + }); + } + return arraylist.toOwnedSlice(); + }, + else => unreachable, }, - else => return error.TypeError, + else => unreachable, }, .array => |slice| switch (Schema) { + .@"struct" => |structInfo| { + if (structInfo.is_tuple) { + assert(structInfo.fields.len == slice.len); + + var r: T = undefined; + inline for (structInfo.fields, 0..slice.len) |field, i| { + if (field.is_comptime) + @panic(@typeName(T) ++ "." ++ field.name ++ " may not be a comptime field"); + + @field(r, field.name) = try self.reflectT(field.type, allocator, slice.tip + i); + } + + return r; + } + unreachable; // may only reflect tuples + }, .array => |arrayInfo| { assert(slice.len == arrayInfo.len); var r: T = undefined; @@ -153,8 +184,10 @@ pub fn reflectT(self: *Self, comptime T: type, allocator: mem.Allocator, idx: us var r: T = try allocator.alloc(ptrInfo.child, slice.len); if (ptrInfo.sentinel_ptr) |some| { - const sentinel = @as(*align(1) const ptrInfo.child, @ptrCast(some)).*; - r[slice.len - 1] = sentinel; + r[slice.len - 1] = blk: { + const sentinel: *align(1) const ptrInfo.child = @ptrCast(some); + break :blk sentinel.*; + }; } for (0..slice.len) |i| { @@ -164,9 +197,9 @@ pub fn reflectT(self: *Self, comptime T: type, allocator: mem.Allocator, idx: us } return r; }, - else => return error.TypeError, + else => unreachable, }, - else => return error.TypeError, + else => unreachable, }, .object => |object| switch (Schema) { .@"struct" => |structInfo| { @@ -192,18 +225,14 @@ pub fn reflectT(self: *Self, comptime T: type, allocator: mem.Allocator, idx: us if (map.get(field.name)) |next_i| { @field(r, field.name) = try self.reflectT(field.type, allocator, next_i); } else switch (@typeInfo(field.type)) { - .optional => { - if (flags.bitfields) - @field(r, field.name) = null; - @panic("Unknown property: " ++ field.name); - }, + .optional => @field(r, field.name) = null, else => @panic("Unknown property: " ++ field.name), } } return r; }, - else => return error.TypeError, + else => unreachable, }, } @@ -221,7 +250,8 @@ test reflectT { \\ "flags": 0, \\ "union": ":D", \\ "enum": "world", - \\ "many": [1,2,3] + \\ "many": [1,2,3], + \\ "tuple": [1, 2] \\} ; var self = try allocator.create(Self); @@ -245,6 +275,7 @@ test reflectT { @"union": union { hi: bool, bye: f64, n128: []const u8 }, @"enum": enum { hello, world }, many: []const u8, + tuple: struct { u64, u64 }, }; const root = try self.reflectT(UserSchema, allocator, idx); @@ -252,6 +283,7 @@ test reflectT { std.debug.print("hello? {s}\n", .{@tagName(root.@"enum")}); std.debug.print("friend? {s}\n", .{root.@"union".n128}); std.debug.print("many: {any}\n", .{root.many}); + std.debug.print("tuple: {any}\n", .{root.tuple}); allocator.free(root.many); } @@ -268,19 +300,18 @@ pub const Ruleset = packed struct { allow_data_cast: bool = false, /// ignore comments allow_comments: bool = false, + /// whether to print '*T' as int + allow_derefptrs: bool = false, /// prettify the output pretty: bool = false, - pub const Pedantic = Ruleset{ - .allow_bitfields = false, - .allow_data_cast = false, - .allow_comments = false, - }; + pub const Pedantic = Ruleset{}; pub const Chill = Ruleset{ .allow_bitfields = true, .allow_data_cast = true, .allow_comments = true, + .allow_derefptrs = true, }; }; @@ -321,18 +352,16 @@ pub fn stringify(raw: anytype, comptime options: Options) ![]const u8 { if (options.ruleset.allow_data_cast) { const i: enumInfo.tag_type = @intFromEnum(raw); return std.fmt.bufPrint(&buf, "{d}", .{i}); - } else { - return std.fmt.bufPrint(&buf, "{s}", .{@tagName(raw)}); } + return std.fmt.bufPrint(&buf, "{s}", .{@tagName(raw)}); }, .@"struct" => |structInfo| switch (structInfo.layout) { .@"packed" => { if (options.ruleset.allow_bitfields) { const i: structInfo.backing_integer.? = @bitCast(raw); return std.fmt.bufPrint(&buf, "{d}", .{i}); - } else { - return error.TypeError; } + return error.TypeError; }, .auto, .@"extern" => { var string: std.ArrayListUnmanaged(u8) = .empty; @@ -380,6 +409,16 @@ pub fn stringify(raw: anytype, comptime options: Options) ![]const u8 { return string.toOwnedSlice(); }, .pointer => |ptrInfo| switch (ptrInfo.size) { + .one => { + if (options.ruleset.allow_derefptrs) { + return stringify(blk: { + const ptr: *align(ptrInfo.alignment) const ptrInfo.child = @ptrCast(raw); + break :blk ptr.*; + }, options); + } + + return std.fmt.bufPrint(&buf, "{d}", .{@intFromPtr(raw)}); + }, .slice => { if (ptrInfo.child == u8) { if (ptrInfo.is_const) @@ -422,9 +461,8 @@ pub fn stringify(raw: anytype, comptime options: Options) ![]const u8 { return error.TypeError; } unreachable; - } else { - return error.TypeError; } + unreachable; // union has no tag }, else => |t| @compileError("Error on " ++ @tagName(t)), } @@ -451,7 +489,10 @@ test stringify { }, @"enum": enum { hello, world }, many: []const u8, + epicptr: *const u8, }; + + const e: []const u8 = "epic pointer"; const user = UserSchema{ .age = 15.0, .name = "Yuzu", @@ -460,6 +501,7 @@ test stringify { .@"union" = .{ .hi = true }, .@"enum" = .world, .many = "hello", + .epicptr = @ptrCast(e[0..1]), }; const options = Options{