Refactor Kym runtime API
This commit is contained in:
parent
1ea115b277
commit
7b4b827dc2
|
@ -17,25 +17,17 @@ pub const Manifest = struct {
|
||||||
|
|
||||||
defer env.discard(manifest_ref);
|
defer env.discard(manifest_ref);
|
||||||
|
|
||||||
const title_ref = try kym.get_field(env, manifest_ref, "title");
|
|
||||||
|
|
||||||
defer env.discard(title_ref);
|
|
||||||
|
|
||||||
const title_string = switch (env.unbox(title_ref)) {
|
|
||||||
.string => |string| string,
|
|
||||||
else => "",
|
|
||||||
};
|
|
||||||
|
|
||||||
const width = @as(u16, get: {
|
const width = @as(u16, get: {
|
||||||
const ref = try kym.get_field(env, manifest_ref, "width");
|
const ref = try kym.get_field(env, manifest_ref, "width");
|
||||||
|
|
||||||
defer env.discard(ref);
|
defer env.discard(ref);
|
||||||
|
|
||||||
|
if (env.unbox(ref).expect_number()) |number| {
|
||||||
// TODO: Add safety-checks to int cast.
|
// TODO: Add safety-checks to int cast.
|
||||||
break: get switch (env.unbox(ref)) {
|
break: get @intFromFloat(number);
|
||||||
.number => |number| @intFromFloat(number),
|
}
|
||||||
else => self.width,
|
|
||||||
};
|
break: get self.width;
|
||||||
});
|
});
|
||||||
|
|
||||||
const height = @as(u16, get: {
|
const height = @as(u16, get: {
|
||||||
|
@ -43,11 +35,12 @@ pub const Manifest = struct {
|
||||||
|
|
||||||
defer env.discard(ref);
|
defer env.discard(ref);
|
||||||
|
|
||||||
|
if (env.unbox(ref).expect_number()) |number| {
|
||||||
// TODO: Add safety-checks to int cast.
|
// TODO: Add safety-checks to int cast.
|
||||||
break: get switch (env.unbox(ref)) {
|
break: get @intFromFloat(number);
|
||||||
.number => |number| @intFromFloat(number),
|
}
|
||||||
else => self.height,
|
|
||||||
};
|
break: get self.height;
|
||||||
});
|
});
|
||||||
|
|
||||||
const tick_rate = @as(f32, get: {
|
const tick_rate = @as(f32, get: {
|
||||||
|
@ -55,13 +48,20 @@ pub const Manifest = struct {
|
||||||
|
|
||||||
defer env.discard(ref);
|
defer env.discard(ref);
|
||||||
|
|
||||||
break: get switch (env.unbox(ref)) {
|
if (env.unbox(ref).expect_number()) |number| {
|
||||||
.number => |number| @floatCast(number),
|
// TODO: Add safety-checks to int cast.
|
||||||
else => self.tick_rate,
|
break: get @floatCast(number);
|
||||||
};
|
}
|
||||||
|
|
||||||
|
break: get self.tick_rate;
|
||||||
});
|
});
|
||||||
|
|
||||||
{
|
{
|
||||||
|
const title_ref = try kym.get_field(env, manifest_ref, "title");
|
||||||
|
|
||||||
|
defer env.discard(title_ref);
|
||||||
|
|
||||||
|
const title_string = env.unbox(title_ref).expect_string() orelse "";
|
||||||
const limited_title_len = coral.math.min(title_string.len, self.title.len);
|
const limited_title_len = coral.math.min(title_string.len, self.title.len);
|
||||||
|
|
||||||
coral.io.copy(&self.title, title_string[0 .. limited_title_len]);
|
coral.io.copy(&self.title, title_string[0 .. limited_title_len]);
|
||||||
|
|
|
@ -156,15 +156,15 @@ pub const allocator = coral.io.Allocator.bind(Context, &context, .{
|
||||||
pub fn trace_leaks() void {
|
pub fn trace_leaks() void {
|
||||||
switch (builtin.mode) {
|
switch (builtin.mode) {
|
||||||
.Debug, .ReleaseSafe => {
|
.Debug, .ReleaseSafe => {
|
||||||
var current_allocation_info = context.allocation_info_head;
|
var current_node = context.head;
|
||||||
|
|
||||||
while (current_allocation_info) |allocation_info| : (current_allocation_info = allocation_info.next_info) {
|
while (current_node) |node| : (current_node = node.next) {
|
||||||
std.debug.print("{d} byte leak at 0x{x} detected:\n", .{
|
std.debug.print("{d} byte leak at 0x{x} detected:\n", .{
|
||||||
allocation_info.size,
|
node.size,
|
||||||
@as(usize, allocation_info) + @sizeOf(AllocationNode),
|
@intFromPtr(node) + @sizeOf(AllocationNode),
|
||||||
});
|
});
|
||||||
|
|
||||||
allocation_info.trace.dump();
|
node.trace.dump();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -10,17 +10,48 @@ const file = @import("./file.zig");
|
||||||
|
|
||||||
const tokens = @import("./kym/tokens.zig");
|
const tokens = @import("./kym/tokens.zig");
|
||||||
|
|
||||||
pub const Args = []const ?*const RuntimeRef;
|
pub const Any = union (enum) {
|
||||||
|
nil,
|
||||||
|
boolean: bool,
|
||||||
|
number: Float,
|
||||||
|
string: []const coral.io.Byte,
|
||||||
|
dynamic: *DynamicObject,
|
||||||
|
|
||||||
pub const CallContext = struct {
|
pub fn expect_dynamic(self: Any) ?*DynamicObject {
|
||||||
env: *RuntimeEnv,
|
return switch (self) {
|
||||||
caller: ?*const RuntimeRef,
|
.dynamic => |dynamic| dynamic,
|
||||||
userdata: []coral.io.Byte,
|
else => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expect_number(self: Any) ?Float {
|
||||||
|
return switch (self) {
|
||||||
|
.number => |number| number,
|
||||||
|
else => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expect_string(self: Any) ?[]const coral.io.Byte {
|
||||||
|
return switch (self) {
|
||||||
|
.string => |string| string,
|
||||||
|
else => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const Caller = coral.io.Generator(RuntimeError!?*RuntimeRef, *RuntimeEnv);
|
||||||
|
|
||||||
pub const DynamicObject = struct {
|
pub const DynamicObject = struct {
|
||||||
userdata: []coral.io.Byte,
|
userdata: []coral.io.Byte,
|
||||||
typeinfo: *const Typeinfo,
|
typeinfo: *const Typeinfo,
|
||||||
|
|
||||||
|
pub fn as_caller(self: *DynamicObject) Caller {
|
||||||
|
return Caller.bind(DynamicObject, self, DynamicObject.call);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(self: *DynamicObject, env: *RuntimeEnv) RuntimeError!?*RuntimeRef {
|
||||||
|
return self.typeinfo.call(env);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const ErrorHandler = coral.io.Generator(void, ErrorInfo);
|
pub const ErrorHandler = coral.io.Generator(void, ErrorInfo);
|
||||||
|
@ -54,7 +85,7 @@ pub const RuntimeEnv = struct {
|
||||||
|
|
||||||
const FrameStack = coral.list.Stack(Frame);
|
const FrameStack = coral.list.Stack(Frame);
|
||||||
|
|
||||||
const SyscallerTable = coral.map.StringTable(Syscaller);
|
const SyscallerTable = coral.map.StringTable(Caller);
|
||||||
|
|
||||||
const RefStack = coral.list.Stack(?*RuntimeRef);
|
const RefStack = coral.list.Stack(?*RuntimeRef);
|
||||||
|
|
||||||
|
@ -72,7 +103,7 @@ pub const RuntimeEnv = struct {
|
||||||
|
|
||||||
pub const Syscall = struct {
|
pub const Syscall = struct {
|
||||||
name: []const coral.io.Byte,
|
name: []const coral.io.Byte,
|
||||||
caller: Syscaller,
|
caller: Caller,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn acquire(self: *RuntimeEnv, ref: ?*const RuntimeRef) ?*RuntimeRef {
|
pub fn acquire(self: *RuntimeEnv, ref: ?*const RuntimeRef) ?*RuntimeRef {
|
||||||
|
@ -94,6 +125,30 @@ pub const RuntimeEnv = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn call(
|
||||||
|
self: *RuntimeEnv,
|
||||||
|
name: []const coral.io.Byte,
|
||||||
|
arg_count: u8,
|
||||||
|
caller: Caller,
|
||||||
|
) RuntimeError!?*RuntimeRef {
|
||||||
|
try self.frames.push_one(.{
|
||||||
|
.name = name,
|
||||||
|
.arg_count = arg_count,
|
||||||
|
.locals_top = self.local_refs.values.len,
|
||||||
|
});
|
||||||
|
|
||||||
|
defer {
|
||||||
|
const frame = self.frames.pop();
|
||||||
|
|
||||||
|
coral.debug.assert(frame != null);
|
||||||
|
|
||||||
|
coral.debug.assert(self.local_refs.drop(
|
||||||
|
(self.local_refs.values.len - frame.?.locals_top) + frame.?.arg_count));
|
||||||
|
}
|
||||||
|
|
||||||
|
return caller.invoke(self);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn discard(self: *RuntimeEnv, ref: ?*RuntimeRef) void {
|
pub fn discard(self: *RuntimeEnv, ref: ?*RuntimeRef) void {
|
||||||
const key = @intFromPtr(ref orelse return);
|
const key = @intFromPtr(ref orelse return);
|
||||||
var ref_data = self.ref_values.remove(key) orelse unreachable;
|
var ref_data = self.ref_values.remove(key) orelse unreachable;
|
||||||
|
@ -143,28 +198,13 @@ pub const RuntimeEnv = struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var exe = Chunk.make(self);
|
var chunk = Chunk.make(self);
|
||||||
|
|
||||||
defer exe.free();
|
defer chunk.free();
|
||||||
|
|
||||||
try exe.compile_ast(ast);
|
try chunk.compile_ast(ast);
|
||||||
|
|
||||||
return try exe.execute(name);
|
return self.call(name, 0, chunk.as_caller());
|
||||||
}
|
|
||||||
|
|
||||||
pub fn frame_pop(self: *RuntimeEnv) void {
|
|
||||||
const frame = self.frames.pop();
|
|
||||||
|
|
||||||
coral.debug.assert(frame != null);
|
|
||||||
coral.debug.assert(self.local_refs.drop((self.local_refs.values.len - frame.?.locals_top) + frame.?.arg_count));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn frame_push(self: *RuntimeEnv, frame_name: []const coral.io.Byte, arg_count: u8) RuntimeError!void {
|
|
||||||
try self.frames.push_one(.{
|
|
||||||
.name = frame_name,
|
|
||||||
.arg_count = arg_count,
|
|
||||||
.locals_top = self.local_refs.values.len,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn free(self: *RuntimeEnv) void {
|
pub fn free(self: *RuntimeEnv) void {
|
||||||
|
@ -172,41 +212,35 @@ pub const RuntimeEnv = struct {
|
||||||
self.ref_values.free();
|
self.ref_values.free();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_arg(self: *RuntimeEnv, index: usize) RuntimeError!?*const RuntimeRef {
|
pub fn get_local(self: *RuntimeEnv, local: u8) RuntimeError!?*RuntimeRef {
|
||||||
const frame = self.frames.peek() orelse return self.raise(error.IllegalState, "stack underflow");
|
if (local >= self.local_refs.values.len) {
|
||||||
|
return self.raise(error.IllegalState, "out of bounds local get");
|
||||||
if (index >= frame.arg_count) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.local_refs.values[frame.locals_top - (1 + index)];
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn local_get(self: *RuntimeEnv, local: u8) ?*RuntimeRef {
|
|
||||||
return self.local_refs.values[local];
|
return self.local_refs.values[local];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn local_pop(self: *RuntimeEnv) ?*RuntimeRef {
|
pub fn pop_local(self: *RuntimeEnv) RuntimeError!?*RuntimeRef {
|
||||||
const ref = self.local_refs.pop();
|
return self.local_refs.pop() orelse self.raise(error.IllegalState, "stack underflow");
|
||||||
|
|
||||||
coral.debug.assert(ref != null);
|
|
||||||
|
|
||||||
return ref.?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn local_push_ref(self: *RuntimeEnv, ref: ?*RuntimeRef) RuntimeError!void {
|
pub fn push_boolean(self: *RuntimeEnv, boolean: bool) RuntimeError!void {
|
||||||
return self.local_refs.push_one(self.acquire(ref));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn local_push_boolean(self: *RuntimeEnv, boolean: bool) RuntimeError!void {
|
|
||||||
return self.local_refs.push_one(try self.new_boolean(boolean));
|
return self.local_refs.push_one(try self.new_boolean(boolean));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn local_push_number(self: *RuntimeEnv, number: Float) RuntimeError!void {
|
pub fn push_number(self: *RuntimeEnv, number: Float) RuntimeError!void {
|
||||||
return self.local_refs.push_one(try self.new_number(number));
|
return self.local_refs.push_one(try self.new_number(number));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn local_set(self: *RuntimeEnv, local: u8, value: ?*RuntimeRef) void {
|
pub fn push_ref(self: *RuntimeEnv, ref: ?*RuntimeRef) RuntimeError!void {
|
||||||
|
return self.local_refs.push_one(self.acquire(ref));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_local(self: *RuntimeEnv, local: u8, value: ?*RuntimeRef) RuntimeError!void {
|
||||||
|
if (local >= self.local_refs.values.len) {
|
||||||
|
return self.raise(error.IllegalState, "out of bounds local set");
|
||||||
|
}
|
||||||
|
|
||||||
self.local_refs.values[local] = self.acquire(value);
|
self.local_refs.values[local] = self.acquire(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,12 +311,13 @@ pub const RuntimeEnv = struct {
|
||||||
return error_value;
|
return error_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn syscaller(self: *RuntimeEnv, name: []const coral.io.Byte) RuntimeError!Syscaller {
|
pub fn syscaller(self: *RuntimeEnv, name: []const coral.io.Byte) RuntimeError!Caller {
|
||||||
return self.syscallers.lookup(name) orelse self.raise(error.BadOperation, "attempt to call undefined syscall");
|
return self.syscallers.lookup(name) orelse self.raise(error.BadOperation, "attempt to call undefined syscall");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unbox(self: *RuntimeEnv, ref: ?*const RuntimeRef) Unboxed {
|
pub fn unbox(self: *RuntimeEnv, ref: ?*const RuntimeRef) Any {
|
||||||
const ref_data = self.ref_values.lookup(@intFromPtr(ref orelse return .nil));
|
if (ref) |live_ref| {
|
||||||
|
const ref_data = self.ref_values.lookup(@intFromPtr(live_ref));
|
||||||
|
|
||||||
coral.debug.assert(ref_data != null);
|
coral.debug.assert(ref_data != null);
|
||||||
|
|
||||||
|
@ -295,32 +330,17 @@ pub const RuntimeEnv = struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unbox_dynamic(self: *RuntimeEnv, ref: ?*const RuntimeRef) RuntimeError![]const coral.io.Byte {
|
return .nil;
|
||||||
if (ref) |live_ref| {
|
|
||||||
const ref_data = self.ref_values.lookup(@intFromPtr(live_ref));
|
|
||||||
|
|
||||||
coral.debug.assert(ref_data != null);
|
|
||||||
|
|
||||||
if (ref_data.?.object == .dynamic) {
|
|
||||||
return ref_data.?.object.dynamic;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.raise(error.TypeMismatch, "expected dynamic");
|
pub fn view_arg(self: *RuntimeEnv, index: usize) RuntimeError!?*const RuntimeRef {
|
||||||
|
const frame = self.frames.peek() orelse return self.raise(error.IllegalState, "stack underflow");
|
||||||
|
|
||||||
|
if (index >= frame.arg_count) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unbox_string(self: *RuntimeEnv, ref: ?*const RuntimeRef) RuntimeError![]const coral.io.Byte {
|
return self.local_refs.values[frame.locals_top - (1 + index)];
|
||||||
if (ref) |live_ref| {
|
|
||||||
const ref_data = self.ref_values.lookup(@intFromPtr(live_ref));
|
|
||||||
|
|
||||||
coral.debug.assert(ref_data != null);
|
|
||||||
|
|
||||||
if (ref_data.?.object == .string) {
|
|
||||||
return ref_data.?.object.string;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.raise(error.TypeMismatch, "expected string");
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -329,13 +349,10 @@ pub const RuntimeError = coral.io.AllocationError || error {
|
||||||
TypeMismatch,
|
TypeMismatch,
|
||||||
BadOperation,
|
BadOperation,
|
||||||
BadSyntax,
|
BadSyntax,
|
||||||
Assertion,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const RuntimeRef = opaque {};
|
pub const RuntimeRef = opaque {};
|
||||||
|
|
||||||
pub const Syscaller = coral.io.Generator(RuntimeError!?*RuntimeRef, *RuntimeEnv);
|
|
||||||
|
|
||||||
pub const TestContext = struct {
|
pub const TestContext = struct {
|
||||||
env: *RuntimeEnv,
|
env: *RuntimeEnv,
|
||||||
userdata: []const coral.io.Byte,
|
userdata: []const coral.io.Byte,
|
||||||
|
@ -344,15 +361,15 @@ pub const TestContext = struct {
|
||||||
|
|
||||||
pub const Typeinfo = struct {
|
pub const Typeinfo = struct {
|
||||||
name: []const coral.io.Byte,
|
name: []const coral.io.Byte,
|
||||||
call: *const fn (context: CallContext) RuntimeError!?*RuntimeRef = default_call,
|
call: *const fn (env: *RuntimeEnv) RuntimeError!?*RuntimeRef = default_call,
|
||||||
clean: *const fn (userdata: []coral.io.Byte) void = default_clean,
|
clean: *const fn (userdata: []coral.io.Byte) void = default_clean,
|
||||||
get: *const fn (context: IndexContext) RuntimeError!?*RuntimeRef = default_get,
|
get: *const fn (context: IndexContext) RuntimeError!?*RuntimeRef = default_get,
|
||||||
set: *const fn (context: IndexContext, value: ?*const RuntimeRef) RuntimeError!void = default_set,
|
set: *const fn (context: IndexContext, value: ?*const RuntimeRef) RuntimeError!void = default_set,
|
||||||
test_difference: *const fn (context: TestContext) RuntimeError!Float = default_test_difference,
|
test_difference: *const fn (context: TestContext) RuntimeError!Float = default_test_difference,
|
||||||
test_equality: *const fn (context: TestContext) RuntimeError!bool = default_test_equality,
|
test_equality: *const fn (context: TestContext) RuntimeError!bool = default_test_equality,
|
||||||
|
|
||||||
fn default_call(context: CallContext) RuntimeError!?*RuntimeRef {
|
fn default_call(env: *RuntimeEnv) RuntimeError!?*RuntimeRef {
|
||||||
return context.env.raise(error.TypeMismatch, "object is not callable");
|
return env.raise(error.TypeMismatch, "object is not callable");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_clean(_: []coral.io.Byte) void {
|
fn default_clean(_: []coral.io.Byte) void {
|
||||||
|
@ -379,32 +396,11 @@ pub const Typeinfo = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Unboxed = union (enum) {
|
|
||||||
nil,
|
|
||||||
boolean: bool,
|
|
||||||
number: Float,
|
|
||||||
string: []const coral.io.Byte,
|
|
||||||
dynamic: *const DynamicObject,
|
|
||||||
|
|
||||||
pub fn expect_dynamic(self: Unboxed, env: *RuntimeEnv) RuntimeError!*const DynamicObject {
|
|
||||||
return switch (self) {
|
|
||||||
.dynamic => |dynamic| dynamic,
|
|
||||||
else => env.raise(error.TypeMismatch, "expected dynamic"),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn assert(env: *RuntimeEnv, condition: bool, message: []const coral.io.Byte) RuntimeError!void {
|
|
||||||
if (!condition) {
|
|
||||||
return env.raise(error.Assertion, message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn call(
|
pub fn call(
|
||||||
env: *RuntimeEnv,
|
env: *RuntimeEnv,
|
||||||
caller_ref: ?*const RuntimeRef,
|
caller_ref: ?*const RuntimeRef,
|
||||||
callable_ref: ?*const RuntimeRef,
|
callable_ref: ?*const RuntimeRef,
|
||||||
arg_refs: Args,
|
arg_refs: []const ?*const RuntimeRef,
|
||||||
) RuntimeError!?*RuntimeRef {
|
) RuntimeError!?*RuntimeRef {
|
||||||
for (arg_refs) |arg_ref| {
|
for (arg_refs) |arg_ref| {
|
||||||
try env.local_push_ref(arg_ref);
|
try env.local_push_ref(arg_ref);
|
||||||
|
@ -428,7 +424,7 @@ pub fn get(
|
||||||
indexable_ref: ?*const RuntimeRef,
|
indexable_ref: ?*const RuntimeRef,
|
||||||
index_ref: ?*const RuntimeRef,
|
index_ref: ?*const RuntimeRef,
|
||||||
) RuntimeError!?*RuntimeRef {
|
) RuntimeError!?*RuntimeRef {
|
||||||
const dynamic = try env.unbox(indexable_ref).expect_dynamic(env);
|
const dynamic = try unbox_dynamic(env, indexable_ref);
|
||||||
|
|
||||||
return dynamic.typeinfo.get(.{
|
return dynamic.typeinfo.get(.{
|
||||||
.userdata = dynamic.userdata,
|
.userdata = dynamic.userdata,
|
||||||
|
@ -437,7 +433,11 @@ pub fn get(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_field(env: *RuntimeEnv, indexable_ref: ?*const RuntimeRef, field_name: []const coral.io.Byte) RuntimeError!?*RuntimeRef {
|
pub fn get_field(
|
||||||
|
env: *RuntimeEnv,
|
||||||
|
indexable_ref: ?*const RuntimeRef,
|
||||||
|
field_name: []const coral.io.Byte,
|
||||||
|
) RuntimeError!?*RuntimeRef {
|
||||||
const field_name_ref = try env.new_string(field_name);
|
const field_name_ref = try env.new_string(field_name);
|
||||||
|
|
||||||
defer env.discard(field_name_ref);
|
defer env.discard(field_name_ref);
|
||||||
|
@ -451,7 +451,7 @@ pub fn set(
|
||||||
index_ref: ?*const RuntimeRef,
|
index_ref: ?*const RuntimeRef,
|
||||||
value_ref: ?*const RuntimeRef,
|
value_ref: ?*const RuntimeRef,
|
||||||
) RuntimeError!void {
|
) RuntimeError!void {
|
||||||
const dynamic = try env.unbox(indexable_ref).expect_dynamic(env);
|
const dynamic = try unbox_dynamic(env, indexable_ref);
|
||||||
|
|
||||||
return dynamic.typeinfo.set(.{
|
return dynamic.typeinfo.set(.{
|
||||||
.userdata = dynamic.userdata,
|
.userdata = dynamic.userdata,
|
||||||
|
@ -460,7 +460,12 @@ pub fn set(
|
||||||
}, value_ref);
|
}, value_ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_field(env: *RuntimeEnv, indexable_ref: ?*const RuntimeRef, field_name: []const coral.io.Byte, value_ref: ?*const RuntimeRef) RuntimeError!void {
|
pub fn set_field(
|
||||||
|
env: *RuntimeEnv,
|
||||||
|
indexable_ref: ?*const RuntimeRef,
|
||||||
|
field_name: []const coral.io.Byte,
|
||||||
|
value_ref: ?*const RuntimeRef,
|
||||||
|
) RuntimeError!void {
|
||||||
const field_name_ref = try env.new_string(field_name);
|
const field_name_ref = try env.new_string(field_name);
|
||||||
|
|
||||||
defer env.discard(field_name_ref);
|
defer env.discard(field_name_ref);
|
||||||
|
@ -525,3 +530,11 @@ pub fn test_equality(env: *RuntimeEnv, lhs_ref: ?*const RuntimeRef, rhs_ref: ?*c
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn unbox_dynamic(env: *RuntimeEnv, ref: ?*const RuntimeRef) RuntimeError!*DynamicObject {
|
||||||
|
return env.unbox(ref).expect_dynamic() orelse env.raise(error.TypeMismatch, "expected dynamic object");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unbox_string(env: *RuntimeEnv, ref: ?*const RuntimeRef) RuntimeError![]const coral.io.Byte {
|
||||||
|
return env.unbox(ref).expect_string() orelse env.raise(error.TypeMismatch, "expected string object");
|
||||||
|
}
|
||||||
|
|
|
@ -206,6 +206,10 @@ pub fn append_opcode(self: *Self, opcode: Opcode) kym.RuntimeError!void {
|
||||||
return self.opcodes.push_one(opcode);
|
return self.opcodes.push_one(opcode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_caller(self: *Self) kym.Caller {
|
||||||
|
return kym.Caller.bind(Self, self, execute);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn compile_ast(self: *Self, ast: Ast) kym.RuntimeError!void {
|
pub fn compile_ast(self: *Self, ast: Ast) kym.RuntimeError!void {
|
||||||
self.free();
|
self.free();
|
||||||
|
|
||||||
|
@ -253,250 +257,239 @@ pub fn declare_constant_string(self: *Self, constant: []const coral.io.Byte) kym
|
||||||
return @intCast(tail);
|
return @intCast(tail);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute(self: *Self, name: []const coral.io.Byte) kym.RuntimeError!?*kym.RuntimeRef {
|
fn execute(self: *Self, env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeRef {
|
||||||
try self.env.frame_push(name, 0);
|
|
||||||
|
|
||||||
defer self.env.frame_pop();
|
|
||||||
|
|
||||||
for (self.opcodes.values) |opcode| {
|
for (self.opcodes.values) |opcode| {
|
||||||
switch (opcode) {
|
switch (opcode) {
|
||||||
.pop => self.env.discard(self.env.local_pop()),
|
.pop => env.discard(try env.pop_local()),
|
||||||
.push_nil => try self.env.local_push_ref(null),
|
.push_nil => try env.push_ref(null),
|
||||||
.push_true => try self.env.local_push_boolean(true),
|
.push_true => try env.push_boolean(true),
|
||||||
.push_false => try self.env.local_push_boolean(false),
|
.push_false => try env.push_boolean(false),
|
||||||
.push_const => |constant| try self.env.local_push_ref(self.constant_refs.values[constant]),
|
.push_const => |constant| try env.push_ref(self.constant_refs.values[constant]),
|
||||||
|
|
||||||
.push_table => |field_count| {
|
.push_table => |field_count| {
|
||||||
const table_ref = try kym.new_table(self.env);
|
const table_ref = try kym.new_table(env);
|
||||||
|
|
||||||
defer self.env.discard(table_ref);
|
defer env.discard(table_ref);
|
||||||
|
|
||||||
{
|
{
|
||||||
const dynamic = try self.env.unbox(table_ref).expect_dynamic(self.env);
|
const dynamic = try kym.unbox_dynamic(env, table_ref);
|
||||||
var popped = @as(usize, 0);
|
var popped = @as(usize, 0);
|
||||||
|
|
||||||
while (popped < field_count) : (popped += 1) {
|
while (popped < field_count) : (popped += 1) {
|
||||||
const index_ref = self.env.local_pop();
|
const index_ref = try env.pop_local();
|
||||||
const value_ref = self.env.local_pop();
|
|
||||||
|
defer env.discard(index_ref);
|
||||||
|
|
||||||
|
const value_ref = try env.pop_local();
|
||||||
|
|
||||||
|
defer env.discard(value_ref);
|
||||||
|
|
||||||
try dynamic.typeinfo.set(.{
|
try dynamic.typeinfo.set(.{
|
||||||
.userdata = dynamic.userdata,
|
.userdata = dynamic.userdata,
|
||||||
.env = self.env,
|
.env = env,
|
||||||
.index_ref = index_ref,
|
.index_ref = index_ref,
|
||||||
}, value_ref);
|
}, value_ref);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try self.env.local_push_ref(table_ref);
|
try env.push_ref(table_ref);
|
||||||
},
|
},
|
||||||
|
|
||||||
.push_local => |local| {
|
.push_local => |local| {
|
||||||
const ref = self.env.local_get(local);
|
const ref = try env.get_local(local);
|
||||||
|
|
||||||
defer self.env.discard(ref);
|
defer env.discard(ref);
|
||||||
|
|
||||||
try self.env.local_push_ref(ref);
|
try env.push_ref(ref);
|
||||||
},
|
},
|
||||||
|
|
||||||
.set_local => |local| {
|
.set_local => |local| {
|
||||||
const ref = self.env.local_pop();
|
const ref = try env.pop_local();
|
||||||
|
|
||||||
defer self.env.discard(ref);
|
defer env.discard(ref);
|
||||||
|
|
||||||
self.env.local_set(local, ref);
|
try env.set_local(local, ref);
|
||||||
},
|
},
|
||||||
|
|
||||||
.call => |arg_count| {
|
.call => |arg_count| {
|
||||||
const callable_ref = self.env.local_pop();
|
const result_ref = call: {
|
||||||
|
const callable_ref = try env.pop_local();
|
||||||
|
|
||||||
defer self.env.discard(callable_ref);
|
defer env.discard(callable_ref);
|
||||||
|
|
||||||
try self.env.frame_push("", arg_count);
|
break: call try env.call("", arg_count, (try kym.unbox_dynamic(env, callable_ref)).as_caller());
|
||||||
|
};
|
||||||
|
|
||||||
defer self.env.frame_pop();
|
defer env.discard(result_ref);
|
||||||
|
|
||||||
const dynamic = try self.env.unbox(callable_ref).expect_dynamic(self.env);
|
try env.push_ref(result_ref);
|
||||||
|
|
||||||
const result_ref = try dynamic.typeinfo.call(.{
|
|
||||||
.env = self.env,
|
|
||||||
.caller = null,
|
|
||||||
.userdata = dynamic.userdata,
|
|
||||||
});
|
|
||||||
|
|
||||||
defer self.env.discard(result_ref);
|
|
||||||
|
|
||||||
try self.env.local_push_ref(result_ref);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
.syscall => |arg_count| {
|
.syscall => |arg_count| {
|
||||||
const identifier_ref = self.env.local_pop();
|
const result_ref = call: {
|
||||||
|
const identifier_ref = try env.pop_local();
|
||||||
|
|
||||||
defer self.env.discard(identifier_ref);
|
defer env.discard(identifier_ref);
|
||||||
|
|
||||||
const identifier = try self.env.unbox_string(identifier_ref);
|
const identifier = try kym.unbox_string(env, identifier_ref);
|
||||||
|
|
||||||
try self.env.frame_push(identifier, arg_count);
|
break: call try env.call(identifier, arg_count, try env.syscaller(identifier));
|
||||||
|
};
|
||||||
|
|
||||||
errdefer self.env.frame_pop();
|
defer env.discard(result_ref);
|
||||||
|
|
||||||
const result_ref = try (try self.env.syscaller(identifier)).invoke(self.env);
|
try env.push_ref(result_ref);
|
||||||
|
|
||||||
defer self.env.discard(result_ref);
|
|
||||||
|
|
||||||
self.env.frame_pop();
|
|
||||||
|
|
||||||
try self.env.local_push_ref(result_ref);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
.neg => try self.env.local_push_number(switch (self.env.unbox(self.env.local_pop())) {
|
.neg => try env.push_number(switch (env.unbox(try env.pop_local())) {
|
||||||
.number => |number| -number,
|
.number => |number| -number,
|
||||||
else => return self.env.raise(error.TypeMismatch, "object is not scalar negatable"),
|
else => return env.raise(error.TypeMismatch, "object is not scalar negatable"),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
.not => try self.env.local_push_boolean(switch (self.env.unbox(self.env.local_pop())) {
|
.not => try env.push_boolean(switch (env.unbox(try env.pop_local())) {
|
||||||
.boolean => |boolean| !boolean,
|
.boolean => |boolean| !boolean,
|
||||||
else => return self.env.raise(error.TypeMismatch, "object is not boolean negatable"),
|
else => return env.raise(error.TypeMismatch, "object is not boolean negatable"),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
.add => {
|
.add => {
|
||||||
const rhs_ref = self.env.local_pop();
|
const rhs_ref = try env.pop_local();
|
||||||
|
|
||||||
defer self.env.discard(rhs_ref);
|
defer env.discard(rhs_ref);
|
||||||
|
|
||||||
const lhs_ref = self.env.local_pop();
|
const lhs_ref = try env.pop_local();
|
||||||
|
|
||||||
defer self.env.discard(lhs_ref);
|
defer env.discard(lhs_ref);
|
||||||
|
|
||||||
try self.env.local_push_ref(switch (self.env.unbox(lhs_ref)) {
|
try env.push_ref(try switch (env.unbox(lhs_ref)) {
|
||||||
.number => |lhs_number| switch (self.env.unbox(rhs_ref)) {
|
.number => |lhs_number| switch (env.unbox(rhs_ref)) {
|
||||||
.number => |rhs_number| try self.env.new_number(lhs_number + rhs_number),
|
.number => |rhs_number| env.new_number(lhs_number + rhs_number),
|
||||||
else => return self.env.raise(error.TypeMismatch, "right-hand object is not addable"),
|
else => return env.raise(error.TypeMismatch, "right-hand object is not addable"),
|
||||||
},
|
},
|
||||||
|
|
||||||
else => return self.env.raise(error.TypeMismatch, "left-hand object is not addable"),
|
else => return env.raise(error.TypeMismatch, "left-hand object is not addable"),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
.sub => {
|
.sub => {
|
||||||
const rhs_ref = self.env.local_pop();
|
const rhs_ref = try env.pop_local();
|
||||||
|
|
||||||
defer self.env.discard(rhs_ref);
|
defer env.discard(rhs_ref);
|
||||||
|
|
||||||
const lhs_ref = self.env.local_pop();
|
const lhs_ref = try env.pop_local();
|
||||||
|
|
||||||
defer self.env.discard(lhs_ref);
|
defer env.discard(lhs_ref);
|
||||||
|
|
||||||
try self.env.local_push_ref(switch (self.env.unbox(lhs_ref)) {
|
try env.push_ref(try switch (env.unbox(lhs_ref)) {
|
||||||
.number => |lhs_number| switch (self.env.unbox(rhs_ref)) {
|
.number => |lhs_number| switch (env.unbox(rhs_ref)) {
|
||||||
.number => |rhs_number| try self.env.new_number(lhs_number - rhs_number),
|
.number => |rhs_number| env.new_number(lhs_number - rhs_number),
|
||||||
else => return self.env.raise(error.TypeMismatch, "right-hand object is not subtractable"),
|
else => return env.raise(error.TypeMismatch, "right-hand object is not subtractable"),
|
||||||
},
|
},
|
||||||
|
|
||||||
else => return self.env.raise(error.TypeMismatch, "left-hand object is not subtractable"),
|
else => return env.raise(error.TypeMismatch, "left-hand object is not subtractable"),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
.mul => {
|
.mul => {
|
||||||
const rhs_ref = self.env.local_pop();
|
const rhs_ref = try env.pop_local();
|
||||||
|
|
||||||
defer self.env.discard(rhs_ref);
|
defer env.discard(rhs_ref);
|
||||||
|
|
||||||
const lhs_ref = self.env.local_pop();
|
const lhs_ref = try env.pop_local();
|
||||||
|
|
||||||
defer self.env.discard(lhs_ref);
|
defer env.discard(lhs_ref);
|
||||||
|
|
||||||
try self.env.local_push_ref(switch (self.env.unbox(lhs_ref)) {
|
try env.push_ref(try switch (env.unbox(lhs_ref)) {
|
||||||
.number => |lhs_number| switch (self.env.unbox(rhs_ref)) {
|
.number => |lhs_number| switch (env.unbox(rhs_ref)) {
|
||||||
.number => |rhs_number| try self.env.new_number(lhs_number * rhs_number),
|
.number => |rhs_number| env.new_number(lhs_number * rhs_number),
|
||||||
else => return self.env.raise(error.TypeMismatch, "right-hand object is not multiplyable"),
|
else => return env.raise(error.TypeMismatch, "right-hand object is not multiplyable"),
|
||||||
},
|
},
|
||||||
|
|
||||||
else => return self.env.raise(error.TypeMismatch, "left-hand object is not multiplyable"),
|
else => return env.raise(error.TypeMismatch, "left-hand object is not multiplyable"),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
.div => {
|
.div => {
|
||||||
const rhs_ref = self.env.local_pop();
|
const rhs_ref = try env.pop_local();
|
||||||
|
|
||||||
defer self.env.discard(rhs_ref);
|
defer env.discard(rhs_ref);
|
||||||
|
|
||||||
const lhs_ref = self.env.local_pop();
|
const lhs_ref = try env.pop_local();
|
||||||
|
|
||||||
defer self.env.discard(lhs_ref);
|
defer env.discard(lhs_ref);
|
||||||
|
|
||||||
try self.env.local_push_ref(switch (self.env.unbox(lhs_ref)) {
|
try env.push_ref(try switch (env.unbox(lhs_ref)) {
|
||||||
.number => |lhs_number| switch (self.env.unbox(rhs_ref)) {
|
.number => |lhs_number| switch (env.unbox(rhs_ref)) {
|
||||||
.number => |rhs_number| try self.env.new_number(lhs_number / rhs_number),
|
.number => |rhs_number| env.new_number(lhs_number / rhs_number),
|
||||||
else => return self.env.raise(error.TypeMismatch, "right-hand object is not divisable"),
|
else => return env.raise(error.TypeMismatch, "right-hand object is not divisable"),
|
||||||
},
|
},
|
||||||
|
|
||||||
else => return self.env.raise(error.TypeMismatch, "left-hand object is not divisable"),
|
else => return env.raise(error.TypeMismatch, "left-hand object is not divisable"),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
.eql => {
|
.eql => {
|
||||||
const rhs_ref = self.env.local_pop();
|
const rhs_ref = try env.pop_local();
|
||||||
|
|
||||||
defer self.env.discard(rhs_ref);
|
defer env.discard(rhs_ref);
|
||||||
|
|
||||||
const lhs_ref = self.env.local_pop();
|
const lhs_ref = try env.pop_local();
|
||||||
|
|
||||||
defer self.env.discard(lhs_ref);
|
defer env.discard(lhs_ref);
|
||||||
|
|
||||||
try self.env.local_push_boolean(try kym.test_equality(self.env, lhs_ref, rhs_ref));
|
try env.push_boolean(try kym.test_equality(env, lhs_ref, rhs_ref));
|
||||||
},
|
},
|
||||||
|
|
||||||
.cgt => {
|
.cgt => {
|
||||||
const rhs_ref = self.env.local_pop();
|
const rhs_ref = try env.pop_local();
|
||||||
|
|
||||||
defer self.env.discard(rhs_ref);
|
defer env.discard(rhs_ref);
|
||||||
|
|
||||||
const lhs_ref = self.env.local_pop();
|
const lhs_ref = try env.pop_local();
|
||||||
|
|
||||||
defer self.env.discard(lhs_ref);
|
defer env.discard(lhs_ref);
|
||||||
|
|
||||||
try self.env.local_push_boolean(try kym.test_difference(self.env, lhs_ref, rhs_ref) > 0);
|
try env.push_boolean(try kym.test_difference(env, lhs_ref, rhs_ref) > 0);
|
||||||
},
|
},
|
||||||
|
|
||||||
.clt => {
|
.clt => {
|
||||||
const rhs_ref = self.env.local_pop();
|
const rhs_ref = try env.pop_local();
|
||||||
|
|
||||||
defer self.env.discard(rhs_ref);
|
defer env.discard(rhs_ref);
|
||||||
|
|
||||||
const lhs_ref = self.env.local_pop();
|
const lhs_ref = try env.pop_local();
|
||||||
|
|
||||||
defer self.env.discard(lhs_ref);
|
defer env.discard(lhs_ref);
|
||||||
|
|
||||||
try self.env.local_push_boolean(try kym.test_difference(self.env, lhs_ref, rhs_ref) < 0);
|
try env.push_boolean(try kym.test_difference(env, lhs_ref, rhs_ref) < 0);
|
||||||
},
|
},
|
||||||
|
|
||||||
.cge => {
|
.cge => {
|
||||||
const rhs_ref = self.env.local_pop();
|
const rhs_ref = try env.pop_local();
|
||||||
|
|
||||||
defer self.env.discard(rhs_ref);
|
defer env.discard(rhs_ref);
|
||||||
|
|
||||||
const lhs_ref = self.env.local_pop();
|
const lhs_ref = try env.pop_local();
|
||||||
|
|
||||||
defer self.env.discard(lhs_ref);
|
defer env.discard(lhs_ref);
|
||||||
|
|
||||||
try self.env.local_push_boolean(try kym.test_difference(self.env, lhs_ref, rhs_ref) >= 0);
|
try env.push_boolean(try kym.test_difference(env, lhs_ref, rhs_ref) >= 0);
|
||||||
},
|
},
|
||||||
|
|
||||||
.cle => {
|
.cle => {
|
||||||
const rhs_ref = self.env.local_pop();
|
const rhs_ref = try env.pop_local();
|
||||||
|
|
||||||
defer self.env.discard(rhs_ref);
|
defer env.discard(rhs_ref);
|
||||||
|
|
||||||
const lhs_ref = self.env.local_pop();
|
const lhs_ref = try env.pop_local();
|
||||||
|
|
||||||
defer self.env.discard(lhs_ref);
|
defer env.discard(lhs_ref);
|
||||||
|
|
||||||
try self.env.local_push_boolean(try kym.test_difference(self.env, lhs_ref, rhs_ref) <= 0);
|
try env.push_boolean(try kym.test_difference(env, lhs_ref, rhs_ref) <= 0);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.env.local_pop();
|
return env.pop_local();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn free(self: *Self) void {
|
pub fn free(self: *Self) void {
|
||||||
|
|
|
@ -21,19 +21,19 @@ fn kym_handle_errors(info: kym.ErrorInfo) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kym_log_info(env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeRef {
|
fn kym_log_info(env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeRef {
|
||||||
app.log_info(try env.unbox_string(try env.get_arg(0)));
|
app.log_info(try kym.unbox_string(env, try env.view_arg(0)));
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kym_log_warn(env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeRef {
|
fn kym_log_warn(env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeRef {
|
||||||
app.log_warn(try env.unbox_string(try env.get_arg(0)));
|
app.log_warn(try kym.unbox_string(env, try env.view_arg(0)));
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kym_log_fail(env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeRef {
|
fn kym_log_fail(env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeRef {
|
||||||
app.log_fail(try env.unbox_string(try env.get_arg(0)));
|
app.log_fail(try kym.unbox_string(env, try env.view_arg(0)));
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,8 @@ fn last_sdl_error() [:0]const u8 {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_app(file_access: file.Access) void {
|
pub fn run_app(file_access: file.Access) void {
|
||||||
|
defer heap.trace_leaks();
|
||||||
|
|
||||||
if (ext.SDL_Init(ext.SDL_INIT_EVERYTHING) != 0) {
|
if (ext.SDL_Init(ext.SDL_INIT_EVERYTHING) != 0) {
|
||||||
return app.log_fail(last_sdl_error());
|
return app.log_fail(last_sdl_error());
|
||||||
}
|
}
|
||||||
|
@ -58,11 +60,11 @@ pub fn run_app(file_access: file.Access) void {
|
||||||
script_env.bind_syscalls(&.{
|
script_env.bind_syscalls(&.{
|
||||||
.{
|
.{
|
||||||
.name = "log_info",
|
.name = "log_info",
|
||||||
.caller = kym.Syscaller.from(kym_log_info),
|
.caller = kym.Caller.from(kym_log_info),
|
||||||
},
|
},
|
||||||
.{
|
.{
|
||||||
.name = "log_fail",
|
.name = "log_fail",
|
||||||
.caller = kym.Syscaller.from(kym_log_fail),
|
.caller = kym.Caller.from(kym_log_fail),
|
||||||
},
|
},
|
||||||
}) catch {
|
}) catch {
|
||||||
return app.log_fail("failed to initialize script runtime");
|
return app.log_fail("failed to initialize script runtime");
|
||||||
|
|
Loading…
Reference in New Issue