181 lines
6.0 KiB
Zig
181 lines
6.0 KiB
Zig
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});
|
|
}
|