kym-tables-overhaul #30
|
@ -7,7 +7,8 @@
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"target": "${workspaceRoot}/zig-out/bin/runner",
|
"target": "${workspaceRoot}/zig-out/bin/runner",
|
||||||
"cwd": "${workspaceRoot}/debug/",
|
"cwd": "${workspaceRoot}/debug/",
|
||||||
"valuesFormatting": "parseText"
|
"valuesFormatting": "parseText",
|
||||||
|
"preLaunchTask": "Build All"
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
@log_info("game is loading")
|
@log_info("game is loading")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title = "Afterglow",
|
.title = "Afterglow",
|
||||||
width = 1280,
|
.width = 1280,
|
||||||
height = 800,
|
.height = 800,
|
||||||
tick_rate = 60,
|
.tick_rate = 60,
|
||||||
}
|
}
|
||||||
|
|
|
@ -336,7 +336,7 @@ pub fn Table(comptime Key: type, comptime Value: type, comptime traits: TableTra
|
||||||
pub fn TableTraits(comptime Key: type) type {
|
pub fn TableTraits(comptime Key: type) type {
|
||||||
return struct {
|
return struct {
|
||||||
hash: fn (key: Key) usize,
|
hash: fn (key: Key) usize,
|
||||||
match: fn (key: Key, key: Key) bool,
|
match: fn (self: Key, other: Key) bool,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ pub const Manifest = struct {
|
||||||
tick_rate: f32 = 60.0,
|
tick_rate: f32 = 60.0,
|
||||||
|
|
||||||
pub fn load(self: *Manifest, env: *kym.RuntimeEnv, file_access: file.Access) kym.RuntimeError!void {
|
pub fn load(self: *Manifest, env: *kym.RuntimeEnv, file_access: file.Access) kym.RuntimeError!void {
|
||||||
const manifest_ref = try env.execute_file(file_access, file.Path.from(&.{"app.ona"}));
|
const manifest_ref = try env.execute_file(file_access, file.Path.from(&.{"app.ona"})) orelse return;
|
||||||
|
|
||||||
defer env.discard(manifest_ref);
|
defer env.discard(manifest_ref);
|
||||||
|
|
||||||
|
@ -22,11 +22,13 @@ pub const Manifest = struct {
|
||||||
|
|
||||||
defer env.discard(ref);
|
defer env.discard(ref);
|
||||||
|
|
||||||
if (env.unbox(ref).expect_number()) |number| {
|
if (ref) |live_ref| {
|
||||||
|
if (env.unbox(live_ref).expect_number()) |number| {
|
||||||
if (number > 0 and number < coral.math.max_int(@typeInfo(@TypeOf(self.width)).Int)) {
|
if (number > 0 and number < coral.math.max_int(@typeInfo(@TypeOf(self.width)).Int)) {
|
||||||
break: get @intFromFloat(number);
|
break: get @intFromFloat(number);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break: get self.width;
|
break: get self.width;
|
||||||
});
|
});
|
||||||
|
@ -36,11 +38,13 @@ pub const Manifest = struct {
|
||||||
|
|
||||||
defer env.discard(ref);
|
defer env.discard(ref);
|
||||||
|
|
||||||
if (env.unbox(ref).expect_number()) |number| {
|
if (ref) |live_ref| {
|
||||||
|
if (env.unbox(live_ref).expect_number()) |number| {
|
||||||
if (number > 0 and number < coral.math.max_int(@typeInfo(@TypeOf(self.height)).Int)) {
|
if (number > 0 and number < coral.math.max_int(@typeInfo(@TypeOf(self.height)).Int)) {
|
||||||
break: get @intFromFloat(number);
|
break: get @intFromFloat(number);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break: get self.height;
|
break: get self.height;
|
||||||
});
|
});
|
||||||
|
@ -50,19 +54,30 @@ pub const Manifest = struct {
|
||||||
|
|
||||||
defer env.discard(ref);
|
defer env.discard(ref);
|
||||||
|
|
||||||
if (env.unbox(ref).expect_number()) |number| {
|
if (ref) |live_ref| {
|
||||||
|
if (env.unbox(live_ref).expect_number()) |number| {
|
||||||
break: get @floatCast(number);
|
break: get @floatCast(number);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break: get self.tick_rate;
|
break: get self.tick_rate;
|
||||||
});
|
});
|
||||||
|
|
||||||
{
|
{
|
||||||
const title_ref = try kym.get_field(env, manifest_ref, "title");
|
const ref = try kym.get_field(env, manifest_ref, "title");
|
||||||
|
|
||||||
defer env.discard(title_ref);
|
defer env.discard(ref);
|
||||||
|
|
||||||
|
const title_string = unbox: {
|
||||||
|
if (ref) |live_ref| {
|
||||||
|
if (env.unbox(live_ref).expect_string()) |string| {
|
||||||
|
break: unbox string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break: unbox "";
|
||||||
|
};
|
||||||
|
|
||||||
const title_string = env.unbox(title_ref).expect_string() orelse "";
|
|
||||||
const limited_title_len = @min(title_string.len, self.title.len);
|
const limited_title_len = @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]);
|
||||||
|
|
|
@ -9,10 +9,10 @@ const coral = @import("coral");
|
||||||
const file = @import("./file.zig");
|
const file = @import("./file.zig");
|
||||||
|
|
||||||
pub const Any = union (enum) {
|
pub const Any = union (enum) {
|
||||||
nil,
|
|
||||||
boolean: bool,
|
boolean: bool,
|
||||||
number: Float,
|
number: Float,
|
||||||
string: []const coral.io.Byte,
|
string: []const coral.io.Byte,
|
||||||
|
symbol: []const coral.io.Byte,
|
||||||
dynamic: *DynamicObject,
|
dynamic: *DynamicObject,
|
||||||
|
|
||||||
pub fn expect_dynamic(self: Any) ?*DynamicObject {
|
pub fn expect_dynamic(self: Any) ?*DynamicObject {
|
||||||
|
@ -39,6 +39,11 @@ pub const Any = union (enum) {
|
||||||
|
|
||||||
pub const Caller = coral.io.Generator(RuntimeError!?*RuntimeRef, *RuntimeEnv);
|
pub const Caller = coral.io.Generator(RuntimeError!?*RuntimeRef, *RuntimeEnv);
|
||||||
|
|
||||||
|
pub const Context = struct {
|
||||||
|
env: *RuntimeEnv,
|
||||||
|
userdata: []coral.io.Byte
|
||||||
|
};
|
||||||
|
|
||||||
pub const DynamicObject = struct {
|
pub const DynamicObject = struct {
|
||||||
userdata: []coral.io.Byte,
|
userdata: []coral.io.Byte,
|
||||||
typeinfo: *const Typeinfo,
|
typeinfo: *const Typeinfo,
|
||||||
|
@ -67,13 +72,8 @@ pub const Frame = struct {
|
||||||
locals_top: usize,
|
locals_top: usize,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const IndexContext = struct {
|
|
||||||
env: *RuntimeEnv,
|
|
||||||
userdata: []coral.io.Byte,
|
|
||||||
index_ref: ?*const RuntimeRef,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const RuntimeEnv = struct {
|
pub const RuntimeEnv = struct {
|
||||||
|
interned_symbols: SymbolTable,
|
||||||
allocator: coral.io.Allocator,
|
allocator: coral.io.Allocator,
|
||||||
error_handler: ErrorHandler,
|
error_handler: ErrorHandler,
|
||||||
syscallers: SyscallerTable,
|
syscallers: SyscallerTable,
|
||||||
|
@ -83,6 +83,8 @@ pub const RuntimeEnv = struct {
|
||||||
|
|
||||||
const FrameStack = coral.list.Stack(Frame);
|
const FrameStack = coral.list.Stack(Frame);
|
||||||
|
|
||||||
|
const SymbolTable = coral.map.StringTable(*RuntimeRef);
|
||||||
|
|
||||||
const SyscallerTable = coral.map.StringTable(Caller);
|
const SyscallerTable = coral.map.StringTable(Caller);
|
||||||
|
|
||||||
const RefStack = coral.list.Stack(?*RuntimeRef);
|
const RefStack = coral.list.Stack(?*RuntimeRef);
|
||||||
|
@ -95,6 +97,7 @@ pub const RuntimeEnv = struct {
|
||||||
true,
|
true,
|
||||||
number: Float,
|
number: Float,
|
||||||
string: []coral.io.Byte,
|
string: []coral.io.Byte,
|
||||||
|
symbol: []coral.io.Byte,
|
||||||
dynamic: *DynamicObject,
|
dynamic: *DynamicObject,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -104,8 +107,8 @@ pub const RuntimeEnv = struct {
|
||||||
caller: Caller,
|
caller: Caller,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn acquire(self: *RuntimeEnv, ref: ?*const RuntimeRef) ?*RuntimeRef {
|
pub fn acquire(self: *RuntimeEnv, ref: *const RuntimeRef) *RuntimeRef {
|
||||||
const key = @intFromPtr(ref orelse return null);
|
const key = @intFromPtr(ref);
|
||||||
var ref_data = self.ref_values.remove(key);
|
var ref_data = self.ref_values.remove(key);
|
||||||
|
|
||||||
coral.debug.assert(ref_data != null);
|
coral.debug.assert(ref_data != null);
|
||||||
|
@ -166,15 +169,21 @@ pub const RuntimeEnv = struct {
|
||||||
|
|
||||||
if (ref_data.ref_count == 0) {
|
if (ref_data.ref_count == 0) {
|
||||||
switch (ref_data.object) {
|
switch (ref_data.object) {
|
||||||
|
.false => {},
|
||||||
|
.true => {},
|
||||||
|
.number => {},
|
||||||
.string => |string| self.allocator.deallocate(string),
|
.string => |string| self.allocator.deallocate(string),
|
||||||
|
.symbol => |symbol| self.allocator.deallocate(symbol),
|
||||||
|
|
||||||
.dynamic => |dynamic| {
|
.dynamic => |dynamic| {
|
||||||
dynamic.typeinfo.clean(self, dynamic.userdata);
|
dynamic.typeinfo.clean(.{
|
||||||
|
.env = self,
|
||||||
|
.userdata = dynamic.userdata,
|
||||||
|
});
|
||||||
|
|
||||||
self.allocator.deallocate(dynamic.userdata);
|
self.allocator.deallocate(dynamic.userdata);
|
||||||
self.allocator.deallocate(dynamic);
|
self.allocator.deallocate(dynamic);
|
||||||
},
|
},
|
||||||
|
|
||||||
else => {},
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
coral.debug.assert(self.ref_values.insert_at(key, ref_data));
|
coral.debug.assert(self.ref_values.insert_at(key, ref_data));
|
||||||
|
@ -217,14 +226,23 @@ pub const RuntimeEnv = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn free(self: *RuntimeEnv) void {
|
pub fn free(self: *RuntimeEnv) void {
|
||||||
for (self.local_refs.values) |ref| {
|
while (self.local_refs.pop()) |ref| {
|
||||||
self.discard(ref);
|
self.discard(ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var iterable = self.interned_symbols.as_iterable();
|
||||||
|
|
||||||
|
while (iterable.next()) |entry| {
|
||||||
|
self.discard(entry.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.frames.free();
|
self.frames.free();
|
||||||
self.syscallers.free();
|
|
||||||
self.local_refs.free();
|
self.local_refs.free();
|
||||||
|
self.syscallers.free();
|
||||||
self.ref_values.free();
|
self.ref_values.free();
|
||||||
|
self.interned_symbols.free();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_local(self: *RuntimeEnv, local: u8) RuntimeError!?*RuntimeRef {
|
pub fn get_local(self: *RuntimeEnv, local: u8) RuntimeError!?*RuntimeRef {
|
||||||
|
@ -232,7 +250,7 @@ pub const RuntimeEnv = struct {
|
||||||
return self.raise(error.IllegalState, "out of bounds local get");
|
return self.raise(error.IllegalState, "out of bounds local get");
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.acquire(self.local_refs.values[local]);
|
return self.acquire(self.local_refs.values[local] orelse return null);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop_local(self: *RuntimeEnv) RuntimeError!?*RuntimeRef {
|
pub fn pop_local(self: *RuntimeEnv) RuntimeError!?*RuntimeRef {
|
||||||
|
@ -248,15 +266,15 @@ pub const RuntimeEnv = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_ref(self: *RuntimeEnv, ref: ?*RuntimeRef) RuntimeError!void {
|
pub fn push_ref(self: *RuntimeEnv, ref: ?*RuntimeRef) RuntimeError!void {
|
||||||
return self.local_refs.push_one(self.acquire(ref));
|
return self.local_refs.push_one(if (ref) |live_ref| self.acquire(live_ref) else null);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_local(self: *RuntimeEnv, local: u8, value: ?*RuntimeRef) RuntimeError!void {
|
pub fn set_local(self: *RuntimeEnv, local: u8, ref: ?*RuntimeRef) RuntimeError!void {
|
||||||
if (local >= self.local_refs.values.len) {
|
if (local >= self.local_refs.values.len) {
|
||||||
return self.raise(error.IllegalState, "out of bounds local set");
|
return self.raise(error.IllegalState, "out of bounds local set");
|
||||||
}
|
}
|
||||||
|
|
||||||
self.local_refs.values[local] = self.acquire(value);
|
self.local_refs.values[local] = if (ref) |live_ref| self.acquire(live_ref) else null;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make(allocator: coral.io.Allocator, error_handler: ErrorHandler) coral.io.AllocationError!RuntimeEnv {
|
pub fn make(allocator: coral.io.Allocator, error_handler: ErrorHandler) coral.io.AllocationError!RuntimeEnv {
|
||||||
|
@ -265,12 +283,13 @@ pub const RuntimeEnv = struct {
|
||||||
.ref_values = RefSlab.make(allocator),
|
.ref_values = RefSlab.make(allocator),
|
||||||
.frames = FrameStack.make(allocator),
|
.frames = FrameStack.make(allocator),
|
||||||
.syscallers = SyscallerTable.make(allocator),
|
.syscallers = SyscallerTable.make(allocator),
|
||||||
|
.interned_symbols = SymbolTable.make(allocator),
|
||||||
.error_handler = error_handler,
|
.error_handler = error_handler,
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_boolean(self: *RuntimeEnv, value: bool) RuntimeError!?*RuntimeRef {
|
pub fn new_boolean(self: *RuntimeEnv, value: bool) RuntimeError!*RuntimeRef {
|
||||||
return @ptrFromInt(try self.ref_values.insert(.{
|
return @ptrFromInt(try self.ref_values.insert(.{
|
||||||
.ref_count = 1,
|
.ref_count = 1,
|
||||||
.object = if (value) .true else .false,
|
.object = if (value) .true else .false,
|
||||||
|
@ -299,14 +318,14 @@ pub const RuntimeEnv = struct {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_number(self: *RuntimeEnv, value: Float) RuntimeError!?*RuntimeRef {
|
pub fn new_number(self: *RuntimeEnv, value: Float) RuntimeError!*RuntimeRef {
|
||||||
return @ptrFromInt(try self.ref_values.insert(.{
|
return @ptrFromInt(try self.ref_values.insert(.{
|
||||||
.ref_count = 1,
|
.ref_count = 1,
|
||||||
.object = .{.number = value},
|
.object = .{.number = value},
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_string(self: *RuntimeEnv, string_data: []const coral.io.Byte) RuntimeError!?*RuntimeRef {
|
pub fn new_string(self: *RuntimeEnv, string_data: []const coral.io.Byte) RuntimeError!*RuntimeRef {
|
||||||
const string_copy = try coral.io.allocate_copy(self.allocator, string_data);
|
const string_copy = try coral.io.allocate_copy(self.allocator, string_data);
|
||||||
|
|
||||||
errdefer self.allocator.deallocate(string_copy);
|
errdefer self.allocator.deallocate(string_copy);
|
||||||
|
@ -317,6 +336,29 @@ pub const RuntimeEnv = struct {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_symbol(self: *RuntimeEnv, symbol_data: []const coral.io.Byte) RuntimeError!*RuntimeRef {
|
||||||
|
if (self.interned_symbols.lookup(symbol_data)) |symbol_ref| {
|
||||||
|
return self.acquire(symbol_ref);
|
||||||
|
} else {
|
||||||
|
const symbol_copy = try coral.io.allocate_copy(self.allocator, symbol_data);
|
||||||
|
|
||||||
|
const symbol_ref = @as(*RuntimeRef, new: {
|
||||||
|
errdefer self.allocator.deallocate(symbol_copy);
|
||||||
|
|
||||||
|
break: new @ptrFromInt(try self.ref_values.insert(.{
|
||||||
|
.ref_count = 1,
|
||||||
|
.object = .{.symbol = symbol_copy},
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
errdefer self.discard(symbol_ref);
|
||||||
|
|
||||||
|
coral.debug.assert(try self.interned_symbols.insert(symbol_copy, symbol_ref));
|
||||||
|
|
||||||
|
return self.acquire(symbol_ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn raise(self: *RuntimeEnv, error_value: RuntimeError, message: []const coral.io.Byte) RuntimeError {
|
pub fn raise(self: *RuntimeEnv, error_value: RuntimeError, message: []const coral.io.Byte) RuntimeError {
|
||||||
self.error_handler.invoke(.{
|
self.error_handler.invoke(.{
|
||||||
.message = message,
|
.message = message,
|
||||||
|
@ -330,9 +372,8 @@ pub const RuntimeEnv = struct {
|
||||||
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) Any {
|
pub fn unbox(self: *RuntimeEnv, ref: *const RuntimeRef) Any {
|
||||||
if (ref) |live_ref| {
|
const ref_data = self.ref_values.lookup(@intFromPtr(ref));
|
||||||
const ref_data = self.ref_values.lookup(@intFromPtr(live_ref));
|
|
||||||
|
|
||||||
coral.debug.assert(ref_data != null);
|
coral.debug.assert(ref_data != null);
|
||||||
|
|
||||||
|
@ -341,13 +382,11 @@ pub const RuntimeEnv = struct {
|
||||||
.true => .{.boolean = true},
|
.true => .{.boolean = true},
|
||||||
.number => |number| .{.number = number},
|
.number => |number| .{.number = number},
|
||||||
.string => |string| .{.string = string},
|
.string => |string| .{.string = string},
|
||||||
|
.symbol => |symbol| .{.symbol = symbol},
|
||||||
.dynamic => |dynamic| .{.dynamic = dynamic},
|
.dynamic => |dynamic| .{.dynamic = dynamic},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return .nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn view_arg(self: *RuntimeEnv, index: usize) RuntimeError!?*const RuntimeRef {
|
pub fn view_arg(self: *RuntimeEnv, index: usize) RuntimeError!?*const RuntimeRef {
|
||||||
const frame = self.frames.peek() orelse return self.raise(error.IllegalState, "stack underflow");
|
const frame = self.frames.peek() orelse return self.raise(error.IllegalState, "stack underflow");
|
||||||
|
|
||||||
|
@ -368,43 +407,37 @@ pub const RuntimeError = coral.io.AllocationError || error {
|
||||||
|
|
||||||
pub const RuntimeRef = opaque {};
|
pub const RuntimeRef = opaque {};
|
||||||
|
|
||||||
pub const TestContext = struct {
|
|
||||||
env: *RuntimeEnv,
|
|
||||||
userdata: []const coral.io.Byte,
|
|
||||||
other_ref: ?*const RuntimeRef,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Typeinfo = struct {
|
pub const Typeinfo = struct {
|
||||||
name: []const coral.io.Byte,
|
name: []const coral.io.Byte,
|
||||||
call: *const fn (env: *RuntimeEnv) RuntimeError!?*RuntimeRef = default_call,
|
call: *const fn (context: Context) RuntimeError!?*RuntimeRef = default_call,
|
||||||
clean: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte) void = default_clean,
|
clean: *const fn (context: Context) void = default_clean,
|
||||||
get: *const fn (context: IndexContext) RuntimeError!?*RuntimeRef = default_get,
|
get: *const fn (context: Context, index: *const RuntimeRef) RuntimeError!?*RuntimeRef = default_get,
|
||||||
set: *const fn (context: IndexContext, value: ?*const RuntimeRef) RuntimeError!void = default_set,
|
set: *const fn (context: Context, index: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void = default_set,
|
||||||
test_difference: *const fn (context: TestContext) RuntimeError!Float = default_test_difference,
|
test_difference: *const fn (context: Context, other_ref: *const RuntimeRef) RuntimeError!Float = default_test_difference,
|
||||||
test_equality: *const fn (context: TestContext) RuntimeError!bool = default_test_equality,
|
test_equality: *const fn (context: Context, other_ref: *const RuntimeRef) RuntimeError!bool = default_test_equality,
|
||||||
|
|
||||||
fn default_call(env: *RuntimeEnv) RuntimeError!?*RuntimeRef {
|
fn default_call(context: Context) RuntimeError!?*RuntimeRef {
|
||||||
return env.raise(error.TypeMismatch, "object is not callable");
|
return context.env.raise(error.TypeMismatch, "object is not callable");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_clean(_: *RuntimeEnv, _: []coral.io.Byte) void {
|
fn default_clean(_: Context) void {
|
||||||
// Nothing to clean by default.
|
// Nothing to clean by default.
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_get(context: IndexContext) RuntimeError!?*RuntimeRef {
|
fn default_get(context: Context, _: *const RuntimeRef) RuntimeError!?*RuntimeRef {
|
||||||
return context.env.raise(error.TypeMismatch, "object is not indexable");
|
return context.env.raise(error.TypeMismatch, "object is not indexable");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_set(context: IndexContext, _: ?*const RuntimeRef) RuntimeError!void {
|
fn default_set(context: Context, _: *const RuntimeRef, _: ?*const RuntimeRef) RuntimeError!void {
|
||||||
return context.env.raise(error.TypeMismatch, "object is not indexable");
|
return context.env.raise(error.TypeMismatch, "object is not indexable");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_test_difference(context: TestContext) RuntimeError!Float {
|
fn default_test_difference(context: Context, _: *const RuntimeRef) RuntimeError!Float {
|
||||||
return context.env.raise(error.TypeMismatch, "object is not comparable");
|
return context.env.raise(error.TypeMismatch, "object is not comparable");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_test_equality(context: TestContext) RuntimeError!bool {
|
fn default_test_equality(context: Context, other_ref: *const RuntimeRef) RuntimeError!bool {
|
||||||
return switch (context.env.unbox(context.other_ref)) {
|
return switch (context.env.unbox(other_ref)) {
|
||||||
.dynamic => |dynamic| context.userdata.ptr == dynamic.userdata.ptr,
|
.dynamic => |dynamic| context.userdata.ptr == dynamic.userdata.ptr,
|
||||||
else => false,
|
else => false,
|
||||||
};
|
};
|
||||||
|
@ -414,7 +447,7 @@ pub const Typeinfo = struct {
|
||||||
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: []const ?*const RuntimeRef,
|
arg_refs: []const ?*const RuntimeRef,
|
||||||
) RuntimeError!?*RuntimeRef {
|
) RuntimeError!?*RuntimeRef {
|
||||||
for (arg_refs) |arg_ref| {
|
for (arg_refs) |arg_ref| {
|
||||||
|
@ -436,21 +469,20 @@ pub fn call(
|
||||||
|
|
||||||
pub fn get(
|
pub fn get(
|
||||||
env: *RuntimeEnv,
|
env: *RuntimeEnv,
|
||||||
indexable_ref: ?*const RuntimeRef,
|
indexable_ref: *const RuntimeRef,
|
||||||
index_ref: ?*const RuntimeRef,
|
index_ref: *const RuntimeRef,
|
||||||
) RuntimeError!?*RuntimeRef {
|
) RuntimeError!?*RuntimeRef {
|
||||||
const dynamic = try unbox_dynamic(env, indexable_ref);
|
const dynamic = try unbox_dynamic(env, indexable_ref);
|
||||||
|
|
||||||
return dynamic.typeinfo.get(.{
|
return dynamic.typeinfo.get(.{
|
||||||
.userdata = dynamic.userdata,
|
.userdata = dynamic.userdata,
|
||||||
.env = env,
|
.env = env,
|
||||||
.index_ref = index_ref,
|
}, index_ref);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_field(
|
pub fn get_field(
|
||||||
env: *RuntimeEnv,
|
env: *RuntimeEnv,
|
||||||
indexable_ref: ?*const RuntimeRef,
|
indexable_ref: *const RuntimeRef,
|
||||||
field_name: []const coral.io.Byte,
|
field_name: []const coral.io.Byte,
|
||||||
) RuntimeError!?*RuntimeRef {
|
) RuntimeError!?*RuntimeRef {
|
||||||
const field_name_ref = try env.new_string(field_name);
|
const field_name_ref = try env.new_string(field_name);
|
||||||
|
@ -462,8 +494,8 @@ pub fn get_field(
|
||||||
|
|
||||||
pub fn set(
|
pub fn set(
|
||||||
env: *RuntimeEnv,
|
env: *RuntimeEnv,
|
||||||
indexable_ref: ?*const RuntimeRef,
|
indexable_ref: *const RuntimeRef,
|
||||||
index_ref: ?*const RuntimeRef,
|
index_ref: *const RuntimeRef,
|
||||||
value_ref: ?*const RuntimeRef,
|
value_ref: ?*const RuntimeRef,
|
||||||
) RuntimeError!void {
|
) RuntimeError!void {
|
||||||
const dynamic = try unbox_dynamic(env, indexable_ref);
|
const dynamic = try unbox_dynamic(env, indexable_ref);
|
||||||
|
@ -477,7 +509,7 @@ pub fn set(
|
||||||
|
|
||||||
pub fn set_field(
|
pub fn set_field(
|
||||||
env: *RuntimeEnv,
|
env: *RuntimeEnv,
|
||||||
indexable_ref: ?*const RuntimeRef,
|
indexable_ref: *const RuntimeRef,
|
||||||
field_name: []const coral.io.Byte,
|
field_name: []const coral.io.Byte,
|
||||||
value_ref: ?*const RuntimeRef,
|
value_ref: ?*const RuntimeRef,
|
||||||
) RuntimeError!void {
|
) RuntimeError!void {
|
||||||
|
@ -490,9 +522,8 @@ pub fn set_field(
|
||||||
|
|
||||||
pub const new_table = Table.new;
|
pub const new_table = Table.new;
|
||||||
|
|
||||||
pub fn test_difference(env: *RuntimeEnv, lhs_ref: ?*const RuntimeRef, rhs_ref: ?*const RuntimeRef) RuntimeError!Float {
|
pub fn test_difference(env: *RuntimeEnv, lhs_ref: *const RuntimeRef, rhs_ref: *const RuntimeRef) RuntimeError!Float {
|
||||||
return switch (env.unbox(lhs_ref)) {
|
return switch (env.unbox(lhs_ref)) {
|
||||||
.nil => env.raise(error.TypeMismatch, "cannot compare nil objects"),
|
|
||||||
.boolean => env.raise(error.TypeMismatch, "cannot compare boolean objects"),
|
.boolean => env.raise(error.TypeMismatch, "cannot compare boolean objects"),
|
||||||
|
|
||||||
.number => |lhs_number| switch (env.unbox(rhs_ref)) {
|
.number => |lhs_number| switch (env.unbox(rhs_ref)) {
|
||||||
|
@ -502,21 +533,25 @@ pub fn test_difference(env: *RuntimeEnv, lhs_ref: ?*const RuntimeRef, rhs_ref: ?
|
||||||
|
|
||||||
.string => |lhs_string| switch (env.unbox(rhs_ref)) {
|
.string => |lhs_string| switch (env.unbox(rhs_ref)) {
|
||||||
.string => |rhs_string| @floatFromInt(coral.io.compare(lhs_string, rhs_string)),
|
.string => |rhs_string| @floatFromInt(coral.io.compare(lhs_string, rhs_string)),
|
||||||
|
.symbol => |rhs_symbol| @floatFromInt(coral.io.compare(lhs_string, rhs_symbol)),
|
||||||
else => env.raise(error.TypeMismatch, "right-hand object is not comparable with string objects"),
|
else => env.raise(error.TypeMismatch, "right-hand object is not comparable with string objects"),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.symbol => |lhs_symbol| switch (env.unbox(rhs_ref)) {
|
||||||
|
.symbol => env.raise(error.TypeMismatch, "cannot compare symbol objects"),
|
||||||
|
.string => |rhs_string| @floatFromInt(coral.io.compare(lhs_symbol, rhs_string)),
|
||||||
|
else => env.raise(error.TypeMismatch, "right-hand object is not comparable with symbol objects"),
|
||||||
|
},
|
||||||
|
|
||||||
.dynamic => |lhs_dynamic| lhs_dynamic.typeinfo.test_difference(.{
|
.dynamic => |lhs_dynamic| lhs_dynamic.typeinfo.test_difference(.{
|
||||||
.env = env,
|
.env = env,
|
||||||
.userdata = lhs_dynamic.userdata,
|
.userdata = lhs_dynamic.userdata,
|
||||||
.other_ref = rhs_ref,
|
}, rhs_ref),
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn test_equality(env: *RuntimeEnv, lhs_ref: ?*const RuntimeRef, rhs_ref: ?*const RuntimeRef) RuntimeError!bool {
|
pub fn test_equality(env: *RuntimeEnv, lhs_ref: *const RuntimeRef, rhs_ref: *const RuntimeRef) RuntimeError!bool {
|
||||||
return switch (env.unbox(lhs_ref)) {
|
return switch (env.unbox(lhs_ref)) {
|
||||||
.nil => (rhs_ref == null),
|
|
||||||
|
|
||||||
.boolean => |lhs_boolean| switch (env.unbox(rhs_ref)) {
|
.boolean => |lhs_boolean| switch (env.unbox(rhs_ref)) {
|
||||||
.boolean => |rhs_boolean| rhs_boolean == lhs_boolean,
|
.boolean => |rhs_boolean| rhs_boolean == lhs_boolean,
|
||||||
else => false,
|
else => false,
|
||||||
|
@ -529,21 +564,27 @@ pub fn test_equality(env: *RuntimeEnv, lhs_ref: ?*const RuntimeRef, rhs_ref: ?*c
|
||||||
|
|
||||||
.string => |lhs_string| switch (env.unbox(rhs_ref)) {
|
.string => |lhs_string| switch (env.unbox(rhs_ref)) {
|
||||||
.string => |rhs_string| coral.io.equals(lhs_string, rhs_string),
|
.string => |rhs_string| coral.io.equals(lhs_string, rhs_string),
|
||||||
|
.symbol => |rhs_symbol| coral.io.equals(lhs_string, rhs_symbol),
|
||||||
else => false,
|
else => false,
|
||||||
},
|
},
|
||||||
|
|
||||||
.dynamic => |lhs_dynamic| try lhs_dynamic.typeinfo.test_equality(.{
|
.symbol => |lhs_symbol| switch (env.unbox(rhs_ref)) {
|
||||||
|
.symbol => |rhs_symbol| lhs_symbol.ptr == rhs_symbol.ptr,
|
||||||
|
.string => |rhs_string| coral.io.equals(lhs_symbol, rhs_string),
|
||||||
|
else => false,
|
||||||
|
},
|
||||||
|
|
||||||
|
.dynamic => |lhs_dynamic| lhs_dynamic.typeinfo.test_equality(.{
|
||||||
.env = env,
|
.env = env,
|
||||||
.userdata = lhs_dynamic.userdata,
|
.userdata = lhs_dynamic.userdata,
|
||||||
.other_ref = rhs_ref,
|
}, rhs_ref),
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unbox_dynamic(env: *RuntimeEnv, ref: ?*const RuntimeRef) RuntimeError!*DynamicObject {
|
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");
|
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 {
|
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");
|
return env.unbox(ref).expect_string() orelse env.raise(error.TypeMismatch, "expected string object");
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,8 @@ pub const Expression = union (enum) {
|
||||||
false_literal,
|
false_literal,
|
||||||
number_literal: []const coral.io.Byte,
|
number_literal: []const coral.io.Byte,
|
||||||
string_literal: []const coral.io.Byte,
|
string_literal: []const coral.io.Byte,
|
||||||
table_literal: NamedList,
|
symbol_literal: []const coral.io.Byte,
|
||||||
|
table_literal: TableLiteral,
|
||||||
grouped_expression: *Expression,
|
grouped_expression: *Expression,
|
||||||
get_local: []const coral.io.Byte,
|
get_local: []const coral.io.Byte,
|
||||||
|
|
||||||
|
@ -61,9 +62,9 @@ pub const Expression = union (enum) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const NamedList = coral.list.Stack(struct {
|
pub const TableLiteral = coral.list.Stack(struct {
|
||||||
identifier: []const coral.io.Byte,
|
key_expression: Expression,
|
||||||
expression: Expression,
|
value_expression: Expression,
|
||||||
});
|
});
|
||||||
|
|
||||||
pub const List = coral.list.Stack(Expression);
|
pub const List = coral.list.Stack(Expression);
|
||||||
|
@ -397,7 +398,7 @@ fn parse_factor(self: *Self) ParseError!Expression {
|
||||||
},
|
},
|
||||||
|
|
||||||
.symbol_brace_left => {
|
.symbol_brace_left => {
|
||||||
var table_fields = Expression.NamedList.make(allocator);
|
var table_literal = Expression.TableLiteral.make(allocator);
|
||||||
|
|
||||||
self.tokenizer.skip(.newline);
|
self.tokenizer.skip(.newline);
|
||||||
|
|
||||||
|
@ -406,14 +407,30 @@ fn parse_factor(self: *Self) ParseError!Expression {
|
||||||
.symbol_brace_right => {
|
.symbol_brace_right => {
|
||||||
self.tokenizer.step();
|
self.tokenizer.step();
|
||||||
|
|
||||||
return Expression{.table_literal = table_fields};
|
return Expression{.table_literal = table_literal};
|
||||||
},
|
},
|
||||||
|
|
||||||
.identifier => |identifier| {
|
.symbol_bracket_left => {
|
||||||
self.tokenizer.skip(.newline);
|
self.tokenizer.skip(.newline);
|
||||||
|
|
||||||
if (!self.tokenizer.is_token(.symbol_equals)) {
|
if (!self.tokenizer.is_token(.symbol_equals)) {
|
||||||
return self.report("expected `=` after identifier");
|
return self.report("expected expression after identifier");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
.symbol_period => {
|
||||||
|
self.tokenizer.step();
|
||||||
|
|
||||||
|
if (!self.tokenizer.is_token(.identifier)) {
|
||||||
|
return self.report("expected identifier after `.`");
|
||||||
|
}
|
||||||
|
|
||||||
|
const identifier = self.tokenizer.token.?.identifier;
|
||||||
|
|
||||||
|
self.tokenizer.skip(.newline);
|
||||||
|
|
||||||
|
if (!self.tokenizer.is_token(.symbol_equals)) {
|
||||||
|
return self.report("expected `=` after key");
|
||||||
}
|
}
|
||||||
|
|
||||||
self.tokenizer.skip(.newline);
|
self.tokenizer.skip(.newline);
|
||||||
|
@ -422,9 +439,9 @@ fn parse_factor(self: *Self) ParseError!Expression {
|
||||||
return self.report("unexpected end after `=`");
|
return self.report("unexpected end after `=`");
|
||||||
}
|
}
|
||||||
|
|
||||||
try table_fields.push_one(.{
|
try table_literal.push_one(.{
|
||||||
.expression = try self.parse_expression(),
|
.value_expression = try self.parse_expression(),
|
||||||
.identifier = identifier,
|
.key_expression = .{.symbol_literal = identifier},
|
||||||
});
|
});
|
||||||
|
|
||||||
switch (self.tokenizer.token orelse return self.report("unexpected end of table")) {
|
switch (self.tokenizer.token orelse return self.report("unexpected end of table")) {
|
||||||
|
@ -433,7 +450,7 @@ fn parse_factor(self: *Self) ParseError!Expression {
|
||||||
.symbol_brace_right => {
|
.symbol_brace_right => {
|
||||||
self.tokenizer.step();
|
self.tokenizer.step();
|
||||||
|
|
||||||
return Expression{.table_literal = table_fields};
|
return Expression{.table_literal = table_literal};
|
||||||
},
|
},
|
||||||
|
|
||||||
else => return self.report("expected `,` or `}` after expression"),
|
else => return self.report("expected `,` or `}` after expression"),
|
||||||
|
|
|
@ -38,17 +38,18 @@ const AstCompiler = struct {
|
||||||
try self.chunk.append_opcode(.{.push_const = try self.chunk.declare_constant_string(literal)});
|
try self.chunk.append_opcode(.{.push_const = try self.chunk.declare_constant_string(literal)});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.symbol_literal => |literal| {
|
||||||
|
try self.chunk.append_opcode(.{.push_const = try self.chunk.declare_constant_symbol(literal)});
|
||||||
|
},
|
||||||
|
|
||||||
.table_literal => |fields| {
|
.table_literal => |fields| {
|
||||||
if (fields.values.len > coral.math.max_int(@typeInfo(u32).Int)) {
|
if (fields.values.len > coral.math.max_int(@typeInfo(u32).Int)) {
|
||||||
return error.OutOfMemory;
|
return error.OutOfMemory;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (fields.values) |field| {
|
for (fields.values) |field| {
|
||||||
try self.compile_expression(field.expression);
|
try self.compile_expression(field.value_expression);
|
||||||
|
try self.compile_expression(field.key_expression);
|
||||||
try self.chunk.append_opcode(.{
|
|
||||||
.push_const = try self.chunk.declare_constant_string(field.identifier),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try self.chunk.append_opcode(.{.push_table = @intCast(fields.values.len)});
|
try self.chunk.append_opcode(.{.push_table = @intCast(fields.values.len)});
|
||||||
|
@ -165,8 +166,6 @@ const AstCompiler = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Constant = u16;
|
|
||||||
|
|
||||||
const RefList = coral.list.Stack(?*kym.RuntimeRef);
|
const RefList = coral.list.Stack(?*kym.RuntimeRef);
|
||||||
|
|
||||||
const LocalsList = coral.list.Stack([]const u8);
|
const LocalsList = coral.list.Stack([]const u8);
|
||||||
|
@ -176,7 +175,7 @@ pub const Opcode = union (enum) {
|
||||||
push_nil,
|
push_nil,
|
||||||
push_true,
|
push_true,
|
||||||
push_false,
|
push_false,
|
||||||
push_const: Constant,
|
push_const: u16,
|
||||||
push_local: u8,
|
push_local: u8,
|
||||||
push_table: u32,
|
push_table: u32,
|
||||||
set_local: u8,
|
set_local: u8,
|
||||||
|
@ -223,7 +222,7 @@ pub fn compile_ast(self: *Self, ast: Ast) kym.RuntimeError!void {
|
||||||
try self.opcodes.pack();
|
try self.opcodes.pack();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn declare_constant_number(self: *Self, constant: kym.Float) kym.RuntimeError!Constant {
|
pub fn declare_constant_number(self: *Self, constant: kym.Float) kym.RuntimeError!u16 {
|
||||||
const tail = self.constant_refs.values.len;
|
const tail = self.constant_refs.values.len;
|
||||||
|
|
||||||
if (tail == coral.math.max_int(@typeInfo(u16).Int)) {
|
if (tail == coral.math.max_int(@typeInfo(u16).Int)) {
|
||||||
|
@ -239,7 +238,7 @@ pub fn declare_constant_number(self: *Self, constant: kym.Float) kym.RuntimeErro
|
||||||
return @intCast(tail);
|
return @intCast(tail);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn declare_constant_string(self: *Self, constant: []const coral.io.Byte) kym.RuntimeError!Constant {
|
pub fn declare_constant_string(self: *Self, constant: []const coral.io.Byte) kym.RuntimeError!u16 {
|
||||||
const tail = self.constant_refs.values.len;
|
const tail = self.constant_refs.values.len;
|
||||||
|
|
||||||
if (tail == coral.math.max_int(@typeInfo(u16).Int)) {
|
if (tail == coral.math.max_int(@typeInfo(u16).Int)) {
|
||||||
|
@ -255,6 +254,22 @@ pub fn declare_constant_string(self: *Self, constant: []const coral.io.Byte) kym
|
||||||
return @intCast(tail);
|
return @intCast(tail);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn declare_constant_symbol(self: *Self, constant: []const coral.io.Byte) kym.RuntimeError!u16 {
|
||||||
|
const tail = self.constant_refs.values.len;
|
||||||
|
|
||||||
|
if (tail == coral.math.max_int(@typeInfo(u16).Int)) {
|
||||||
|
return self.env.raise(error.BadSyntax, "functions may contain a maximum of 65,535 constants");
|
||||||
|
}
|
||||||
|
|
||||||
|
const constant_ref = try self.env.new_symbol(constant);
|
||||||
|
|
||||||
|
errdefer self.env.discard(constant_ref);
|
||||||
|
|
||||||
|
try self.constant_refs.push_one(constant_ref);
|
||||||
|
|
||||||
|
return @intCast(tail);
|
||||||
|
}
|
||||||
|
|
||||||
fn execute(self: *Self, env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeRef {
|
fn execute(self: *Self, env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeRef {
|
||||||
for (self.opcodes.values) |opcode| {
|
for (self.opcodes.values) |opcode| {
|
||||||
switch (opcode) {
|
switch (opcode) {
|
||||||
|
@ -274,7 +289,9 @@ fn execute(self: *Self, env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeRef
|
||||||
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 = try env.pop_local();
|
const index_ref = try env.pop_local() orelse {
|
||||||
|
return env.raise(error.TypeMismatch, "nil is not a valid index");
|
||||||
|
};
|
||||||
|
|
||||||
defer env.discard(index_ref);
|
defer env.discard(index_ref);
|
||||||
|
|
||||||
|
@ -285,8 +302,7 @@ fn execute(self: *Self, env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeRef
|
||||||
try dynamic.typeinfo.set(.{
|
try dynamic.typeinfo.set(.{
|
||||||
.userdata = dynamic.userdata,
|
.userdata = dynamic.userdata,
|
||||||
.env = env,
|
.env = env,
|
||||||
.index_ref = index_ref,
|
}, index_ref, value_ref);
|
||||||
}, value_ref);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,7 +327,9 @@ fn execute(self: *Self, env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeRef
|
||||||
|
|
||||||
.call => |arg_count| {
|
.call => |arg_count| {
|
||||||
const result_ref = call: {
|
const result_ref = call: {
|
||||||
const callable_ref = try env.pop_local();
|
const callable_ref = try env.pop_local() orelse {
|
||||||
|
return env.raise(error.TypeMismatch, "nil is not callable");
|
||||||
|
};
|
||||||
|
|
||||||
defer env.discard(callable_ref);
|
defer env.discard(callable_ref);
|
||||||
|
|
||||||
|
@ -325,7 +343,9 @@ fn execute(self: *Self, env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeRef
|
||||||
|
|
||||||
.syscall => |arg_count| {
|
.syscall => |arg_count| {
|
||||||
const result_ref = call: {
|
const result_ref = call: {
|
||||||
const identifier_ref = try env.pop_local();
|
const identifier_ref = try env.pop_local() orelse {
|
||||||
|
return env.raise(error.TypeMismatch, "nil is not syscallable");
|
||||||
|
};
|
||||||
|
|
||||||
defer env.discard(identifier_ref);
|
defer env.discard(identifier_ref);
|
||||||
|
|
||||||
|
@ -339,22 +359,42 @@ fn execute(self: *Self, env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeRef
|
||||||
try env.push_ref(result_ref);
|
try env.push_ref(result_ref);
|
||||||
},
|
},
|
||||||
|
|
||||||
.neg => try env.push_number(switch (env.unbox(try env.pop_local())) {
|
.neg => {
|
||||||
|
const ref = (try env.pop_local()) orelse {
|
||||||
|
return env.raise(error.TypeMismatch, "null is not scalar negatable");
|
||||||
|
};
|
||||||
|
|
||||||
|
defer env.discard(ref);
|
||||||
|
|
||||||
|
try env.push_number(switch (env.unbox(ref)) {
|
||||||
.number => |number| -number,
|
.number => |number| -number,
|
||||||
else => return env.raise(error.TypeMismatch, "object is not scalar negatable"),
|
else => return env.raise(error.TypeMismatch, "object is not scalar negatable"),
|
||||||
}),
|
});
|
||||||
|
},
|
||||||
|
|
||||||
.not => try env.push_boolean(switch (env.unbox(try env.pop_local())) {
|
.not => {
|
||||||
|
const ref = (try env.pop_local()) orelse {
|
||||||
|
return env.raise(error.TypeMismatch, "null is not boolean negatable");
|
||||||
|
};
|
||||||
|
|
||||||
|
defer env.discard(ref);
|
||||||
|
|
||||||
|
try env.push_boolean(switch (env.unbox(ref)) {
|
||||||
.boolean => |boolean| !boolean,
|
.boolean => |boolean| !boolean,
|
||||||
else => return 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 = try env.pop_local();
|
const rhs_ref = (try env.pop_local()) orelse {
|
||||||
|
return env.raise(error.TypeMismatch, "nil is not addable");
|
||||||
|
};
|
||||||
|
|
||||||
defer env.discard(rhs_ref);
|
defer env.discard(rhs_ref);
|
||||||
|
|
||||||
const lhs_ref = try env.pop_local();
|
const lhs_ref = (try env.pop_local()) orelse {
|
||||||
|
return env.raise(error.TypeMismatch, "nil is not addable");
|
||||||
|
};
|
||||||
|
|
||||||
defer env.discard(lhs_ref);
|
defer env.discard(lhs_ref);
|
||||||
|
|
||||||
|
@ -369,11 +409,15 @@ fn execute(self: *Self, env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeRef
|
||||||
},
|
},
|
||||||
|
|
||||||
.sub => {
|
.sub => {
|
||||||
const rhs_ref = try env.pop_local();
|
const rhs_ref = (try env.pop_local()) orelse {
|
||||||
|
return env.raise(error.TypeMismatch, "nil is not subtractable");
|
||||||
|
};
|
||||||
|
|
||||||
defer env.discard(rhs_ref);
|
defer env.discard(rhs_ref);
|
||||||
|
|
||||||
const lhs_ref = try env.pop_local();
|
const lhs_ref = (try env.pop_local()) orelse {
|
||||||
|
return env.raise(error.TypeMismatch, "nil is not subtractable");
|
||||||
|
};
|
||||||
|
|
||||||
defer env.discard(lhs_ref);
|
defer env.discard(lhs_ref);
|
||||||
|
|
||||||
|
@ -388,11 +432,15 @@ fn execute(self: *Self, env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeRef
|
||||||
},
|
},
|
||||||
|
|
||||||
.mul => {
|
.mul => {
|
||||||
const rhs_ref = try env.pop_local();
|
const rhs_ref = (try env.pop_local()) orelse {
|
||||||
|
return env.raise(error.TypeMismatch, "nil is not multiplicable");
|
||||||
|
};
|
||||||
|
|
||||||
defer env.discard(rhs_ref);
|
defer env.discard(rhs_ref);
|
||||||
|
|
||||||
const lhs_ref = try env.pop_local();
|
const lhs_ref = (try env.pop_local()) orelse {
|
||||||
|
return env.raise(error.TypeMismatch, "nil is not multiplicable");
|
||||||
|
};
|
||||||
|
|
||||||
defer env.discard(lhs_ref);
|
defer env.discard(lhs_ref);
|
||||||
|
|
||||||
|
@ -407,11 +455,15 @@ fn execute(self: *Self, env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeRef
|
||||||
},
|
},
|
||||||
|
|
||||||
.div => {
|
.div => {
|
||||||
const rhs_ref = try env.pop_local();
|
const rhs_ref = (try env.pop_local()) orelse {
|
||||||
|
return env.raise(error.TypeMismatch, "nil is not divisible");
|
||||||
|
};
|
||||||
|
|
||||||
defer env.discard(rhs_ref);
|
defer env.discard(rhs_ref);
|
||||||
|
|
||||||
const lhs_ref = try env.pop_local();
|
const lhs_ref = (try env.pop_local()) orelse {
|
||||||
|
return env.raise(error.TypeMismatch, "nil is not divisible");
|
||||||
|
};
|
||||||
|
|
||||||
defer env.discard(lhs_ref);
|
defer env.discard(lhs_ref);
|
||||||
|
|
||||||
|
@ -426,11 +478,15 @@ fn execute(self: *Self, env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeRef
|
||||||
},
|
},
|
||||||
|
|
||||||
.eql => {
|
.eql => {
|
||||||
const rhs_ref = try env.pop_local();
|
const rhs_ref = try env.pop_local() orelse {
|
||||||
|
return env.raise(error.TypeMismatch, "nil is not equatable");
|
||||||
|
};
|
||||||
|
|
||||||
defer env.discard(rhs_ref);
|
defer env.discard(rhs_ref);
|
||||||
|
|
||||||
const lhs_ref = try env.pop_local();
|
const lhs_ref = try env.pop_local() orelse {
|
||||||
|
return env.raise(error.TypeMismatch, "nil is not equatable");
|
||||||
|
};
|
||||||
|
|
||||||
defer env.discard(lhs_ref);
|
defer env.discard(lhs_ref);
|
||||||
|
|
||||||
|
@ -438,11 +494,15 @@ fn execute(self: *Self, env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeRef
|
||||||
},
|
},
|
||||||
|
|
||||||
.cgt => {
|
.cgt => {
|
||||||
const rhs_ref = try env.pop_local();
|
const rhs_ref = try env.pop_local() orelse {
|
||||||
|
return env.raise(error.TypeMismatch, "nil is not comparable");
|
||||||
|
};
|
||||||
|
|
||||||
defer env.discard(rhs_ref);
|
defer env.discard(rhs_ref);
|
||||||
|
|
||||||
const lhs_ref = try env.pop_local();
|
const lhs_ref = try env.pop_local() orelse {
|
||||||
|
return env.raise(error.TypeMismatch, "nil is not comparable");
|
||||||
|
};
|
||||||
|
|
||||||
defer env.discard(lhs_ref);
|
defer env.discard(lhs_ref);
|
||||||
|
|
||||||
|
@ -450,11 +510,15 @@ fn execute(self: *Self, env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeRef
|
||||||
},
|
},
|
||||||
|
|
||||||
.clt => {
|
.clt => {
|
||||||
const rhs_ref = try env.pop_local();
|
const rhs_ref = try env.pop_local() orelse {
|
||||||
|
return env.raise(error.TypeMismatch, "nil is not comparable");
|
||||||
|
};
|
||||||
|
|
||||||
defer env.discard(rhs_ref);
|
defer env.discard(rhs_ref);
|
||||||
|
|
||||||
const lhs_ref = try env.pop_local();
|
const lhs_ref = try env.pop_local() orelse {
|
||||||
|
return env.raise(error.TypeMismatch, "nil is not comparable");
|
||||||
|
};
|
||||||
|
|
||||||
defer env.discard(lhs_ref);
|
defer env.discard(lhs_ref);
|
||||||
|
|
||||||
|
@ -462,11 +526,15 @@ fn execute(self: *Self, env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeRef
|
||||||
},
|
},
|
||||||
|
|
||||||
.cge => {
|
.cge => {
|
||||||
const rhs_ref = try env.pop_local();
|
const rhs_ref = try env.pop_local() orelse {
|
||||||
|
return env.raise(error.TypeMismatch, "nil is not comparable");
|
||||||
|
};
|
||||||
|
|
||||||
defer env.discard(rhs_ref);
|
defer env.discard(rhs_ref);
|
||||||
|
|
||||||
const lhs_ref = try env.pop_local();
|
const lhs_ref = try env.pop_local() orelse {
|
||||||
|
return env.raise(error.TypeMismatch, "nil is not comparable");
|
||||||
|
};
|
||||||
|
|
||||||
defer env.discard(lhs_ref);
|
defer env.discard(lhs_ref);
|
||||||
|
|
||||||
|
@ -474,11 +542,15 @@ fn execute(self: *Self, env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeRef
|
||||||
},
|
},
|
||||||
|
|
||||||
.cle => {
|
.cle => {
|
||||||
const rhs_ref = try env.pop_local();
|
const rhs_ref = try env.pop_local() orelse {
|
||||||
|
return env.raise(error.TypeMismatch, "nil is not comparable");
|
||||||
|
};
|
||||||
|
|
||||||
defer env.discard(rhs_ref);
|
defer env.discard(rhs_ref);
|
||||||
|
|
||||||
const lhs_ref = try env.pop_local();
|
const lhs_ref = try env.pop_local() orelse {
|
||||||
|
return env.raise(error.TypeMismatch, "nil is not comparable");
|
||||||
|
};
|
||||||
|
|
||||||
defer env.discard(lhs_ref);
|
defer env.discard(lhs_ref);
|
||||||
|
|
||||||
|
|
|
@ -2,22 +2,43 @@ const coral = @import("coral");
|
||||||
|
|
||||||
const kym = @import("../kym.zig");
|
const kym = @import("../kym.zig");
|
||||||
|
|
||||||
fields: FieldTable,
|
associative: AssociativeTable,
|
||||||
|
contiguous: ContiguousList,
|
||||||
|
|
||||||
const FieldTable = coral.map.StringTable(struct {
|
// TODO: Modify hash traits to be support fat contexts rather than passing Envs into the table.
|
||||||
key_ref: ?*kym.RuntimeRef,
|
const AssociativeTable = coral.map.Table(Field, *kym.RuntimeRef, .{
|
||||||
value_ref: ?*kym.RuntimeRef,
|
.hash = Field.hash,
|
||||||
|
.match = Field.match,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const ContiguousList = coral.list.Stack(?*kym.RuntimeRef);
|
||||||
|
|
||||||
|
const Field = struct {
|
||||||
|
env: *kym.RuntimeEnv,
|
||||||
|
ref: *kym.RuntimeRef,
|
||||||
|
|
||||||
|
fn hash(field: Field) usize {
|
||||||
|
return switch (field.env.unbox(field.ref)) {
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn match(self: Field, other: Field) bool {
|
||||||
|
return kym.test_equality(self.env, self.ref, other.ref);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
pub fn new(env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeRef {
|
pub fn new(env: *kym.RuntimeEnv) kym.RuntimeError!*kym.RuntimeRef {
|
||||||
var self = Self{
|
var self = Self{
|
||||||
.fields = FieldTable.make(env.allocator),
|
.associative = AssociativeTable.make(env.allocator),
|
||||||
|
.contiguous = ContiguousList.make(env.allocator),
|
||||||
};
|
};
|
||||||
|
|
||||||
errdefer {
|
errdefer {
|
||||||
self.fields.free();
|
self.associative.free();
|
||||||
|
self.contiguous.free();
|
||||||
}
|
}
|
||||||
|
|
||||||
return try env.new_dynamic(coral.io.bytes_of(&self), &typeinfo);
|
return try env.new_dynamic(coral.io.bytes_of(&self), &typeinfo);
|
||||||
|
@ -30,62 +51,109 @@ const typeinfo = kym.Typeinfo{
|
||||||
.set = typeinfo_set,
|
.set = typeinfo_set,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn typeinfo_clean(env: *kym.RuntimeEnv, userdata: []coral.io.Byte) void {
|
fn typeinfo_clean(context: kym.Context) void {
|
||||||
const table = @as(*Self, @ptrCast(@alignCast(userdata.ptr)));
|
const table = @as(*Self, @ptrCast(@alignCast(context.userdata.ptr)));
|
||||||
|
|
||||||
{
|
{
|
||||||
var field_iterable = table.fields.as_iterable();
|
var field_iterable = table.associative.as_iterable();
|
||||||
|
|
||||||
while (field_iterable.next()) |entry| {
|
while (field_iterable.next()) |entry| {
|
||||||
env.discard(entry.value.key_ref);
|
context.env.discard(entry.key.ref);
|
||||||
env.discard(entry.value.value_ref);
|
context.env.discard(entry.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
table.fields.free();
|
table.associative.free();
|
||||||
|
|
||||||
|
while (table.contiguous.pop()) |ref| {
|
||||||
|
context.env.discard(ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn typeinfo_get(context: kym.IndexContext) kym.RuntimeError!?*kym.RuntimeRef {
|
table.contiguous.free();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn typeinfo_get(context: kym.Context, index_ref: *const kym.RuntimeRef) kym.RuntimeError!?*kym.RuntimeRef {
|
||||||
const table = @as(*Self, @ptrCast(@alignCast(context.userdata.ptr)));
|
const table = @as(*Self, @ptrCast(@alignCast(context.userdata.ptr)));
|
||||||
|
|
||||||
return switch (context.env.unbox(context.index_ref)) {
|
switch (context.env.unbox(index_ref)) {
|
||||||
.string => |string| context.env.acquire((table.fields.lookup(string) orelse return null).value_ref),
|
.symbol, .string => {
|
||||||
// TODO: Implement number indices in tables.
|
return context.env.acquire(table.associative.lookup(index_ref));
|
||||||
.number => |_| unreachable,
|
},
|
||||||
else => context.env.raise(error.TypeMismatch, "table objects may only be indexed with strings or numbers"),
|
|
||||||
};
|
.number => |number| {
|
||||||
|
// TODO: Implement dedicated integer type within VM internals for this sort of indexing.
|
||||||
|
if (@trunc(number) == number) {
|
||||||
|
const index = @as(usize, @intFromFloat(number));
|
||||||
|
|
||||||
|
if (index < table.contiguous.values.len) {
|
||||||
|
return context.env.acquire(table.contiguous.values[index] orelse return null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn typeinfo_set(context: kym.IndexContext, value_ref: ?*const kym.RuntimeRef) kym.RuntimeError!void {
|
return context.env.acquire(table.associative.lookup(.{
|
||||||
|
.env = context.env,
|
||||||
|
.ref = index_ref,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
else => return context.env.raise(error.TypeMismatch, "expected symbol, string, or number index"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn typeinfo_set(context: kym.Context, index_ref: *const kym.RuntimeRef, value_ref: ?*const kym.RuntimeRef) kym.RuntimeError!void {
|
||||||
const table = @as(*Self, @ptrCast(@alignCast(context.userdata.ptr)));
|
const table = @as(*Self, @ptrCast(@alignCast(context.userdata.ptr)));
|
||||||
|
|
||||||
|
switch (context.env.unbox(context.index_ref)) {
|
||||||
|
.symbol, .string => {
|
||||||
|
const acquired_index_ref = context.env.acquire(index_ref);
|
||||||
|
|
||||||
|
errdefer context.env.discard(acquired_index_ref);
|
||||||
|
|
||||||
const acquired_value_ref = context.env.acquire(value_ref);
|
const acquired_value_ref = context.env.acquire(value_ref);
|
||||||
|
|
||||||
errdefer context.env.discard(acquired_value_ref);
|
errdefer context.env.discard(acquired_value_ref);
|
||||||
|
|
||||||
switch (context.env.unbox(context.index_ref)) {
|
if (try table.associative.replace(acquired_index_ref, acquired_value_ref)) |replaced| {
|
||||||
.string => |string| {
|
context.env.discard(replaced.key);
|
||||||
const acquired_index_ref = context.env.acquire(context.index_ref);
|
context.env.discard(replaced.value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
.number => |number| {
|
||||||
|
const index = coral.math.clamped_cast(@typeInfo(usize), number);
|
||||||
|
|
||||||
|
if (index == number) {
|
||||||
|
if (index < table.contiguous.values.len) {
|
||||||
|
table.contiguous.values[index] = context.env.acquire(value_ref);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index == table.contiguous.values.len) {
|
||||||
|
const acquired_index_ref = context.env.acquire(value_ref);
|
||||||
|
|
||||||
errdefer context.env.discard(acquired_index_ref);
|
errdefer context.env.discard(acquired_index_ref);
|
||||||
|
|
||||||
var displaced_table_entry = if (acquired_value_ref) |ref| try table.fields.replace(string, .{
|
try table.contiguous.push_one(acquired_index_ref);
|
||||||
.key_ref = acquired_index_ref,
|
|
||||||
.value_ref = ref,
|
|
||||||
}) else table.fields.remove(string);
|
|
||||||
|
|
||||||
if (displaced_table_entry) |*entry| {
|
return;
|
||||||
context.env.discard(entry.value.key_ref);
|
}
|
||||||
context.env.discard(entry.value.value_ref);
|
}
|
||||||
|
|
||||||
|
const acquired_index_ref = context.env.acquire(index_ref);
|
||||||
|
|
||||||
|
errdefer context.env.discard(index_ref);
|
||||||
|
|
||||||
|
const acquired_value_ref = context.env.acquire(value_ref);
|
||||||
|
|
||||||
|
errdefer context.env.discard(value_ref);
|
||||||
|
|
||||||
|
if (try table.associative.replace(acquired_index_ref, acquired_value_ref)) |replaced| {
|
||||||
|
context.env.discard(replaced.key);
|
||||||
|
context.env.discard(replaced.value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
.number => |_| {
|
else => return context.env.raise(error.TypeMismatch, "expected symbol, string, or number index"),
|
||||||
// TODO: Implement number indices in tables.
|
|
||||||
unreachable;
|
|
||||||
},
|
|
||||||
|
|
||||||
else => {
|
|
||||||
return context.env.raise(error.TypeMismatch, "table objects may only be indexed with strings or numbers");
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,19 +27,25 @@ 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 kym.unbox_string(env, try env.view_arg(0)));
|
if (try env.view_arg(0)) |arg| {
|
||||||
|
app.log_info(try kym.unbox_string(env, arg));
|
||||||
|
}
|
||||||
|
|
||||||
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 kym.unbox_string(env, try env.view_arg(0)));
|
if (try env.view_arg(0)) |arg| {
|
||||||
|
app.log_warn(try kym.unbox_string(env, arg));
|
||||||
|
}
|
||||||
|
|
||||||
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 kym.unbox_string(env, try env.view_arg(0)));
|
if (try env.view_arg(0)) |arg| {
|
||||||
|
app.log_fail(try kym.unbox_string(env, arg));
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue