Compare commits

...

2 Commits

Author SHA1 Message Date
1ea115b277 Replace globals with native call interface
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-07-21 23:03:25 +01:00
84e5236ba1 Add global function calling syntax support 2023-07-19 12:19:56 +01:00
11 changed files with 496 additions and 368 deletions

View File

@ -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,

View File

@ -116,18 +116,32 @@ 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;
const Invoker = struct {
fn invoke(context: *const anyopaque, input: Input) Output {
if (is_zero_aligned) {
return invoker(@ptrCast(context), input);
}
return invoker(@ptrCast(@alignCast(context)), input);
}
};
return .{ return .{
.context = if (is_zero_aligned) state else @ptrCast(state), .context = if (is_zero_aligned) state else @ptrCast(state),
.invoker = Invoker.invoke,
};
}
.invoker = struct { pub fn from(comptime invoker: fn (input: Input) Output) Self {
fn invoke(context: *const anyopaque, input: Input) Output { const Invoker = struct {
if (is_zero_aligned) { fn invoke(_: *const anyopaque, input: Input) Output {
return invoker(@ptrCast(context), input); return invoker(input);
} }
};
return invoker(@ptrCast(@alignCast(context)), input); return .{
} .context = &.{},
}.invoke, .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);
} }

View File

@ -73,12 +73,12 @@ pub fn Stack(comptime Value: type) type {
self.capacity = packed_size; self.capacity = packed_size;
} }
pub fn peek(self: *Self) ?Value { pub fn peek(self: Self) ?Value {
if (self.values.len == 0) { if (self.values.len == 0) {
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;
}

View File

@ -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,
};

View File

@ -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 { pub fn log_warn(message: []const coral.io.Byte) void {
severity: LogSeverity, ext.SDL_LogWarn(ext.SDL_LOG_CATEGORY_APPLICATION, "%.*s", @as(c_int, @intCast(message.len)), message.ptr);
write_buffer: coral.list.ByteStack, }
pub fn as_writer(self: *WritableLog) coral.io.Writer { pub fn log_fail(message: []const coral.io.Byte) void {
return coral.io.Writer.bind(WritableLog, self, struct { ext.SDL_LogError(ext.SDL_LOG_CATEGORY_APPLICATION, "%.*s", @as(c_int, @intCast(message.len)), message.ptr);
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 {
self.write_buffer.free();
}
pub fn make(log_severity: LogSeverity, allocator: coral.io.Allocator) WritableLog {
return .{
.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();
}
}
};

View File

@ -10,19 +10,12 @@ 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: []u8, 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 DynamicObject = struct { pub const DynamicObject = struct {
@ -30,8 +23,21 @@ pub const DynamicObject = struct {
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,
@ -40,22 +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);
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,
@ -68,13 +70,13 @@ 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 {
const key = @intFromPtr(ref); const key = @intFromPtr(ref orelse return null);
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);
@ -86,25 +88,14 @@ 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 {
const callable = (try callable_ref.fetch(self)).to_object();
return callable.userinfo.call(.{
.env = self,
.caller = caller_ref,
.callable = callable_ref,
.userdata = callable.userdata,
.args = arg_refs,
});
} }
pub fn discard(self: *RuntimeEnv, ref: *?*RuntimeRef) void { pub fn discard(self: *RuntimeEnv, ref: ?*RuntimeRef) void {
const key = @intFromPtr(ref.* orelse return); const key = @intFromPtr(ref orelse return);
var ref_data = self.ref_values.remove(key) orelse unreachable; var ref_data = self.ref_values.remove(key) orelse unreachable;
coral.debug.assert(ref_data.ref_count != 0); coral.debug.assert(ref_data.ref_count != 0);
@ -120,32 +111,31 @@ pub const RuntimeEnv = struct {
} else { } else {
coral.debug.assert(self.ref_values.insert_at(key, ref_data)); coral.debug.assert(self.ref_values.insert_at(key, ref_data));
} }
ref.* = null;
} }
pub fn execute_file(self: *RuntimeEnv, global_ref: ?*const RuntimeRef, file_access: file.Access, file_path: file.Path) RuntimeError!?*RuntimeRef { pub fn execute_file(self: *RuntimeEnv, file_access: file.Access, file_path: file.Path) RuntimeError!?*RuntimeRef {
const error_message = "failed to load file"; if ((try file.allocate_and_load(self.allocator, file_access, file_path))) |file_data| {
defer self.allocator.deallocate(file_data);
const file_data = (try file.allocate_and_load(self.allocator, file_access, file_path)) orelse { if (file_path.to_string()) |string| {
return self.raise(error.SystemFailure, error_message); return self.execute_script(string, file_data);
}; }
}
defer self.allocator.deallocate(file_data); return self.raise(error.BadOperation, "failed to load file");
return self.execute_script(global_ref, .{
.name = file_path.to_string() orelse return self.raise(error.SystemFailure, error_message),
.data = file_data,
});
} }
pub fn execute_script(self: *RuntimeEnv, global_ref: ?*const RuntimeRef, source: ScriptSource) RuntimeError!?*RuntimeRef { 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),
@ -159,17 +149,20 @@ 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) RuntimeError!void { pub fn frame_pop(self: *RuntimeEnv) void {
const frame = self.frames.pop() orelse return self.raise(error.IllegalState, "stack underflow"); const frame = self.frames.pop();
coral.debug.assert(self.local_refs.drop(self.local_refs.values.len - frame.locals_top)); coral.debug.assert(frame != null);
coral.debug.assert(self.local_refs.drop((self.local_refs.values.len - frame.?.locals_top) + frame.?.arg_count));
} }
pub fn frame_push(self: *RuntimeEnv) RuntimeError!void { pub fn frame_push(self: *RuntimeEnv, frame_name: []const coral.io.Byte, arg_count: u8) RuntimeError!void {
return self.frames.push_one(.{ try self.frames.push_one(.{
.name = frame_name,
.arg_count = arg_count,
.locals_top = self.local_refs.values.len, .locals_top = self.local_refs.values.len,
}); });
} }
@ -179,27 +172,26 @@ pub const RuntimeEnv = struct {
self.ref_values.free(); self.ref_values.free();
} }
pub fn get_dynamic(self: *RuntimeEnv, indexable_ref: ?*const RuntimeRef, index_ref: ?*const RuntimeRef) RuntimeError!?*RuntimeRef { pub fn get_arg(self: *RuntimeEnv, index: usize) RuntimeError!?*const RuntimeRef {
return switch (self.unbox(indexable_ref)) { const frame = self.frames.peek() orelse return self.raise(error.IllegalState, "stack underflow");
.nil => self.raise(error.BadOperation, "nil is immutable"),
.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(.{ if (index >= frame.arg_count) {
.userdata = dynamic.userdata, return null;
.env = self, }
.index_ref = index_ref,
}), return self.local_refs.values[frame.locals_top - (1 + index)];
};
} }
pub fn local_get(self: *RuntimeEnv, local: u8) RuntimeError!?*RuntimeRef { pub fn local_get(self: *RuntimeEnv, local: u8) ?*RuntimeRef {
return self.local_refs.values[local]; return self.local_refs.values[local];
} }
pub fn local_pop(self: *RuntimeEnv) RuntimeError!?*RuntimeRef { pub fn local_pop(self: *RuntimeEnv) ?*RuntimeRef {
return self.local_refs.pop() orelse self.raise(error.IllegalState, "stack underflow"); const ref = self.local_refs.pop();
coral.debug.assert(ref != null);
return ref.?;
} }
pub fn local_push_ref(self: *RuntimeEnv, ref: ?*RuntimeRef) RuntimeError!void { pub fn local_push_ref(self: *RuntimeEnv, ref: ?*RuntimeRef) RuntimeError!void {
@ -214,16 +206,17 @@ pub const RuntimeEnv = struct {
return self.local_refs.push_one(try self.new_number(number)); return self.local_refs.push_one(try self.new_number(number));
} }
pub fn local_set(self: *RuntimeEnv, local: u8, value: ?*RuntimeRef) RuntimeError!void { pub fn local_set(self: *RuntimeEnv, local: u8, value: ?*RuntimeRef) void {
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,
}; };
} }
@ -235,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);
@ -271,30 +268,17 @@ pub const RuntimeEnv = struct {
})); }));
} }
pub fn raise(self: *RuntimeEnv, error_value: RuntimeError, error_message: []const u8) 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 set_dynamic(self: *RuntimeEnv, indexable_ref: ?*const RuntimeRef, index_ref: ?*const RuntimeRef, value_ref: ?*const RuntimeRef) RuntimeError!void { pub fn syscaller(self: *RuntimeEnv, name: []const coral.io.Byte) RuntimeError!Syscaller {
return switch (self.unbox(indexable_ref)) { return self.syscallers.lookup(name) orelse self.raise(error.BadOperation, "attempt to call undefined syscall");
.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 unbox(self: *RuntimeEnv, ref: ?*const RuntimeRef) Unboxed { pub fn unbox(self: *RuntimeEnv, ref: ?*const RuntimeRef) Unboxed {
@ -310,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,
@ -329,13 +343,30 @@ pub const TestContext = struct {
}; };
pub const Typeinfo = struct { pub const Typeinfo = struct {
call: *const fn (context: CallContext) RuntimeError!?*RuntimeRef, name: []const coral.io.Byte,
clean: *const fn (userdata: []u8) void, call: *const fn (context: CallContext) RuntimeError!?*RuntimeRef = default_call,
get: *const fn (context: IndexContext) RuntimeError!?*RuntimeRef, clean: *const fn (userdata: []coral.io.Byte) void = default_clean,
set: *const fn (context: IndexContext, value: ?*const RuntimeRef) RuntimeError!void, get: *const fn (context: IndexContext) RuntimeError!?*RuntimeRef = default_get,
set: *const fn (context: IndexContext, value: ?*const RuntimeRef) RuntimeError!void = default_set,
test_difference: *const fn (context: TestContext) RuntimeError!Float = default_test_difference, test_difference: *const fn (context: TestContext) RuntimeError!Float = default_test_difference,
test_equality: *const fn (context: TestContext) RuntimeError!bool = default_test_equality, test_equality: *const fn (context: TestContext) RuntimeError!bool = default_test_equality,
fn default_call(context: CallContext) RuntimeError!?*RuntimeRef {
return context.env.raise(error.TypeMismatch, "object is not callable");
}
fn default_clean(_: []coral.io.Byte) void {
// Nothing to clean by default.
}
fn default_get(context: IndexContext) RuntimeError!?*RuntimeRef {
return context.env.raise(error.TypeMismatch, "object is not indexable");
}
fn default_set(context: IndexContext, _: ?*const RuntimeRef) RuntimeError!void {
return context.env.raise(error.TypeMismatch, "object is not indexable");
}
fn default_test_difference(context: TestContext) RuntimeError!Float { fn default_test_difference(context: TestContext) RuntimeError!Float {
return context.env.raise(error.TypeMismatch, "object is not comparable"); return context.env.raise(error.TypeMismatch, "object is not comparable");
} }
@ -352,16 +383,89 @@ pub const Unboxed = union (enum) {
nil, nil,
boolean: bool, boolean: bool,
number: Float, number: Float,
string: []const u8, 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: ?*const RuntimeRef, field: []const u8) RuntimeError!?*RuntimeRef { pub fn assert(env: *RuntimeEnv, condition: bool, message: []const coral.io.Byte) RuntimeError!void {
var interned_field = try env.new_string(field); if (!condition) {
return env.raise(error.Assertion, message);
}
}
defer env.discard(&interned_field); 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);
}
return env.get_dynamic(indexable, interned_field); 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);
defer env.discard(field_name_ref);
return get(env, indexable_ref, field_name_ref);
}
pub fn set(
env: *RuntimeEnv,
indexable_ref: ?*const RuntimeRef,
index_ref: ?*const RuntimeRef,
value_ref: ?*const RuntimeRef,
) RuntimeError!void {
const dynamic = try env.unbox(indexable_ref).expect_dynamic(env);
return dynamic.typeinfo.set(.{
.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 {

View File

@ -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,
}, },

View File

@ -51,8 +51,7 @@ const AstCompiler = struct {
}); });
} }
try self.chunk.append_opcode(.{ try self.chunk.append_opcode(.{.push_table = @intCast(fields.values.len)});
.push_table = .{.field_count = @intCast(fields.values.len)}});
}, },
.binary_operation => |operation| { .binary_operation => |operation| {
@ -87,11 +86,11 @@ const AstCompiler = struct {
.get_local => |local| { .get_local => |local| {
try self.chunk.append_opcode(.{ try self.chunk.append_opcode(.{
.local_push_ref = self.resolve_local(local) orelse return self.chunk.env.raise(error.OutOfMemory, "undefined local"), .push_local = self.resolve_local(local) orelse return self.chunk.env.raise(error.OutOfMemory, "undefined local"),
}); });
}, },
.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");
} }
@ -101,12 +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(.{.syscall = @intCast(call.argument_expressions.values.len)});
try self.chunk.append_opcode(.{ try self.chunk.append_opcode(.pop);
.global_call = .{
.arg_count = @intCast(call.argument_expressions.values.len),
},
});
}, },
} }
} }
@ -126,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");
} }
@ -136,12 +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(.{.syscall = @intCast(call.argument_expressions.values.len)});
try self.chunk.append_opcode(.{ try self.chunk.append_opcode(.pop);
.global_call = .{
.arg_count = @intCast(call.argument_expressions.values.len),
},
});
} }
} }
} }
@ -181,21 +172,16 @@ 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,
push_const: Constant, push_const: Constant,
local_push_ref: u8, push_local: u8,
push_table: u32,
set_local: u8, set_local: u8,
get_global, call: u8,
syscall: u8,
push_table: struct {
field_count: u32,
},
global_call: struct {
arg_count: u8,
},
not, not,
neg, neg,
@ -242,9 +228,9 @@ pub fn declare_constant_number(self: *Self, constant: kym.Float) kym.RuntimeErro
return self.env.raise(error.BadSyntax, "functions may contain a maximum of 65,535 constants"); return self.env.raise(error.BadSyntax, "functions may contain a maximum of 65,535 constants");
} }
var constant_ref = try self.env.new_number(constant); const constant_ref = try self.env.new_number(constant);
errdefer self.env.discard(&constant_ref); errdefer self.env.discard(constant_ref);
try self.constant_refs.push_one(constant_ref); try self.constant_refs.push_one(constant_ref);
@ -258,93 +244,128 @@ pub fn declare_constant_string(self: *Self, constant: []const coral.io.Byte) kym
return self.env.raise(error.BadSyntax, "functions may contain a maximum of 65,535 constants"); return self.env.raise(error.BadSyntax, "functions may contain a maximum of 65,535 constants");
} }
var constant_ref = try self.env.new_string(constant); const constant_ref = try self.env.new_string(constant);
errdefer self.env.discard(&constant_ref); errdefer self.env.discard(constant_ref);
try self.constant_refs.push_one(constant_ref); try self.constant_refs.push_one(constant_ref);
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(); 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),
.push_const => |constant| try self.env.local_push_ref(self.constant_refs.values[constant]), .push_const => |constant| try self.env.local_push_ref(self.constant_refs.values[constant]),
.push_table => |push_table| { .push_table => |field_count| {
var table_ref = try kym.new_table(self.env); const table_ref = try kym.new_table(self.env);
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 < push_table.field_count) : (popped += 1) { while (popped < field_count) : (popped += 1) {
try self.env.set_dynamic(table_ref, try self.env.local_pop(), try 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);
} }
} }
try self.env.local_push_ref(table_ref); try self.env.local_push_ref(table_ref);
}, },
.local_push_ref => |local| { .push_local => |local| {
var ref = try self.env.local_get(local); const ref = self.env.local_get(local);
defer self.env.discard(&ref); defer self.env.discard(ref);
try self.env.local_push_ref(ref);
},
.get_global => {
var identifier_ref = try self.env.local_pop();
defer self.env.discard(&identifier_ref);
var ref = try self.env.get_dynamic(global_ref, identifier_ref);
defer self.env.discard(&ref);
try self.env.local_push_ref(ref); try self.env.local_push_ref(ref);
}, },
.set_local => |local| { .set_local => |local| {
var ref = try self.env.local_pop(); const ref = self.env.local_pop();
defer self.env.discard(&ref); defer self.env.discard(ref);
try self.env.local_set(local, ref); self.env.local_set(local, ref);
}, },
.global_call => |_| { .call => |arg_count| {
const callable_ref = self.env.local_pop();
defer self.env.discard(callable_ref);
try self.env.frame_push("", arg_count);
defer self.env.frame_pop();
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);
try self.env.local_push_ref(result_ref);
}, },
.neg => try self.env.local_push_number(switch (self.env.unbox(try self.env.local_pop())) { .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())) {
.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"),
}), }),
.not => try self.env.local_push_boolean(switch (self.env.unbox(try self.env.local_pop())) { .not => try self.env.local_push_boolean(switch (self.env.unbox(self.env.local_pop())) {
.boolean => |boolean| !boolean, .boolean => |boolean| !boolean,
else => return self.env.raise(error.TypeMismatch, "object is not boolean negatable"), else => return self.env.raise(error.TypeMismatch, "object is not boolean negatable"),
}), }),
.add => { .add => {
var rhs_ref = try self.env.local_pop(); const rhs_ref = self.env.local_pop();
defer self.env.discard(&rhs_ref); defer self.env.discard(rhs_ref);
var lhs_ref = try self.env.local_pop(); const lhs_ref = self.env.local_pop();
defer self.env.discard(&lhs_ref); defer self.env.discard(lhs_ref);
try self.env.local_push_ref(switch (self.env.unbox(lhs_ref)) { try self.env.local_push_ref(switch (self.env.unbox(lhs_ref)) {
.number => |lhs_number| switch (self.env.unbox(rhs_ref)) { .number => |lhs_number| switch (self.env.unbox(rhs_ref)) {
@ -357,13 +378,13 @@ pub fn execute(self: *Self, global_ref: ?*const kym.RuntimeRef, name: []const co
}, },
.sub => { .sub => {
var rhs_ref = try self.env.local_pop(); const rhs_ref = self.env.local_pop();
defer self.env.discard(&rhs_ref); defer self.env.discard(rhs_ref);
var lhs_ref = try self.env.local_pop(); const lhs_ref = self.env.local_pop();
defer self.env.discard(&lhs_ref); defer self.env.discard(lhs_ref);
try self.env.local_push_ref(switch (self.env.unbox(lhs_ref)) { try self.env.local_push_ref(switch (self.env.unbox(lhs_ref)) {
.number => |lhs_number| switch (self.env.unbox(rhs_ref)) { .number => |lhs_number| switch (self.env.unbox(rhs_ref)) {
@ -376,13 +397,13 @@ pub fn execute(self: *Self, global_ref: ?*const kym.RuntimeRef, name: []const co
}, },
.mul => { .mul => {
var rhs_ref = try self.env.local_pop(); const rhs_ref = self.env.local_pop();
defer self.env.discard(&rhs_ref); defer self.env.discard(rhs_ref);
var lhs_ref = try self.env.local_pop(); const lhs_ref = self.env.local_pop();
defer self.env.discard(&lhs_ref); defer self.env.discard(lhs_ref);
try self.env.local_push_ref(switch (self.env.unbox(lhs_ref)) { try self.env.local_push_ref(switch (self.env.unbox(lhs_ref)) {
.number => |lhs_number| switch (self.env.unbox(rhs_ref)) { .number => |lhs_number| switch (self.env.unbox(rhs_ref)) {
@ -395,13 +416,13 @@ pub fn execute(self: *Self, global_ref: ?*const kym.RuntimeRef, name: []const co
}, },
.div => { .div => {
var rhs_ref = try self.env.local_pop(); const rhs_ref = self.env.local_pop();
defer self.env.discard(&rhs_ref); defer self.env.discard(rhs_ref);
var lhs_ref = try self.env.local_pop(); const lhs_ref = self.env.local_pop();
defer self.env.discard(&lhs_ref); defer self.env.discard(lhs_ref);
try self.env.local_push_ref(switch (self.env.unbox(lhs_ref)) { try self.env.local_push_ref(switch (self.env.unbox(lhs_ref)) {
.number => |lhs_number| switch (self.env.unbox(rhs_ref)) { .number => |lhs_number| switch (self.env.unbox(rhs_ref)) {
@ -414,76 +435,72 @@ pub fn execute(self: *Self, global_ref: ?*const kym.RuntimeRef, name: []const co
}, },
.eql => { .eql => {
var rhs_ref = try self.env.local_pop(); const rhs_ref = self.env.local_pop();
defer self.env.discard(&rhs_ref); defer self.env.discard(rhs_ref);
var lhs_ref = try self.env.local_pop(); const lhs_ref = self.env.local_pop();
defer self.env.discard(&lhs_ref); defer self.env.discard(lhs_ref);
try self.env.local_push_boolean(try kym.test_equality(self.env, lhs_ref, rhs_ref)); try self.env.local_push_boolean(try kym.test_equality(self.env, lhs_ref, rhs_ref));
}, },
.cgt => { .cgt => {
var rhs_ref = try self.env.local_pop(); const rhs_ref = self.env.local_pop();
defer self.env.discard(&rhs_ref); defer self.env.discard(rhs_ref);
var lhs_ref = try self.env.local_pop(); const lhs_ref = self.env.local_pop();
defer self.env.discard(&lhs_ref); defer self.env.discard(lhs_ref);
try self.env.local_push_boolean(try kym.test_difference(self.env, lhs_ref, rhs_ref) > 0); try self.env.local_push_boolean(try kym.test_difference(self.env, lhs_ref, rhs_ref) > 0);
}, },
.clt => { .clt => {
var rhs_ref = try self.env.local_pop(); const rhs_ref = self.env.local_pop();
defer self.env.discard(&rhs_ref); defer self.env.discard(rhs_ref);
var lhs_ref = try self.env.local_pop(); const lhs_ref = self.env.local_pop();
defer self.env.discard(&lhs_ref); defer self.env.discard(lhs_ref);
try self.env.local_push_boolean(try kym.test_difference(self.env, lhs_ref, rhs_ref) < 0); try self.env.local_push_boolean(try kym.test_difference(self.env, lhs_ref, rhs_ref) < 0);
}, },
.cge => { .cge => {
var rhs_ref = try self.env.local_pop(); const rhs_ref = self.env.local_pop();
defer self.env.discard(&rhs_ref); defer self.env.discard(rhs_ref);
var lhs_ref = try self.env.local_pop(); const lhs_ref = self.env.local_pop();
defer self.env.discard(&lhs_ref); defer self.env.discard(lhs_ref);
try self.env.local_push_boolean(try kym.test_difference(self.env, lhs_ref, rhs_ref) >= 0); try self.env.local_push_boolean(try kym.test_difference(self.env, lhs_ref, rhs_ref) >= 0);
}, },
.cle => { .cle => {
var rhs_ref = try self.env.local_pop(); const rhs_ref = self.env.local_pop();
defer self.env.discard(&rhs_ref); defer self.env.discard(rhs_ref);
var lhs_ref = try self.env.local_pop(); const lhs_ref = self.env.local_pop();
defer self.env.discard(&lhs_ref); defer self.env.discard(lhs_ref);
try self.env.local_push_boolean(try kym.test_difference(self.env, lhs_ref, rhs_ref) <= 0); try self.env.local_push_boolean(try kym.test_difference(self.env, lhs_ref, rhs_ref) <= 0);
}, },
} }
} }
const result = try self.env.local_pop(); return self.env.local_pop();
try self.env.frame_pop();
return result;
} }
pub fn free(self: *Self) void { pub fn free(self: *Self) void {
for (self.constant_refs.values) |*constant| { for (self.constant_refs.values) |constant| {
self.env.discard(constant); self.env.discard(constant);
} }

View File

@ -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,16 +22,12 @@ pub fn make(env: *kym.RuntimeEnv) Self {
} }
pub const typeinfo = kym.Typeinfo{ pub const typeinfo = kym.Typeinfo{
.call = typeinfo_call, .name = "table",
.clean = typeinfo_clean, .clean = typeinfo_clean,
.get = typeinfo_get, .get = typeinfo_get,
.set = typeinfo_set, .set = typeinfo_set,
}; };
fn typeinfo_call(context: kym.CallContext) kym.RuntimeError!?*kym.RuntimeRef {
return context.env.raise(error.TypeMismatch, "cannot call a table");
}
fn typeinfo_clean(userdata: []u8) void { fn typeinfo_clean(userdata: []u8) void {
@as(*Self, @ptrCast(@alignCast(userdata.ptr))).free(); @as(*Self, @ptrCast(@alignCast(userdata.ptr))).free();
} }
@ -49,15 +45,15 @@ fn typeinfo_get(context: kym.IndexContext) kym.RuntimeError!?*kym.RuntimeRef {
fn typeinfo_set(context: kym.IndexContext, value_ref: ?*const kym.RuntimeRef) kym.RuntimeError!void { fn typeinfo_set(context: kym.IndexContext, 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)));
var 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)) { switch (context.env.unbox(context.index_ref)) {
.string => |string| { .string => |string| {
var acquired_index_ref = context.env.acquire(context.index_ref); const acquired_index_ref = context.env.acquire(context.index_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, .{ var displaced_table_entry = if (acquired_value_ref) |ref| try table.fields.replace(string, .{
.key_ref = acquired_index_ref, .key_ref = acquired_index_ref,
@ -65,8 +61,8 @@ fn typeinfo_set(context: kym.IndexContext, value_ref: ?*const kym.RuntimeRef) ky
}) else table.fields.remove(string); }) else table.fields.remove(string);
if (displaced_table_entry) |*entry| { if (displaced_table_entry) |*entry| {
context.env.discard(&entry.value.key_ref); context.env.discard(entry.value.key_ref);
context.env.discard(&entry.value.value_ref); context.env.discard(entry.value.value_ref);
} }
}, },

View File

@ -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;
}; };
}; };

View File

@ -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});
} }