Replace globals with native call interface
This commit is contained in:
parent
84e5236ba1
commit
1ea115b277
|
@ -1,8 +1,8 @@
|
||||||
|
|
||||||
title = "Afterglow"
|
@log_info("game is loading")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title = title,
|
title = "Afterglow",
|
||||||
width = 1280,
|
width = 1280,
|
||||||
height = 800,
|
height = 800,
|
||||||
tick_rate = 60,
|
tick_rate = 60,
|
||||||
|
|
|
@ -116,10 +116,7 @@ pub fn Functor(comptime Output: type, comptime Input: type) type {
|
||||||
pub fn bind(comptime State: type, state: *const State, comptime invoker: fn (capture: *const State, input: Input) Output) Self {
|
pub fn bind(comptime State: type, state: *const State, comptime invoker: fn (capture: *const State, input: Input) Output) Self {
|
||||||
const is_zero_aligned = @alignOf(State) == 0;
|
const is_zero_aligned = @alignOf(State) == 0;
|
||||||
|
|
||||||
return .{
|
const Invoker = struct {
|
||||||
.context = if (is_zero_aligned) state else @ptrCast(state),
|
|
||||||
|
|
||||||
.invoker = struct {
|
|
||||||
fn invoke(context: *const anyopaque, input: Input) Output {
|
fn invoke(context: *const anyopaque, input: Input) Output {
|
||||||
if (is_zero_aligned) {
|
if (is_zero_aligned) {
|
||||||
return invoker(@ptrCast(context), input);
|
return invoker(@ptrCast(context), input);
|
||||||
|
@ -127,7 +124,24 @@ pub fn Functor(comptime Output: type, comptime Input: type) type {
|
||||||
|
|
||||||
return invoker(@ptrCast(@alignCast(context)), input);
|
return invoker(@ptrCast(@alignCast(context)), input);
|
||||||
}
|
}
|
||||||
}.invoke,
|
};
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.context = if (is_zero_aligned) state else @ptrCast(state),
|
||||||
|
.invoker = Invoker.invoke,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from(comptime invoker: fn (input: Input) Output) Self {
|
||||||
|
const Invoker = struct {
|
||||||
|
fn invoke(_: *const anyopaque, input: Input) Output {
|
||||||
|
return invoker(input);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.context = &.{},
|
||||||
|
.invoker = Invoker.invoke,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,6 +176,19 @@ pub fn Generator(comptime Output: type, comptime Input: type) type {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from(comptime invoker: fn (input: Input) Output) Self {
|
||||||
|
const Invoker = struct {
|
||||||
|
fn invoke(_: *const anyopaque, input: Input) Output {
|
||||||
|
return invoker(input);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.context = &.{},
|
||||||
|
.invoker = Invoker.invoke,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn invoke(self: Self, input: Input) Output {
|
pub fn invoke(self: Self, input: Input) Output {
|
||||||
return self.invoker(self.context, input);
|
return self.invoker(self.context, input);
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,7 +78,7 @@ pub fn Stack(comptime Value: type) type {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return &self.values[self.values.len - 1];
|
return self.values[self.values.len - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop(self: *Self) ?Value {
|
pub fn pop(self: *Self) ?Value {
|
||||||
|
@ -93,6 +93,22 @@ pub fn Stack(comptime Value: type) type {
|
||||||
return self.values[last_index];
|
return self.values[last_index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn push_all(self: *Self, values: []const Value) io.AllocationError!void {
|
||||||
|
const new_length = self.values.len + values.len;
|
||||||
|
|
||||||
|
if (new_length > self.capacity) {
|
||||||
|
try self.grow(values.len + values.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
const offset_index = self.values.len;
|
||||||
|
|
||||||
|
self.values = self.values.ptr[0 .. new_length];
|
||||||
|
|
||||||
|
for (0 .. values.len) |index| {
|
||||||
|
self.values[offset_index + index] = values[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn push_one(self: *Self, value: Value) io.AllocationError!void {
|
pub fn push_one(self: *Self, value: Value) io.AllocationError!void {
|
||||||
if (self.values.len == self.capacity) {
|
if (self.values.len == self.capacity) {
|
||||||
try self.grow(math.max(1, self.capacity));
|
try self.grow(math.max(1, self.capacity));
|
||||||
|
@ -106,3 +122,13 @@ pub fn Stack(comptime Value: type) type {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn stack_as_writer(self: *ByteStack) io.Writer {
|
||||||
|
return io.Writer.bind(ByteStack, self, write_stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_stack(stack: *ByteStack, bytes: []const io.Byte) ?usize {
|
||||||
|
stack.push_all(bytes) catch return null;
|
||||||
|
|
||||||
|
return bytes.len;
|
||||||
|
}
|
||||||
|
|
|
@ -103,6 +103,13 @@ pub fn Slab(comptime Value: type) type {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn StringTable(comptime Value: type) type {
|
||||||
|
return Table([]const io.Byte, Value, .{
|
||||||
|
.hash = hash_string,
|
||||||
|
.match = io.equals,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn Table(comptime Key: type, comptime Value: type, comptime traits: TableTraits(Key)) type {
|
pub fn Table(comptime Key: type, comptime Value: type, comptime traits: TableTraits(Key)) type {
|
||||||
const load_max = 0.75;
|
const load_max = 0.75;
|
||||||
|
|
||||||
|
@ -143,7 +150,7 @@ pub fn Table(comptime Key: type, comptime Value: type, comptime traits: TableTra
|
||||||
|
|
||||||
pub const Iterable = struct {
|
pub const Iterable = struct {
|
||||||
table: *Self,
|
table: *Self,
|
||||||
iterations: usize = 0,
|
iterations: usize,
|
||||||
|
|
||||||
pub fn next(self: *Iterable) ?Entry {
|
pub fn next(self: *Iterable) ?Entry {
|
||||||
while (self.iterations < self.table.entries.len) {
|
while (self.iterations < self.table.entries.len) {
|
||||||
|
@ -160,6 +167,13 @@ pub fn Table(comptime Key: type, comptime Value: type, comptime traits: TableTra
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
|
pub fn as_iterable(self: *Self) Iterable {
|
||||||
|
return .{
|
||||||
|
.table = self,
|
||||||
|
.iterations = 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn remove(self: *Self, key: Key) ?Entry {
|
pub fn remove(self: *Self, key: Key) ?Entry {
|
||||||
const hash_max = math.min(math.max_int(@typeInfo(usize).Int), self.entries.len);
|
const hash_max = math.min(math.max_int(@typeInfo(usize).Int), self.entries.len);
|
||||||
var hashed_key = math.wrap(traits.hash(key), math.min_int(@typeInfo(usize).Int), hash_max);
|
var hashed_key = math.wrap(traits.hash(key), math.min_int(@typeInfo(usize).Int), hash_max);
|
||||||
|
@ -332,8 +346,3 @@ fn hash_string(key: []const io.Byte) usize {
|
||||||
|
|
||||||
return hash_code;
|
return hash_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const string_table_traits = TableTraits([]const io.Byte){
|
|
||||||
.hash = hash_string,
|
|
||||||
.match = io.equals,
|
|
||||||
};
|
|
||||||
|
|
|
@ -12,14 +12,14 @@ pub const Manifest = struct {
|
||||||
height: u16 = 480,
|
height: u16 = 480,
|
||||||
tick_rate: f32 = 60.0,
|
tick_rate: f32 = 60.0,
|
||||||
|
|
||||||
pub fn load(self: *Manifest, env: *kym.RuntimeEnv, global_ref: ?*const kym.RuntimeRef, file_access: file.Access) kym.RuntimeError!void {
|
pub fn load(self: *Manifest, env: *kym.RuntimeEnv, file_access: file.Access) kym.RuntimeError!void {
|
||||||
var manifest_ref = try env.execute_file(global_ref, file_access, file.Path.from(&.{"app.ona"}));
|
const manifest_ref = try env.execute_file(file_access, file.Path.from(&.{"app.ona"}));
|
||||||
|
|
||||||
defer env.discard(&manifest_ref);
|
defer env.discard(manifest_ref);
|
||||||
|
|
||||||
var title_ref = try kym.get_dynamic_field(env, manifest_ref, "title");
|
const title_ref = try kym.get_field(env, manifest_ref, "title");
|
||||||
|
|
||||||
defer env.discard(&title_ref);
|
defer env.discard(title_ref);
|
||||||
|
|
||||||
const title_string = switch (env.unbox(title_ref)) {
|
const title_string = switch (env.unbox(title_ref)) {
|
||||||
.string => |string| string,
|
.string => |string| string,
|
||||||
|
@ -27,9 +27,9 @@ pub const Manifest = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
const width = @as(u16, get: {
|
const width = @as(u16, get: {
|
||||||
var ref = try kym.get_dynamic_field(env, manifest_ref, "width");
|
const ref = try kym.get_field(env, manifest_ref, "width");
|
||||||
|
|
||||||
defer env.discard(&ref);
|
defer env.discard(ref);
|
||||||
|
|
||||||
// TODO: Add safety-checks to int cast.
|
// TODO: Add safety-checks to int cast.
|
||||||
break: get switch (env.unbox(ref)) {
|
break: get switch (env.unbox(ref)) {
|
||||||
|
@ -39,9 +39,9 @@ pub const Manifest = struct {
|
||||||
});
|
});
|
||||||
|
|
||||||
const height = @as(u16, get: {
|
const height = @as(u16, get: {
|
||||||
var ref = try kym.get_dynamic_field(env, manifest_ref, "height");
|
const ref = try kym.get_field(env, manifest_ref, "height");
|
||||||
|
|
||||||
defer env.discard(&ref);
|
defer env.discard(ref);
|
||||||
|
|
||||||
// TODO: Add safety-checks to int cast.
|
// TODO: Add safety-checks to int cast.
|
||||||
break: get switch (env.unbox(ref)) {
|
break: get switch (env.unbox(ref)) {
|
||||||
|
@ -51,9 +51,9 @@ pub const Manifest = struct {
|
||||||
});
|
});
|
||||||
|
|
||||||
const tick_rate = @as(f32, get: {
|
const tick_rate = @as(f32, get: {
|
||||||
var ref = try kym.get_dynamic_field(env, manifest_ref, "tick_rate");
|
const ref = try kym.get_field(env, manifest_ref, "tick_rate");
|
||||||
|
|
||||||
defer env.discard(&ref);
|
defer env.discard(ref);
|
||||||
|
|
||||||
break: get switch (env.unbox(ref)) {
|
break: get switch (env.unbox(ref)) {
|
||||||
.number => |number| @floatCast(number),
|
.number => |number| @floatCast(number),
|
||||||
|
@ -74,69 +74,14 @@ pub const Manifest = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const LogSeverity = enum {
|
pub fn log_info(message: []const coral.io.Byte) void {
|
||||||
info,
|
ext.SDL_LogInfo(ext.SDL_LOG_CATEGORY_APPLICATION, "%.*s", @as(c_int, @intCast(message.len)), message.ptr);
|
||||||
warn,
|
|
||||||
fail,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const WritableLog = struct {
|
|
||||||
severity: LogSeverity,
|
|
||||||
write_buffer: coral.list.ByteStack,
|
|
||||||
|
|
||||||
pub fn as_writer(self: *WritableLog) coral.io.Writer {
|
|
||||||
return coral.io.Writer.bind(WritableLog, self, struct {
|
|
||||||
fn write(writable_log: *WritableLog, bytes: []const coral.io.Byte) ?usize {
|
|
||||||
writable_log.write(bytes) catch return null;
|
|
||||||
|
|
||||||
return bytes.len;
|
|
||||||
}
|
|
||||||
}.write);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn free(self: *WritableLog) void {
|
pub fn log_warn(message: []const coral.io.Byte) void {
|
||||||
self.write_buffer.free();
|
ext.SDL_LogWarn(ext.SDL_LOG_CATEGORY_APPLICATION, "%.*s", @as(c_int, @intCast(message.len)), message.ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make(log_severity: LogSeverity, allocator: coral.io.Allocator) WritableLog {
|
pub fn log_fail(message: []const coral.io.Byte) void {
|
||||||
return .{
|
ext.SDL_LogError(ext.SDL_LOG_CATEGORY_APPLICATION, "%.*s", @as(c_int, @intCast(message.len)), message.ptr);
|
||||||
.severity = log_severity,
|
|
||||||
.write_buffer = coral.list.ByteStack.make(allocator),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(self: *WritableLog, bytes: []const coral.io.Byte) coral.io.AllocationError!void {
|
|
||||||
const format_string = "%.*s";
|
|
||||||
var line_written = @as(usize, 0);
|
|
||||||
|
|
||||||
for (bytes) |byte| {
|
|
||||||
if (byte == '\n') {
|
|
||||||
ext.SDL_LogError(
|
|
||||||
ext.SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
format_string,
|
|
||||||
self.write_buffer.values.len,
|
|
||||||
self.write_buffer.values.ptr);
|
|
||||||
|
|
||||||
self.write_buffer.clear();
|
|
||||||
|
|
||||||
line_written = 0;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
try self.write_buffer.push_one(byte);
|
|
||||||
|
|
||||||
line_written += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.write_buffer.values.len == 0) {
|
|
||||||
ext.SDL_LogError(
|
|
||||||
ext.SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
format_string,
|
|
||||||
self.write_buffer.values.len,
|
|
||||||
self.write_buffer.values.ptr);
|
|
||||||
|
|
||||||
self.write_buffer.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
|
@ -10,30 +10,34 @@ 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 CallContext = struct {
|
pub const CallContext = struct {
|
||||||
env: *RuntimeEnv,
|
env: *RuntimeEnv,
|
||||||
caller: ?*const RuntimeRef,
|
caller: ?*const RuntimeRef,
|
||||||
userdata: []coral.io.Byte,
|
userdata: []coral.io.Byte,
|
||||||
args: []const ?*const RuntimeRef = &.{},
|
|
||||||
|
|
||||||
pub fn arg_at(self: CallContext, index: u8) RuntimeError!*const RuntimeRef {
|
|
||||||
if (!coral.math.is_clamped(index, 0, self.args.len - 1)) {
|
|
||||||
return self.env.check_fail("argument out of bounds");
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.args[@as(usize, index)];
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Callable = coral.io.Generator(RuntimeError!?*RuntimeRef, CallContext);
|
|
||||||
|
|
||||||
pub const DynamicObject = struct {
|
pub const DynamicObject = struct {
|
||||||
userdata: []coral.io.Byte,
|
userdata: []coral.io.Byte,
|
||||||
typeinfo: *const Typeinfo,
|
typeinfo: *const Typeinfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const ErrorHandler = coral.io.Generator(void, ErrorInfo);
|
||||||
|
|
||||||
|
pub const ErrorInfo = struct {
|
||||||
|
message: []const coral.io.Byte,
|
||||||
|
frames: []const Frame,
|
||||||
|
};
|
||||||
|
|
||||||
pub const Float = f64;
|
pub const Float = f64;
|
||||||
|
|
||||||
|
pub const Frame = struct {
|
||||||
|
name: []const coral.io.Byte,
|
||||||
|
arg_count: u8,
|
||||||
|
locals_top: usize,
|
||||||
|
};
|
||||||
|
|
||||||
pub const IndexContext = struct {
|
pub const IndexContext = struct {
|
||||||
env: *RuntimeEnv,
|
env: *RuntimeEnv,
|
||||||
userdata: []coral.io.Byte,
|
userdata: []coral.io.Byte,
|
||||||
|
@ -42,23 +46,18 @@ pub const IndexContext = struct {
|
||||||
|
|
||||||
pub const RuntimeEnv = struct {
|
pub const RuntimeEnv = struct {
|
||||||
allocator: coral.io.Allocator,
|
allocator: coral.io.Allocator,
|
||||||
err_writer: coral.io.Writer,
|
error_handler: ErrorHandler,
|
||||||
|
syscallers: SyscallerTable,
|
||||||
local_refs: RefStack,
|
local_refs: RefStack,
|
||||||
frames: FrameStack,
|
frames: FrameStack,
|
||||||
ref_values: RefSlab,
|
ref_values: RefSlab,
|
||||||
|
|
||||||
const FrameStack = coral.list.Stack(struct {
|
const FrameStack = coral.list.Stack(Frame);
|
||||||
arg_count: u8,
|
|
||||||
locals_top: usize,
|
const SyscallerTable = coral.map.StringTable(Syscaller);
|
||||||
});
|
|
||||||
|
|
||||||
const RefStack = coral.list.Stack(?*RuntimeRef);
|
const RefStack = coral.list.Stack(?*RuntimeRef);
|
||||||
|
|
||||||
pub const Options = struct {
|
|
||||||
out_writer: coral.io.Writer = coral.io.null_writer,
|
|
||||||
err_writer: coral.io.Writer = coral.io.null_writer,
|
|
||||||
};
|
|
||||||
|
|
||||||
const RefSlab = coral.map.Slab(struct {
|
const RefSlab = coral.map.Slab(struct {
|
||||||
ref_count: usize,
|
ref_count: usize,
|
||||||
|
|
||||||
|
@ -71,9 +70,9 @@ pub const RuntimeEnv = struct {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
pub const ScriptSource = struct {
|
pub const Syscall = struct {
|
||||||
name: []const coral.io.Byte,
|
name: []const coral.io.Byte,
|
||||||
data: []const coral.io.Byte,
|
caller: Syscaller,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn acquire(self: *RuntimeEnv, ref: ?*const RuntimeRef) ?*RuntimeRef {
|
pub fn acquire(self: *RuntimeEnv, ref: ?*const RuntimeRef) ?*RuntimeRef {
|
||||||
|
@ -89,22 +88,10 @@ pub const RuntimeEnv = struct {
|
||||||
return @ptrFromInt(key);
|
return @ptrFromInt(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call(
|
pub fn bind_syscalls(self: *RuntimeEnv, syscalls: []const Syscall) RuntimeError!void {
|
||||||
self: *RuntimeEnv,
|
for (syscalls) |syscall| {
|
||||||
caller_ref: ?*const RuntimeRef,
|
_ = try self.syscallers.replace(syscall.name, syscall.caller);
|
||||||
callable_ref: ?*const RuntimeRef,
|
}
|
||||||
arg_refs: []const *const RuntimeRef,
|
|
||||||
) RuntimeError!?*RuntimeRef {
|
|
||||||
return switch (self.unbox(callable_ref)) {
|
|
||||||
.dynamic => |dynamic| dynamic.typeinfo.call(.{
|
|
||||||
.env = self,
|
|
||||||
.caller = caller_ref,
|
|
||||||
.userdata = dynamic.userdata,
|
|
||||||
.args = arg_refs,
|
|
||||||
}),
|
|
||||||
|
|
||||||
else => self.raise(error.TypeMismatch, "type is not callable"),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn discard(self: *RuntimeEnv, ref: ?*RuntimeRef) void {
|
pub fn discard(self: *RuntimeEnv, ref: ?*RuntimeRef) void {
|
||||||
|
@ -126,58 +113,29 @@ pub const RuntimeEnv = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dynamic_get(self: *RuntimeEnv, indexable_ref: ?*const RuntimeRef, index_ref: ?*const RuntimeRef) RuntimeError!?*RuntimeRef {
|
pub fn execute_file(self: *RuntimeEnv, file_access: file.Access, file_path: file.Path) RuntimeError!?*RuntimeRef {
|
||||||
return switch (self.unbox(indexable_ref)) {
|
if ((try file.allocate_and_load(self.allocator, file_access, file_path))) |file_data| {
|
||||||
.nil => self.raise(error.BadOperation, "nil has no such index"),
|
|
||||||
.boolean => self.raise(error.BadOperation, "boolean has no such index"),
|
|
||||||
.number => self.raise(error.BadOperation, "number has no such index"),
|
|
||||||
.string => self.raise(error.BadOperation, "string has no such index"),
|
|
||||||
|
|
||||||
.dynamic => |dynamic| dynamic.typeinfo.get(.{
|
|
||||||
.userdata = dynamic.userdata,
|
|
||||||
.env = self,
|
|
||||||
.index_ref = index_ref,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dynamic_set(self: *RuntimeEnv, indexable_ref: ?*const RuntimeRef, index_ref: ?*const RuntimeRef, value_ref: ?*const RuntimeRef) RuntimeError!void {
|
|
||||||
return switch (self.unbox(indexable_ref)) {
|
|
||||||
.nil => self.raise(error.BadOperation, "nil is immutable"),
|
|
||||||
.boolean => self.raise(error.BadOperation, "boolean is immutable"),
|
|
||||||
.number => self.raise(error.BadOperation, "number is immutable"),
|
|
||||||
.string => self.raise(error.BadOperation, "string is immutable"),
|
|
||||||
|
|
||||||
.dynamic => |dynamic| dynamic.typeinfo.set(.{
|
|
||||||
.userdata = dynamic.userdata,
|
|
||||||
.env = self,
|
|
||||||
.index_ref = index_ref,
|
|
||||||
}, value_ref),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn execute_file(self: *RuntimeEnv, global_ref: ?*const RuntimeRef, file_access: file.Access, file_path: file.Path) RuntimeError!?*RuntimeRef {
|
|
||||||
const error_message = "failed to load file";
|
|
||||||
|
|
||||||
const file_data = (try file.allocate_and_load(self.allocator, file_access, file_path)) orelse {
|
|
||||||
return self.raise(error.SystemFailure, error_message);
|
|
||||||
};
|
|
||||||
|
|
||||||
defer self.allocator.deallocate(file_data);
|
defer self.allocator.deallocate(file_data);
|
||||||
|
|
||||||
return self.execute_script(global_ref, .{
|
if (file_path.to_string()) |string| {
|
||||||
.name = file_path.to_string() orelse return self.raise(error.SystemFailure, error_message),
|
return self.execute_script(string, file_data);
|
||||||
.data = file_data,
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute_script(self: *RuntimeEnv, global_ref: ?*const RuntimeRef, source: ScriptSource) RuntimeError!?*RuntimeRef {
|
return self.raise(error.BadOperation, "failed to load file");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn execute_script(
|
||||||
|
self: *RuntimeEnv,
|
||||||
|
name: []const coral.io.Byte,
|
||||||
|
data: []const coral.io.Byte,
|
||||||
|
) RuntimeError!?*RuntimeRef {
|
||||||
var ast = Ast.make(self.allocator);
|
var ast = Ast.make(self.allocator);
|
||||||
|
|
||||||
defer ast.free();
|
defer ast.free();
|
||||||
|
|
||||||
{
|
{
|
||||||
var tokenizer = tokens.Tokenizer{.source = source.data};
|
var tokenizer = tokens.Tokenizer{.source = data};
|
||||||
|
|
||||||
ast.parse(&tokenizer) catch |parse_error| switch (parse_error) {
|
ast.parse(&tokenizer) catch |parse_error| switch (parse_error) {
|
||||||
error.BadSyntax => return self.raise(error.BadSyntax, ast.error_message),
|
error.BadSyntax => return self.raise(error.BadSyntax, ast.error_message),
|
||||||
|
@ -191,7 +149,7 @@ pub const RuntimeEnv = struct {
|
||||||
|
|
||||||
try exe.compile_ast(ast);
|
try exe.compile_ast(ast);
|
||||||
|
|
||||||
return exe.execute(global_ref, source.name);
|
return try exe.execute(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn frame_pop(self: *RuntimeEnv) void {
|
pub fn frame_pop(self: *RuntimeEnv) void {
|
||||||
|
@ -201,13 +159,12 @@ pub const RuntimeEnv = struct {
|
||||||
coral.debug.assert(self.local_refs.drop((self.local_refs.values.len - frame.?.locals_top) + frame.?.arg_count));
|
coral.debug.assert(self.local_refs.drop((self.local_refs.values.len - frame.?.locals_top) + frame.?.arg_count));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn frame_push(self: *RuntimeEnv, arg_count: u8) RuntimeError![]const *const RuntimeRef {
|
pub fn frame_push(self: *RuntimeEnv, frame_name: []const coral.io.Byte, arg_count: u8) RuntimeError!void {
|
||||||
try self.frames.push_one(.{
|
try self.frames.push_one(.{
|
||||||
|
.name = frame_name,
|
||||||
.arg_count = arg_count,
|
.arg_count = arg_count,
|
||||||
.locals_top = self.local_refs.values.len,
|
.locals_top = self.local_refs.values.len,
|
||||||
});
|
});
|
||||||
|
|
||||||
return @as([]const *const RuntimeRef, @ptrCast(self.local_refs.values[(self.local_refs.values.len - arg_count) .. self.local_refs.values.len]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn free(self: *RuntimeEnv) void {
|
pub fn free(self: *RuntimeEnv) void {
|
||||||
|
@ -215,6 +172,16 @@ pub const RuntimeEnv = struct {
|
||||||
self.ref_values.free();
|
self.ref_values.free();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.local_refs.values[frame.locals_top - (1 + index)];
|
||||||
|
}
|
||||||
|
|
||||||
pub fn local_get(self: *RuntimeEnv, local: u8) ?*RuntimeRef {
|
pub fn local_get(self: *RuntimeEnv, local: u8) ?*RuntimeRef {
|
||||||
return self.local_refs.values[local];
|
return self.local_refs.values[local];
|
||||||
}
|
}
|
||||||
|
@ -243,12 +210,13 @@ pub const RuntimeEnv = struct {
|
||||||
self.local_refs.values[local] = self.acquire(value);
|
self.local_refs.values[local] = self.acquire(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make(allocator: coral.io.Allocator, options: Options) RuntimeError!RuntimeEnv {
|
pub fn make(allocator: coral.io.Allocator, error_handler: ErrorHandler) coral.io.AllocationError!RuntimeEnv {
|
||||||
return .{
|
return RuntimeEnv{
|
||||||
.local_refs = RefStack.make(allocator),
|
.local_refs = RefStack.make(allocator),
|
||||||
.ref_values = RefSlab.make(allocator),
|
.ref_values = RefSlab.make(allocator),
|
||||||
.frames = FrameStack.make(allocator),
|
.frames = FrameStack.make(allocator),
|
||||||
.err_writer = options.err_writer,
|
.syscallers = SyscallerTable.make(allocator),
|
||||||
|
.error_handler = error_handler,
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -260,7 +228,11 @@ pub const RuntimeEnv = struct {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_dynamic(self: *RuntimeEnv, userdata: []const coral.io.Byte, typeinfo: *const Typeinfo) RuntimeError!*RuntimeRef {
|
pub fn new_dynamic(
|
||||||
|
self: *RuntimeEnv,
|
||||||
|
userdata: []const coral.io.Byte,
|
||||||
|
typeinfo: *const Typeinfo,
|
||||||
|
) RuntimeError!*RuntimeRef {
|
||||||
const userdata_copy = try coral.io.allocate_copy(self.allocator, userdata);
|
const userdata_copy = try coral.io.allocate_copy(self.allocator, userdata);
|
||||||
|
|
||||||
errdefer self.allocator.deallocate(userdata_copy);
|
errdefer self.allocator.deallocate(userdata_copy);
|
||||||
|
@ -296,17 +268,19 @@ pub const RuntimeEnv = struct {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn raise(self: *RuntimeEnv, error_value: RuntimeError, error_message: []const coral.io.Byte) RuntimeError {
|
pub fn raise(self: *RuntimeEnv, error_value: RuntimeError, message: []const coral.io.Byte) RuntimeError {
|
||||||
// TODO: Print stack trace from state.
|
self.error_handler.invoke(.{
|
||||||
coral.utf8.print_formatted(self.err_writer, "{name}@{line}: {message}", .{
|
.message = message,
|
||||||
.name = "???",
|
.frames = self.frames.values,
|
||||||
.line = @as(u64, 0),
|
});
|
||||||
.message = error_message,
|
|
||||||
}) catch {};
|
|
||||||
|
|
||||||
return error_value;
|
return error_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn syscaller(self: *RuntimeEnv, name: []const coral.io.Byte) RuntimeError!Syscaller {
|
||||||
|
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) Unboxed {
|
||||||
const ref_data = self.ref_values.lookup(@intFromPtr(ref orelse return .nil));
|
const ref_data = self.ref_values.lookup(@intFromPtr(ref orelse return .nil));
|
||||||
|
|
||||||
|
@ -320,18 +294,48 @@ pub const RuntimeEnv = struct {
|
||||||
.dynamic => |dynamic| .{.dynamic = dynamic},
|
.dynamic => |dynamic| .{.dynamic = dynamic},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn unbox_dynamic(self: *RuntimeEnv, ref: ?*const RuntimeRef) RuntimeError![]const coral.io.Byte {
|
||||||
|
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 unbox_string(self: *RuntimeEnv, ref: ?*const RuntimeRef) RuntimeError![]const coral.io.Byte {
|
||||||
|
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");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const RuntimeError = coral.io.AllocationError || error {
|
pub const RuntimeError = coral.io.AllocationError || error {
|
||||||
IllegalState,
|
IllegalState,
|
||||||
SystemFailure,
|
|
||||||
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,
|
||||||
|
@ -339,6 +343,7 @@ pub const TestContext = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Typeinfo = struct {
|
pub const Typeinfo = struct {
|
||||||
|
name: []const coral.io.Byte,
|
||||||
call: *const fn (context: CallContext) RuntimeError!?*RuntimeRef = default_call,
|
call: *const fn (context: CallContext) 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,
|
||||||
|
@ -380,26 +385,87 @@ pub const Unboxed = union (enum) {
|
||||||
number: Float,
|
number: Float,
|
||||||
string: []const coral.io.Byte,
|
string: []const coral.io.Byte,
|
||||||
dynamic: *const DynamicObject,
|
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 get_dynamic_field(env: *RuntimeEnv, indexable_ref: ?*const RuntimeRef, field_name: []const coral.io.Byte) RuntimeError!?*RuntimeRef {
|
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(
|
||||||
|
env: *RuntimeEnv,
|
||||||
|
caller_ref: ?*const RuntimeRef,
|
||||||
|
callable_ref: ?*const RuntimeRef,
|
||||||
|
arg_refs: Args,
|
||||||
|
) RuntimeError!?*RuntimeRef {
|
||||||
|
for (arg_refs) |arg_ref| {
|
||||||
|
try env.local_push_ref(arg_ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
env.frame_push("", arg_refs.len);
|
||||||
|
|
||||||
|
defer env.frame_pop();
|
||||||
|
|
||||||
|
const dynamic = try env.unbox(callable_ref).expect_dynamic();
|
||||||
|
|
||||||
|
return dynamic.type_info.call(.{
|
||||||
|
.env = env,
|
||||||
|
.caller = caller_ref,
|
||||||
|
.userdata = dynamic.userdata,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(
|
||||||
|
env: *RuntimeEnv,
|
||||||
|
indexable_ref: ?*const RuntimeRef,
|
||||||
|
index_ref: ?*const RuntimeRef,
|
||||||
|
) RuntimeError!?*RuntimeRef {
|
||||||
|
const dynamic = try env.unbox(indexable_ref).expect_dynamic(env);
|
||||||
|
|
||||||
|
return dynamic.typeinfo.get(.{
|
||||||
|
.userdata = dynamic.userdata,
|
||||||
|
.env = env,
|
||||||
|
.index_ref = index_ref,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
return env.dynamic_get(indexable_ref, field_name_ref);
|
return get(env, indexable_ref, field_name_ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_callable(env: *RuntimeEnv, generator: Callable) RuntimeError!?*RuntimeRef {
|
pub fn set(
|
||||||
const Concrete = struct {
|
env: *RuntimeEnv,
|
||||||
fn call(context: CallContext) RuntimeError!?*RuntimeRef {
|
indexable_ref: ?*const RuntimeRef,
|
||||||
return @as(*Callable, @ptrCast(@alignCast(context.userdata.ptr))).invoke(context);
|
index_ref: ?*const RuntimeRef,
|
||||||
}
|
value_ref: ?*const RuntimeRef,
|
||||||
};
|
) RuntimeError!void {
|
||||||
|
const dynamic = try env.unbox(indexable_ref).expect_dynamic(env);
|
||||||
|
|
||||||
return env.new_dynamic(coral.io.bytes_of(&generator), &.{
|
return dynamic.typeinfo.set(.{
|
||||||
.call = Concrete.call,
|
.userdata = dynamic.userdata,
|
||||||
});
|
.env = env,
|
||||||
|
.index_ref = index_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 {
|
||||||
|
const field_name_ref = try env.new_string(field_name);
|
||||||
|
|
||||||
|
defer env.discard(field_name_ref);
|
||||||
|
|
||||||
|
return set(env, indexable_ref, field_name_ref, value_ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_table(env: *RuntimeEnv) RuntimeError!?*RuntimeRef {
|
pub fn new_table(env: *RuntimeEnv) RuntimeError!?*RuntimeRef {
|
||||||
|
@ -410,14 +476,6 @@ pub fn new_table(env: *RuntimeEnv) RuntimeError!?*RuntimeRef {
|
||||||
return try env.new_dynamic(coral.io.bytes_of(&table), &Table.typeinfo);
|
return try env.new_dynamic(coral.io.bytes_of(&table), &Table.typeinfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_dynamic_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);
|
|
||||||
|
|
||||||
defer env.discard(field_name_ref);
|
|
||||||
|
|
||||||
return env.dynamic_set(indexable_ref, field_name_ref, value_ref);
|
|
||||||
}
|
|
||||||
|
|
||||||
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"),
|
.nil => env.raise(error.TypeMismatch, "cannot compare nil objects"),
|
||||||
|
|
|
@ -17,7 +17,7 @@ pub const Expression = union (enum) {
|
||||||
grouped_expression: *Expression,
|
grouped_expression: *Expression,
|
||||||
get_local: []const coral.io.Byte,
|
get_local: []const coral.io.Byte,
|
||||||
|
|
||||||
call_global: struct {
|
call_system: struct {
|
||||||
identifier: []const coral.io.Byte,
|
identifier: []const coral.io.Byte,
|
||||||
argument_expressions: List,
|
argument_expressions: List,
|
||||||
},
|
},
|
||||||
|
@ -85,7 +85,7 @@ pub const Statement = union (enum) {
|
||||||
expression: Expression,
|
expression: Expression,
|
||||||
},
|
},
|
||||||
|
|
||||||
call_global: struct {
|
call_system: struct {
|
||||||
identifier: []const coral.io.Byte,
|
identifier: []const coral.io.Byte,
|
||||||
argument_expressions: Expression.List,
|
argument_expressions: Expression.List,
|
||||||
},
|
},
|
||||||
|
@ -249,7 +249,7 @@ pub fn parse(self: *Self, tokenizer: *tokens.Tokenizer) ParseError!void {
|
||||||
tokenizer.step();
|
tokenizer.step();
|
||||||
|
|
||||||
try self.statements.push_one(.{
|
try self.statements.push_one(.{
|
||||||
.call_global = .{
|
.call_system = .{
|
||||||
.argument_expressions = expressions_list,
|
.argument_expressions = expressions_list,
|
||||||
.identifier = identifier,
|
.identifier = identifier,
|
||||||
},
|
},
|
||||||
|
@ -344,7 +344,7 @@ fn parse_factor(self: *Self, tokenizer: *tokens.Tokenizer) ParseError!Expression
|
||||||
tokenizer.step();
|
tokenizer.step();
|
||||||
|
|
||||||
return Expression{
|
return Expression{
|
||||||
.call_global = .{
|
.call_system = .{
|
||||||
.identifier = identifier,
|
.identifier = identifier,
|
||||||
.argument_expressions = expression_list,
|
.argument_expressions = expression_list,
|
||||||
},
|
},
|
||||||
|
@ -361,7 +361,7 @@ fn parse_factor(self: *Self, tokenizer: *tokens.Tokenizer) ParseError!Expression
|
||||||
tokenizer.step();
|
tokenizer.step();
|
||||||
|
|
||||||
return Expression{
|
return Expression{
|
||||||
.call_global = .{
|
.call_system = .{
|
||||||
.identifier = identifier,
|
.identifier = identifier,
|
||||||
.argument_expressions = expression_list,
|
.argument_expressions = expression_list,
|
||||||
},
|
},
|
||||||
|
|
|
@ -90,7 +90,7 @@ const AstCompiler = struct {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
.call_global => |call| {
|
.call_system => |call| {
|
||||||
if (call.argument_expressions.values.len > coral.math.max_int(@typeInfo(u8).Int)) {
|
if (call.argument_expressions.values.len > coral.math.max_int(@typeInfo(u8).Int)) {
|
||||||
return self.chunk.env.raise(error.OutOfMemory, "functions may receive a maximum of 255 locals");
|
return self.chunk.env.raise(error.OutOfMemory, "functions may receive a maximum of 255 locals");
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,8 @@ const AstCompiler = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
try self.chunk.append_opcode(.{.push_const = try self.chunk.declare_constant_string(call.identifier)});
|
try self.chunk.append_opcode(.{.push_const = try self.chunk.declare_constant_string(call.identifier)});
|
||||||
try self.chunk.append_opcode(.{.call = @intCast(call.argument_expressions.values.len)});
|
try self.chunk.append_opcode(.{.syscall = @intCast(call.argument_expressions.values.len)});
|
||||||
|
try self.chunk.append_opcode(.pop);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,7 +121,7 @@ const AstCompiler = struct {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
.call_global => |call| {
|
.call_system => |call| {
|
||||||
if (call.argument_expressions.values.len > coral.math.max_int(@typeInfo(u8).Int)) {
|
if (call.argument_expressions.values.len > coral.math.max_int(@typeInfo(u8).Int)) {
|
||||||
return self.chunk.env.raise(error.OutOfMemory, "functions may receive a maximum of 255 locals");
|
return self.chunk.env.raise(error.OutOfMemory, "functions may receive a maximum of 255 locals");
|
||||||
}
|
}
|
||||||
|
@ -130,8 +131,8 @@ const AstCompiler = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
try self.chunk.append_opcode(.{.push_const = try self.chunk.declare_constant_string(call.identifier)});
|
try self.chunk.append_opcode(.{.push_const = try self.chunk.declare_constant_string(call.identifier)});
|
||||||
try self.chunk.append_opcode(.get_global);
|
try self.chunk.append_opcode(.{.syscall = @intCast(call.argument_expressions.values.len)});
|
||||||
try self.chunk.append_opcode(.{.call = @intCast(call.argument_expressions.values.len)});
|
try self.chunk.append_opcode(.pop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,6 +172,7 @@ const RefList = coral.list.Stack(?*kym.RuntimeRef);
|
||||||
const LocalsList = coral.list.Stack([]const u8);
|
const LocalsList = coral.list.Stack([]const u8);
|
||||||
|
|
||||||
pub const Opcode = union (enum) {
|
pub const Opcode = union (enum) {
|
||||||
|
pop,
|
||||||
push_nil,
|
push_nil,
|
||||||
push_true,
|
push_true,
|
||||||
push_false,
|
push_false,
|
||||||
|
@ -178,8 +180,8 @@ pub const Opcode = union (enum) {
|
||||||
push_local: u8,
|
push_local: u8,
|
||||||
push_table: u32,
|
push_table: u32,
|
||||||
set_local: u8,
|
set_local: u8,
|
||||||
get_global,
|
|
||||||
call: u8,
|
call: u8,
|
||||||
|
syscall: u8,
|
||||||
|
|
||||||
not,
|
not,
|
||||||
neg,
|
neg,
|
||||||
|
@ -251,14 +253,14 @@ pub fn declare_constant_string(self: *Self, constant: []const coral.io.Byte) kym
|
||||||
return @intCast(tail);
|
return @intCast(tail);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute(self: *Self, global_ref: ?*const kym.RuntimeRef, name: []const coral.io.Byte) kym.RuntimeError!?*kym.RuntimeRef {
|
pub fn execute(self: *Self, name: []const coral.io.Byte) kym.RuntimeError!?*kym.RuntimeRef {
|
||||||
_ = name;
|
try self.env.frame_push(name, 0);
|
||||||
_ = try self.env.frame_push(0);
|
|
||||||
|
|
||||||
defer self.env.frame_pop();
|
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()),
|
||||||
.push_nil => try self.env.local_push_ref(null),
|
.push_nil => try self.env.local_push_ref(null),
|
||||||
.push_true => try self.env.local_push_boolean(true),
|
.push_true => try self.env.local_push_boolean(true),
|
||||||
.push_false => try self.env.local_push_boolean(false),
|
.push_false => try self.env.local_push_boolean(false),
|
||||||
|
@ -270,10 +272,18 @@ pub fn execute(self: *Self, global_ref: ?*const kym.RuntimeRef, name: []const co
|
||||||
defer self.env.discard(table_ref);
|
defer self.env.discard(table_ref);
|
||||||
|
|
||||||
{
|
{
|
||||||
|
const dynamic = try self.env.unbox(table_ref).expect_dynamic(self.env);
|
||||||
var popped = @as(usize, 0);
|
var popped = @as(usize, 0);
|
||||||
|
|
||||||
while (popped < field_count) : (popped += 1) {
|
while (popped < field_count) : (popped += 1) {
|
||||||
try self.env.dynamic_set(table_ref, self.env.local_pop(), self.env.local_pop());
|
const index_ref = self.env.local_pop();
|
||||||
|
const value_ref = self.env.local_pop();
|
||||||
|
|
||||||
|
try dynamic.typeinfo.set(.{
|
||||||
|
.userdata = dynamic.userdata,
|
||||||
|
.env = self.env,
|
||||||
|
.index_ref = index_ref,
|
||||||
|
}, value_ref);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,18 +298,6 @@ pub fn execute(self: *Self, global_ref: ?*const kym.RuntimeRef, name: []const co
|
||||||
try self.env.local_push_ref(ref);
|
try self.env.local_push_ref(ref);
|
||||||
},
|
},
|
||||||
|
|
||||||
.get_global => {
|
|
||||||
const identifier_ref = self.env.local_pop();
|
|
||||||
|
|
||||||
defer self.env.discard(identifier_ref);
|
|
||||||
|
|
||||||
const ref = try self.env.dynamic_get(global_ref, identifier_ref);
|
|
||||||
|
|
||||||
defer self.env.discard(ref);
|
|
||||||
|
|
||||||
try self.env.local_push_ref(ref);
|
|
||||||
},
|
|
||||||
|
|
||||||
.set_local => |local| {
|
.set_local => |local| {
|
||||||
const ref = self.env.local_pop();
|
const ref = self.env.local_pop();
|
||||||
|
|
||||||
|
@ -313,17 +311,43 @@ pub fn execute(self: *Self, global_ref: ?*const kym.RuntimeRef, name: []const co
|
||||||
|
|
||||||
defer self.env.discard(callable_ref);
|
defer self.env.discard(callable_ref);
|
||||||
|
|
||||||
const args = try self.env.frame_push(arg_count);
|
try self.env.frame_push("", arg_count);
|
||||||
|
|
||||||
defer self.env.frame_pop();
|
defer self.env.frame_pop();
|
||||||
|
|
||||||
const result_ref = try self.env.call(global_ref, callable_ref, args);
|
const dynamic = try self.env.unbox(callable_ref).expect_dynamic(self.env);
|
||||||
|
|
||||||
|
const result_ref = try dynamic.typeinfo.call(.{
|
||||||
|
.env = self.env,
|
||||||
|
.caller = null,
|
||||||
|
.userdata = dynamic.userdata,
|
||||||
|
});
|
||||||
|
|
||||||
defer self.env.discard(result_ref);
|
defer self.env.discard(result_ref);
|
||||||
|
|
||||||
try self.env.local_push_ref(result_ref);
|
try self.env.local_push_ref(result_ref);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.syscall => |arg_count| {
|
||||||
|
const identifier_ref = self.env.local_pop();
|
||||||
|
|
||||||
|
defer self.env.discard(identifier_ref);
|
||||||
|
|
||||||
|
const identifier = try self.env.unbox_string(identifier_ref);
|
||||||
|
|
||||||
|
try self.env.frame_push(identifier, arg_count);
|
||||||
|
|
||||||
|
errdefer self.env.frame_pop();
|
||||||
|
|
||||||
|
const result_ref = try (try self.env.syscaller(identifier)).invoke(self.env);
|
||||||
|
|
||||||
|
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 self.env.local_push_number(switch (self.env.unbox(self.env.local_pop())) {
|
||||||
.number => |number| -number,
|
.number => |number| -number,
|
||||||
else => return self.env.raise(error.TypeMismatch, "object is not scalar negatable"),
|
else => return self.env.raise(error.TypeMismatch, "object is not scalar negatable"),
|
||||||
|
|
|
@ -4,10 +4,10 @@ const kym = @import("../kym.zig");
|
||||||
|
|
||||||
fields: FieldTable,
|
fields: FieldTable,
|
||||||
|
|
||||||
const FieldTable = coral.map.Table([]const coral.io.Byte, struct {
|
const FieldTable = coral.map.StringTable(struct {
|
||||||
key_ref: ?*kym.RuntimeRef,
|
key_ref: ?*kym.RuntimeRef,
|
||||||
value_ref: ?*kym.RuntimeRef,
|
value_ref: ?*kym.RuntimeRef,
|
||||||
}, coral.map.string_table_traits);
|
});
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ pub fn make(env: *kym.RuntimeEnv) Self {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const typeinfo = kym.Typeinfo{
|
pub const typeinfo = kym.Typeinfo{
|
||||||
|
.name = "table",
|
||||||
.clean = typeinfo_clean,
|
.clean = typeinfo_clean,
|
||||||
.get = typeinfo_get,
|
.get = typeinfo_get,
|
||||||
.set = typeinfo_set,
|
.set = typeinfo_set,
|
||||||
|
|
|
@ -10,81 +10,85 @@ const heap = @import("./heap.zig");
|
||||||
|
|
||||||
const kym = @import("./kym.zig");
|
const kym = @import("./kym.zig");
|
||||||
|
|
||||||
pub const RuntimeError = error {
|
fn kym_handle_errors(info: kym.ErrorInfo) void {
|
||||||
OutOfMemory,
|
var remaining_frames = info.frames.len;
|
||||||
InitFailure,
|
|
||||||
BadManifest,
|
while (remaining_frames != 0) {
|
||||||
};
|
remaining_frames -= 1;
|
||||||
|
|
||||||
|
app.log_fail(info.frames[remaining_frames].name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn kym_log_info(env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeRef {
|
||||||
|
app.log_info(try env.unbox_string(try env.get_arg(0)));
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn kym_log_warn(env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeRef {
|
||||||
|
app.log_warn(try env.unbox_string(try env.get_arg(0)));
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn kym_log_fail(env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeRef {
|
||||||
|
app.log_fail(try env.unbox_string(try env.get_arg(0)));
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
fn last_sdl_error() [:0]const u8 {
|
fn last_sdl_error() [:0]const u8 {
|
||||||
return coral.io.slice_sentineled(@as(u8, 0), @as([*:0]const u8, @ptrCast(ext.SDL_GetError())));
|
return coral.io.slice_sentineled(@as(u8, 0), @as([*:0]const u8, @ptrCast(ext.SDL_GetError())));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_app(file_access: file.Access) RuntimeError!void {
|
pub fn run_app(file_access: file.Access) void {
|
||||||
var info_log = app.WritableLog.make(.info, heap.allocator);
|
|
||||||
|
|
||||||
defer info_log.free();
|
|
||||||
|
|
||||||
var fail_log = app.WritableLog.make(.fail, heap.allocator);
|
|
||||||
|
|
||||||
defer fail_log.free();
|
|
||||||
|
|
||||||
if (ext.SDL_Init(ext.SDL_INIT_EVERYTHING) != 0) {
|
if (ext.SDL_Init(ext.SDL_INIT_EVERYTHING) != 0) {
|
||||||
try fail_log.write(last_sdl_error());
|
return app.log_fail(last_sdl_error());
|
||||||
|
|
||||||
return error.InitFailure;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defer ext.SDL_Quit();
|
defer ext.SDL_Quit();
|
||||||
|
|
||||||
var script_env = kym.RuntimeEnv.make(heap.allocator, .{
|
var script_env = kym.RuntimeEnv.make(heap.allocator, kym.ErrorHandler.from(kym_handle_errors)) catch {
|
||||||
.out_writer = info_log.as_writer(),
|
return app.log_fail("failed to initialize script runtime");
|
||||||
.err_writer = fail_log.as_writer(),
|
|
||||||
}) catch {
|
|
||||||
try fail_log.write("failed to initialize script runtime");
|
|
||||||
|
|
||||||
return error.InitFailure;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
defer script_env.free();
|
defer script_env.free();
|
||||||
|
|
||||||
var globals_ref = kym.new_table(&script_env) catch {
|
script_env.bind_syscalls(&.{
|
||||||
try fail_log.write("failed to initialize script runtime");
|
.{
|
||||||
|
.name = "log_info",
|
||||||
return error.InitFailure;
|
.caller = kym.Syscaller.from(kym_log_info),
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.name = "log_fail",
|
||||||
|
.caller = kym.Syscaller.from(kym_log_fail),
|
||||||
|
},
|
||||||
|
}) catch {
|
||||||
|
return app.log_fail("failed to initialize script runtime");
|
||||||
};
|
};
|
||||||
|
|
||||||
defer script_env.discard(&globals_ref);
|
|
||||||
|
|
||||||
var manifest = app.Manifest{};
|
var manifest = app.Manifest{};
|
||||||
|
|
||||||
manifest.load(&script_env, globals_ref, file_access) catch {
|
manifest.load(&script_env, file_access) catch return;
|
||||||
fail_log.write("failed to load / execute app.ona manifest") catch {};
|
|
||||||
|
|
||||||
return error.BadManifest;
|
|
||||||
};
|
|
||||||
|
|
||||||
const window = create: {
|
const window = create: {
|
||||||
const pos = ext.SDL_WINDOWPOS_CENTERED;
|
const pos = ext.SDL_WINDOWPOS_CENTERED;
|
||||||
const flags = 0;
|
const flags = 0;
|
||||||
|
|
||||||
break: create ext.SDL_CreateWindow(&manifest.title, pos, pos, manifest.width, manifest.height, flags) orelse {
|
break: create ext.SDL_CreateWindow(&manifest.title, pos, pos, manifest.width, manifest.height, flags) orelse {
|
||||||
fail_log.write(last_sdl_error()) catch {};
|
return app.log_fail(last_sdl_error());
|
||||||
|
|
||||||
return error.InitFailure;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
defer ext.SDL_DestroyWindow(window);
|
defer ext.SDL_DestroyWindow(window);
|
||||||
|
|
||||||
const renderer = create: {
|
const renderer = create: {
|
||||||
const defaultDriverIndex = -1;
|
const default_driver_index = -1;
|
||||||
const flags = ext.SDL_RENDERER_ACCELERATED;
|
const flags = ext.SDL_RENDERER_ACCELERATED;
|
||||||
|
|
||||||
break: create ext.SDL_CreateRenderer(window, defaultDriverIndex, flags) orelse {
|
break: create ext.SDL_CreateRenderer(window, default_driver_index, flags) orelse {
|
||||||
fail_log.write(last_sdl_error()) catch {};
|
return app.log_fail(last_sdl_error());
|
||||||
|
|
||||||
return error.InitFailure;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const ona = @import("ona");
|
const ona = @import("ona");
|
||||||
|
|
||||||
pub fn main() ona.RuntimeError!void {
|
pub fn main() anyerror!void {
|
||||||
try ona.run_app(.{.sandboxed_path = &ona.file.Path.cwd});
|
ona.run_app(.{.sandboxed_path = &ona.file.Path.cwd});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue