ona/source/ona/kym/Table.zig
kayomn 8137f2b474
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
Fix memory leaks
2023-07-22 12:36:47 +01:00

92 lines
2.4 KiB
Zig

const coral = @import("coral");
const kym = @import("../kym.zig");
fields: FieldTable,
const FieldTable = coral.map.StringTable(struct {
key_ref: ?*kym.RuntimeRef,
value_ref: ?*kym.RuntimeRef,
});
const Self = @This();
pub fn new(env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeRef {
var self = Self{
.fields = FieldTable.make(env.allocator),
};
errdefer {
self.fields.free();
}
return try env.new_dynamic(coral.io.bytes_of(&self), &typeinfo);
}
pub const typeinfo = kym.Typeinfo{
.name = "table",
.clean = typeinfo_clean,
.get = typeinfo_get,
.set = typeinfo_set,
};
fn typeinfo_clean(env: *kym.RuntimeEnv, userdata: []coral.io.Byte) void {
const table = @as(*Self, @ptrCast(@alignCast(userdata.ptr)));
{
var field_iterable = table.fields.as_iterable();
while (field_iterable.next()) |entry| {
env.discard(entry.value.key_ref);
env.discard(entry.value.value_ref);
}
}
table.fields.free();
}
fn typeinfo_get(context: kym.IndexContext) kym.RuntimeError!?*kym.RuntimeRef {
const table = @as(*Self, @ptrCast(@alignCast(context.userdata.ptr)));
return switch (context.env.unbox(context.index_ref)) {
.string => |string| context.env.acquire((table.fields.lookup(string) orelse return null).value_ref),
// TODO: Implement number indices in tables.
.number => |_| unreachable,
else => context.env.raise(error.TypeMismatch, "table objects may only be indexed with strings or numbers"),
};
}
fn typeinfo_set(context: kym.IndexContext, value_ref: ?*const kym.RuntimeRef) kym.RuntimeError!void {
const table = @as(*Self, @ptrCast(@alignCast(context.userdata.ptr)));
const acquired_value_ref = context.env.acquire(value_ref);
errdefer context.env.discard(acquired_value_ref);
switch (context.env.unbox(context.index_ref)) {
.string => |string| {
const acquired_index_ref = context.env.acquire(context.index_ref);
errdefer context.env.discard(acquired_index_ref);
var displaced_table_entry = if (acquired_value_ref) |ref| try table.fields.replace(string, .{
.key_ref = acquired_index_ref,
.value_ref = ref,
}) else table.fields.remove(string);
if (displaced_table_entry) |*entry| {
context.env.discard(entry.value.key_ref);
context.env.discard(entry.value.value_ref);
}
},
.number => |_| {
// TODO: Implement number indices in tables.
unreachable;
},
else => {
return context.env.raise(error.TypeMismatch, "table objects may only be indexed with strings or numbers");
},
}
}