const std = @import("std"); const mem = std.mem; const Language = @import("language.zig"); const Tokenizer = @import("tokenizer.zig"); const assert = std.debug.assert; const Self = @This(); pub const Error = error{TypeError}; language: *Language, tokenizer: *Tokenizer, pub fn parse(self: *Self, allocator: mem.Allocator) !usize { return self.language.parse(allocator, self.tokenizer); } pub fn init(allocator: mem.Allocator, text: []const u8) !Self { const self = Self{ .language = try allocator.create(Language), .tokenizer = try allocator.create(Tokenizer), }; self.language.* = .init; self.tokenizer.* = try .init(allocator, text); return self; } pub fn deinit(self: *Self, allocator: mem.Allocator) void { self.language.deinit(allocator); self.tokenizer.deinit(allocator); allocator.destroy(self.language); allocator.destroy(self.tokenizer); } /// always returns 0 (root) /// needs an index /// const idx: usize = try self.language.parse(allocator, self.tokenizer); pub fn reflectT(self: *Self, comptime T: type, allocator: mem.Allocator, idx: usize) !T { const Schema = @typeInfo(T); switch (self.language.index.get(idx)) { .null => { if (Schema == .null) return null; return error.TypeError; }, .bool => |b| { if (Schema == .bool) return b; return error.TypeError; }, .number => |number| switch (Schema) { .int, .comptime_int => return @intFromFloat(number), .float, .comptime_float => return @floatCast(number), .@"struct" => |structInfo| switch (structInfo.layout) { .@"packed" => return @bitCast(number), else => return error.TypeError, }, else => unreachable, }, .string => |string| switch (Schema) { .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; return r; }, .pointer => |ptrInfo| switch (ptrInfo.size) { .slice => { 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| if (char != 0x00) arraylist.appendAssumeCapacity(char); if (ptrInfo.sentinel_ptr) |some| { const sentinel = @as(*align(1) const ptrInfo.child, @ptrCast(some)).*; return try arraylist.toOwnedSliceSentinel(sentinel); } if (ptrInfo.is_const) { arraylist.deinit(); return strslice; } else { arraylist.deinit(); const slice = try allocator.dupe(u8, strslice); return @as(T, slice); } return try arraylist.toOwnedSlice(); }, else => return error.TypeError, }, else => return error.TypeError, }, .array => |slice| switch (Schema) { .array => |arrayInfo| { assert(slice.len == arrayInfo.len); var r: T = undefined; for (0..slice.len) |i| r[i] = try self.reflectT(arrayInfo.child, allocator, slice.tip + i); return r; }, .pointer => |ptrInfo| switch (ptrInfo.size) { .slice => {}, else => return error.TypeError, }, else => return error.TypeError, }, .object => |object| switch (Schema) { .@"struct" => |structInfo| { if (structInfo.is_tuple) return error.TypeError; var tip = object.tip; var map: std.StringArrayHashMapUnmanaged(usize) = .empty; try map.ensureTotalCapacity(allocator, object.len); defer map.deinit(allocator); for (0..object.len) |_| if (self.language.property_map.get(tip)) |pen| { const key = pen.tip.slice(&self.language.properties); map.putAssumeCapacity(key, tip); tip += self.language.skipSlots(tip); }; var r: T = undefined; inline for (structInfo.fields) |field| { if (field.is_comptime) @panic(@typeName(T) ++ "." ++ field.name ++ " may not be a comptime field"); 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 => @field(r, field.name) = null, else => @panic("Unknown property: " ++ field.name), } } return r; }, else => return error.TypeError, }, } unreachable; } test reflectT { const allocator = std.testing.allocator; const text = \\{ \\ "age": 15, \\ "name": "Yuzu", \\ "admin": true \\} ; var self = try allocator.create(Self); self.* = try init(allocator, text); defer allocator.destroy(self); defer self.deinit(allocator); const idx: usize = try self.parse(allocator); const UserSchema = struct { age: f64, name: []const u8, admin: bool, }; const root = try self.reflectT(UserSchema, allocator, idx); errdefer allocator.free(root.name); std.debug.print("my name is {s}\n", .{root.name}); }