Tidy up Kym API
This commit is contained in:
parent
ed585278ff
commit
8d361634d7
|
@ -21,14 +21,14 @@ pub const Manifest = struct {
|
|||
|
||||
defer env.discard(title);
|
||||
|
||||
const title_string = try env.to_string(title);
|
||||
const title_string = try env.get_string(title);
|
||||
|
||||
const width = @as(u16, get: {
|
||||
const ref = try env.get_field(manifest, "width");
|
||||
|
||||
defer env.discard(ref);
|
||||
|
||||
break: get @intFromFloat(env.to_float(ref) catch @as(f64, @floatFromInt(self.width)));
|
||||
break: get @intFromFloat(env.get_float(ref) catch @as(f64, @floatFromInt(self.width)));
|
||||
});
|
||||
|
||||
const height = @as(u16, get: {
|
||||
|
@ -36,7 +36,7 @@ pub const Manifest = struct {
|
|||
|
||||
defer env.discard(ref);
|
||||
|
||||
break: get @intFromFloat(env.to_float(ref) catch @as(f64, @floatFromInt(self.height)));
|
||||
break: get @intFromFloat(env.get_float(ref) catch @as(f64, @floatFromInt(self.height)));
|
||||
});
|
||||
|
||||
const tick_rate = @as(f32, get: {
|
||||
|
@ -44,7 +44,7 @@ pub const Manifest = struct {
|
|||
|
||||
defer env.discard(ref);
|
||||
|
||||
break: get @floatCast(env.to_float(ref) catch self.tick_rate);
|
||||
break: get @floatCast(env.get_float(ref) catch self.tick_rate);
|
||||
});
|
||||
|
||||
{
|
||||
|
|
|
@ -2,6 +2,8 @@ const Ast = @import("./kym/Ast.zig");
|
|||
|
||||
const State = @import("./kym/State.zig");
|
||||
|
||||
const Table = @import("./kym/Table.zig");
|
||||
|
||||
const coral = @import("coral");
|
||||
|
||||
const file = @import("./file.zig");
|
||||
|
@ -156,7 +158,7 @@ pub const ObjectInfo = struct {
|
|||
call: *const fn (context: CallContext) RuntimeError!*RuntimeRef = default_call,
|
||||
clean: *const fn (userdata: []u8) void = default_clean,
|
||||
get: *const fn (context: IndexContext) RuntimeError!*RuntimeRef = default_get,
|
||||
set: *const fn (context: IndexContext, Any: *const RuntimeRef) RuntimeError!void = default_set,
|
||||
set: *const fn (context: IndexContext, value: *const RuntimeRef) RuntimeError!void = default_set,
|
||||
|
||||
fn cast(object_info: *const anyopaque) *const ObjectInfo {
|
||||
return @ptrCast(@alignCast(object_info));
|
||||
|
@ -219,119 +221,6 @@ pub const RuntimeEnv = struct {
|
|||
data: []const coral.io.Byte,
|
||||
};
|
||||
|
||||
const Table = struct {
|
||||
state: *State,
|
||||
fields: FieldTable,
|
||||
array: ArrayList,
|
||||
|
||||
const ArrayList = coral.list.Stack(State.Variant);
|
||||
|
||||
const FieldTable = coral.map.Table([]const coral.io.Byte, struct {
|
||||
field: *State.Object,
|
||||
value: State.Variant,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
fn release_objects(self: Self, state: *State) void {
|
||||
state.release(self.field);
|
||||
|
||||
if (self.value == .object) {
|
||||
state.release(self.value.object);
|
||||
}
|
||||
}
|
||||
}, coral.map.string_table_traits);
|
||||
|
||||
fn free(self: *Table) void {
|
||||
{
|
||||
var field_iterator = FieldTable.Iterable{.table = &self.fields};
|
||||
|
||||
while (field_iterator.next()) |entry| {
|
||||
entry.value.release_objects(self.state);
|
||||
}
|
||||
}
|
||||
|
||||
self.fields.free();
|
||||
self.array.free();
|
||||
}
|
||||
|
||||
fn get_field(self: *Table, field_name: *State.Object) State.Variant {
|
||||
const field = self.fields.lookup(field_name.userdata) orelse return .nil;
|
||||
|
||||
if (field.value == .object) {
|
||||
return .{.object = self.state.acquire_instance(field.value.object)};
|
||||
}
|
||||
|
||||
return field.value;
|
||||
}
|
||||
|
||||
fn get_index(self: *Table, index: usize) State.Variant {
|
||||
return self.array.values[index];
|
||||
}
|
||||
|
||||
fn make(allocator: coral.io.Allocator, state: *State) Table {
|
||||
return .{
|
||||
.state = state,
|
||||
.fields = FieldTable.make(allocator),
|
||||
.array = ArrayList.make(allocator),
|
||||
};
|
||||
}
|
||||
|
||||
const object_info = ObjectInfo{
|
||||
.clean = struct {
|
||||
fn clean(userdata: []u8) void {
|
||||
@as(*Table, @ptrCast(@alignCast(userdata.ptr))).free();
|
||||
}
|
||||
}.clean,
|
||||
|
||||
.get = struct {
|
||||
fn get(context: IndexContext) RuntimeError!*RuntimeRef {
|
||||
const table = @as(*Table, @ptrCast(@alignCast(context.userdata.ptr)));
|
||||
|
||||
switch (try context.index.fetch(context.env)) {
|
||||
.nil => return context.env.raise(error.BadOperation, "cannot index a table with nil"),
|
||||
.true => return context.env.raise(error.BadOperation, "cannot index a table with true"),
|
||||
.false => return context.env.raise(error.BadOperation, "cannot index a table with false"),
|
||||
|
||||
.object => |index_object| {
|
||||
const value = table.get_field(index_object);
|
||||
|
||||
errdefer if (value == .object) {
|
||||
context.env.state.release(value.object);
|
||||
};
|
||||
|
||||
return @ptrFromInt(try context.env.bound_refs.insert(value));
|
||||
},
|
||||
|
||||
.number => |index_number| {
|
||||
const value = table.get_index(@intFromFloat(index_number));
|
||||
|
||||
errdefer if (value == .object) {
|
||||
context.env.state.release(value.object);
|
||||
};
|
||||
|
||||
return @ptrFromInt(try context.env.bound_refs.insert(value));
|
||||
},
|
||||
}
|
||||
}
|
||||
}.get,
|
||||
};
|
||||
|
||||
fn set_field(self: *Table, field_name: *State.Object, value: State.Variant) coral.io.AllocationError!void {
|
||||
const previous_entry = try self.fields.replace(field_name.userdata, .{
|
||||
.field = field_name,
|
||||
.value = value,
|
||||
});
|
||||
|
||||
if (previous_entry) |entry| {
|
||||
entry.value.release_objects(self.state);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_index(self: *Table, index: usize, value: State.Variant) coral.io.AllocationError!void {
|
||||
self.array.values[index] = value;
|
||||
}
|
||||
};
|
||||
|
||||
const VariantSlab = coral.map.Slab(State.Variant);
|
||||
|
||||
pub fn discard(self: *RuntimeEnv, ref: *RuntimeRef) void {
|
||||
|
@ -358,14 +247,13 @@ pub const RuntimeEnv = struct {
|
|||
var popped = @as(usize, 0);
|
||||
|
||||
while (popped < size) : (popped += 1) {
|
||||
switch (try self.state.pop_value()) {
|
||||
.object => |field| try table.set_field(field, try self.state.pop_value()),
|
||||
else => return self.raise(error.BadOperation, "attempt to set a non-object field"),
|
||||
}
|
||||
try table.set_field(
|
||||
try to_object(self, try self.state.pop_value()),
|
||||
try self.state.pop_value());
|
||||
}
|
||||
}
|
||||
|
||||
const table_object = try self.state.acquire_new(coral.io.bytes_of(&table), &Table.object_info);
|
||||
const table_object = try self.state.acquire_new(coral.io.bytes_of(&table), &table_info);
|
||||
|
||||
errdefer self.state.release(table_object);
|
||||
|
||||
|
@ -382,58 +270,79 @@ pub const RuntimeEnv = struct {
|
|||
|
||||
.not => {
|
||||
try self.state.push_value(switch (try self.state.pop_value()) {
|
||||
.nil => return self.raise(error.BadOperation, "cannot not nil"),
|
||||
.nil => return self.raise(error.BadOperation, "cannot convert nil to true or false"),
|
||||
.false => .true,
|
||||
.true => .false,
|
||||
.number => return self.raise(error.BadOperation, "cannot not a number"),
|
||||
.object => return self.raise(error.BadOperation, "cannot not an object"),
|
||||
.number => return self.raise(error.BadOperation, "cannot convert a number to true or false"),
|
||||
.object => return self.raise(error.BadOperation, "cannot convert an object to true or false"),
|
||||
});
|
||||
},
|
||||
|
||||
.neg => {
|
||||
try self.state.push_value(switch (try self.state.pop_value()) {
|
||||
.nil => return self.raise(error.BadOperation, "cannot not nil"),
|
||||
.false => return self.raise(error.BadOperation, "cannot not false"),
|
||||
.true => return self.raise(error.BadOperation, "cannot not true"),
|
||||
.number => |number| .{.number = -number},
|
||||
.object => return self.raise(error.BadOperation, "cannot not an object"),
|
||||
});
|
||||
try self.state.push_value(.{.number = -(try to_number(self, try self.state.pop_value()))});
|
||||
},
|
||||
|
||||
.add => {
|
||||
const lhs_number = try to_number(self, try self.state.pop_value());
|
||||
const rhs_number = try to_number(self, try self.state.pop_value());
|
||||
|
||||
try self.state.push_value(.{.number = lhs_number + rhs_number});
|
||||
},
|
||||
|
||||
.sub => {
|
||||
const lhs_number = try to_number(self, try self.state.pop_value());
|
||||
const rhs_number = try to_number(self, try self.state.pop_value());
|
||||
|
||||
try self.state.push_value(.{.number = lhs_number - rhs_number});
|
||||
},
|
||||
|
||||
.mul => {
|
||||
const lhs_number = try to_number(self, try self.state.pop_value());
|
||||
const rhs_number = try to_number(self, try self.state.pop_value());
|
||||
|
||||
try self.state.push_value(.{.number = lhs_number * rhs_number});
|
||||
},
|
||||
|
||||
.div => {
|
||||
const lhs_number = try to_number(self, try self.state.pop_value());
|
||||
const rhs_number = try to_number(self, try self.state.pop_value());
|
||||
|
||||
try self.state.push_value(.{.number = lhs_number / rhs_number});
|
||||
},
|
||||
|
||||
.eql => {
|
||||
const lhs = try self.state.pop_value();
|
||||
const rhs = try self.state.pop_value();
|
||||
|
||||
try self.state.push_value(if (lhs.equals(rhs)) .true else .false);
|
||||
},
|
||||
|
||||
.cgt => {
|
||||
const lhs_number = try to_number(self, try self.state.pop_value());
|
||||
const rhs_number = try to_number(self, try self.state.pop_value());
|
||||
|
||||
try self.state.push_value(if (lhs_number > rhs_number) .true else .false);
|
||||
},
|
||||
|
||||
.clt => {
|
||||
const lhs_number = try to_number(self, try self.state.pop_value());
|
||||
const rhs_number = try to_number(self, try self.state.pop_value());
|
||||
|
||||
try self.state.push_value(if (lhs_number < rhs_number) .true else .false);
|
||||
},
|
||||
|
||||
.cge => {
|
||||
const lhs_number = try to_number(self, try self.state.pop_value());
|
||||
const rhs_number = try to_number(self, try self.state.pop_value());
|
||||
|
||||
try self.state.push_value(if (lhs_number >= rhs_number) .true else .false);
|
||||
},
|
||||
|
||||
.cle => {
|
||||
const lhs_number = try to_number(self, try self.state.pop_value());
|
||||
const rhs_number = try to_number(self, try self.state.pop_value());
|
||||
|
||||
try self.state.push_value(if (lhs_number <= rhs_number) .true else .false);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -495,7 +404,7 @@ pub const RuntimeEnv = struct {
|
|||
|
||||
defer self.discard(interned_field);
|
||||
|
||||
const indexable_object = try indexable.fetch_object(self);
|
||||
const indexable_object = try to_object(self, try indexable.fetch(self));
|
||||
|
||||
return ObjectInfo.cast(indexable_object.userinfo).get(.{
|
||||
.env = self,
|
||||
|
@ -505,6 +414,20 @@ pub const RuntimeEnv = struct {
|
|||
});
|
||||
}
|
||||
|
||||
pub fn get_float(self: *RuntimeEnv, ref: *const RuntimeRef) RuntimeError!State.Float {
|
||||
return to_number(self, try ref.fetch(self));
|
||||
}
|
||||
|
||||
pub fn get_string(self: *RuntimeEnv, ref: *const RuntimeRef) RuntimeError![]const u8 {
|
||||
const object = try to_object(self, try ref.fetch(self));
|
||||
|
||||
if (ObjectInfo.cast(object.userinfo) != &string_info) {
|
||||
return self.raise(error.BadOperation, "object is not a string");
|
||||
}
|
||||
|
||||
return object.userdata;
|
||||
}
|
||||
|
||||
pub fn intern(self: *RuntimeEnv, data: []const u8) RuntimeError!*RuntimeRef {
|
||||
const data_object = try self.state.acquire_interned(data, &string_info);
|
||||
|
||||
|
@ -532,10 +455,6 @@ pub const RuntimeEnv = struct {
|
|||
return @ptrFromInt(try self.bound_refs.insert(.{.object = data_object}));
|
||||
}
|
||||
|
||||
pub fn nil(self: *RuntimeEnv) RuntimeError!*RuntimeRef {
|
||||
return @ptrFromInt(try self.bound_refs.insert(.nil));
|
||||
}
|
||||
|
||||
pub fn raise(self: *RuntimeEnv, runtime_error: RuntimeError, error_message: []const u8) RuntimeError {
|
||||
if (self.err_writer.invoke(error_message) == null) {
|
||||
return error.SystemFailure;
|
||||
|
@ -543,23 +462,6 @@ pub const RuntimeEnv = struct {
|
|||
|
||||
return runtime_error;
|
||||
}
|
||||
|
||||
pub fn to_float(self: *RuntimeEnv, ref: *const RuntimeRef) RuntimeError!State.Float {
|
||||
return ref.fetch_number(self);
|
||||
}
|
||||
|
||||
pub fn to_string(self: *RuntimeEnv, ref: *const RuntimeRef) RuntimeError![]const u8 {
|
||||
const object = try switch (try ref.fetch(self)) {
|
||||
.object => |object| object,
|
||||
else => self.raise(error.BadOperation, "cannot convert to object"),
|
||||
};
|
||||
|
||||
if (ObjectInfo.cast(object.userinfo) != &string_info) {
|
||||
return self.raise(error.BadOperation, "object is not a string");
|
||||
}
|
||||
|
||||
return object.userdata;
|
||||
}
|
||||
};
|
||||
|
||||
pub const RuntimeError = coral.io.AllocationError || State.PopError || error {
|
||||
|
@ -572,9 +474,84 @@ pub const RuntimeRef = opaque {
|
|||
fn fetch(self: *const RuntimeRef, env: *RuntimeEnv) RuntimeError!State.Variant {
|
||||
return env.bound_refs.lookup(@intFromPtr(self)) orelse env.raise(error.BadOperation, "stale ref");
|
||||
}
|
||||
};
|
||||
|
||||
fn fetch_number(self: *const RuntimeRef, env: *RuntimeEnv) RuntimeError!State.Float {
|
||||
return switch (try self.fetch(env)) {
|
||||
fn table_clean(userdata: []u8) void {
|
||||
@as(*Table, @ptrCast(@alignCast(userdata.ptr))).free();
|
||||
}
|
||||
|
||||
fn table_get(context: IndexContext) RuntimeError!*RuntimeRef {
|
||||
const table = @as(*Table, @ptrCast(@alignCast(context.userdata.ptr)));
|
||||
|
||||
switch (try context.index.fetch(context.env)) {
|
||||
.nil => return context.env.raise(error.BadOperation, "cannot index a table with nil"),
|
||||
.true => return context.env.raise(error.BadOperation, "cannot index a table with true"),
|
||||
.false => return context.env.raise(error.BadOperation, "cannot index a table with false"),
|
||||
|
||||
.object => |index_object| {
|
||||
const value = table.get_field(index_object);
|
||||
|
||||
errdefer if (value == .object) {
|
||||
context.env.state.release(value.object);
|
||||
};
|
||||
|
||||
return @ptrFromInt(try context.env.bound_refs.insert(value));
|
||||
},
|
||||
|
||||
.number => |index_number| {
|
||||
const value = table.get_index(@intFromFloat(index_number));
|
||||
|
||||
errdefer if (value == .object) {
|
||||
context.env.state.release(value.object);
|
||||
};
|
||||
|
||||
return @ptrFromInt(try context.env.bound_refs.insert(value));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const table_info = ObjectInfo{
|
||||
.clean = table_clean,
|
||||
.get = table_get,
|
||||
.set = table_set,
|
||||
};
|
||||
|
||||
fn table_set(context: IndexContext, value: *const RuntimeRef) RuntimeError!void {
|
||||
const table = @as(*Table, @ptrCast(@alignCast(context.userdata.ptr)));
|
||||
|
||||
switch (try context.index.fetch(context.env)) {
|
||||
.nil => return context.env.raise(error.BadOperation, "cannot index a table with nil"),
|
||||
.true => return context.env.raise(error.BadOperation, "cannot index a table with true"),
|
||||
.false => return context.env.raise(error.BadOperation, "cannot index a table with false"),
|
||||
|
||||
.object => |index_object| {
|
||||
const fetched_value = try value.fetch(context.env);
|
||||
|
||||
if (fetched_value == .object) {
|
||||
try table.set_field(index_object, .{
|
||||
.object = context.env.state.acquire_instance(fetched_value.object),
|
||||
});
|
||||
} else {
|
||||
try table.set_field(index_object, fetched_value);
|
||||
}
|
||||
},
|
||||
|
||||
.number => |index_number| {
|
||||
const fetched_value = try value.fetch(context.env);
|
||||
|
||||
if (fetched_value == .object) {
|
||||
try table.set_index(@intFromFloat(index_number), .{
|
||||
.object = context.env.state.acquire_instance(fetched_value.object),
|
||||
});
|
||||
} else {
|
||||
try table.set_index(@intFromFloat(index_number), fetched_value);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn to_number(env: *RuntimeEnv, variant: State.Variant) RuntimeError!State.Float {
|
||||
return switch (variant) {
|
||||
.nil => env.raise(error.BadOperation, "cannot convert nil to number"),
|
||||
.true => env.raise(error.BadOperation, "cannot convert true to number"),
|
||||
.false => env.raise(error.BadOperation, "cannot convert false to number"),
|
||||
|
@ -583,8 +560,8 @@ pub const RuntimeRef = opaque {
|
|||
};
|
||||
}
|
||||
|
||||
fn fetch_object(self: *const RuntimeRef, env: *RuntimeEnv) RuntimeError!*State.Object {
|
||||
return switch (try self.fetch(env)) {
|
||||
fn to_object(env: *RuntimeEnv, variant: State.Variant) RuntimeError!*State.Object {
|
||||
return switch (variant) {
|
||||
.nil => env.raise(error.BadOperation, "cannot convert nil to object"),
|
||||
.true => env.raise(error.BadOperation, "cannot convert true to object"),
|
||||
.false => env.raise(error.BadOperation, "cannot convert false to object"),
|
||||
|
@ -592,7 +569,6 @@ pub const RuntimeRef = opaque {
|
|||
.object => |object| object,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const string_info = ObjectInfo{
|
||||
|
||||
|
|
|
@ -36,6 +36,24 @@ pub const Variant = union (enum) {
|
|||
false,
|
||||
number: Float,
|
||||
object: *Object,
|
||||
|
||||
pub fn equals(self: Variant, other: Variant) bool {
|
||||
return switch (self) {
|
||||
.nil => other == .nil,
|
||||
.true => other == .true,
|
||||
.false => other == .false,
|
||||
|
||||
.number => |number| switch (other) {
|
||||
.number => |other_number| number == other_number,
|
||||
else => false,
|
||||
},
|
||||
|
||||
.object => |object| switch (other) {
|
||||
.object => |other_object| object == other_object,
|
||||
else => false,
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub fn acquire_instance(_: *Self, object: *Object) *Object {
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
const State = @import("./State.zig");
|
||||
|
||||
const coral = @import("coral");
|
||||
|
||||
state: *State,
|
||||
fields: FieldTable,
|
||||
array: ArrayList,
|
||||
|
||||
const ArrayList = coral.list.Stack(State.Variant);
|
||||
|
||||
const Field = struct {
|
||||
field: *State.Object,
|
||||
value: State.Variant,
|
||||
|
||||
fn release_objects(self: Field, state: *State) void {
|
||||
state.release(self.field);
|
||||
|
||||
if (self.value == .object) {
|
||||
state.release(self.value.object);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const FieldTable = coral.map.Table([]const coral.io.Byte, Field, coral.map.string_table_traits);
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn free(self: *Self) void {
|
||||
{
|
||||
var field_iterator = FieldTable.Iterable{.table = &self.fields};
|
||||
|
||||
while (field_iterator.next()) |entry| {
|
||||
entry.value.release_objects(self.state);
|
||||
}
|
||||
}
|
||||
|
||||
self.fields.free();
|
||||
self.array.free();
|
||||
}
|
||||
|
||||
pub fn get_field(self: *Self, field_name: *State.Object) State.Variant {
|
||||
const field = self.fields.lookup(field_name.userdata) orelse return .nil;
|
||||
|
||||
if (field.value == .object) {
|
||||
return .{.object = self.state.acquire_instance(field.value.object)};
|
||||
}
|
||||
|
||||
return field.value;
|
||||
}
|
||||
|
||||
pub fn get_index(self: *Self, index: usize) State.Variant {
|
||||
return self.array.values[index];
|
||||
}
|
||||
|
||||
pub fn make(allocator: coral.io.Allocator, state: *State) Self {
|
||||
return .{
|
||||
.state = state,
|
||||
.fields = FieldTable.make(allocator),
|
||||
.array = ArrayList.make(allocator),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn set_field(self: *Self, field_name: *State.Object, value: State.Variant) coral.io.AllocationError!void {
|
||||
const previous_entry = try self.fields.replace(field_name.userdata, .{
|
||||
.field = field_name,
|
||||
.value = value,
|
||||
});
|
||||
|
||||
if (previous_entry) |entry| {
|
||||
entry.value.release_objects(self.state);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_index(self: *Self, index: usize, value: State.Variant) coral.io.AllocationError!void {
|
||||
self.array.values[index] = value;
|
||||
}
|
Loading…
Reference in New Issue