First pass of Ona middleware API
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
This commit is contained in:
parent
e9a28f074c
commit
b0c34d11b6
|
@ -25,6 +25,7 @@ pub fn build(b: *std.Build) void {
|
|||
.optimize = optimize,
|
||||
});
|
||||
|
||||
compile_step.addModule("coral", coral_module);
|
||||
compile_step.addModule("ona", ona_module);
|
||||
compile_step.linkLibC();
|
||||
compile_step.linkSystemLibrary("SDL2");
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
let ona = @import("ona")
|
||||
let events = @import("events")
|
||||
|
||||
var timer = 0
|
||||
|
||||
ona.on_update(lambda (dt):
|
||||
events.on_update(lambda (dt):
|
||||
timer = timer + dt
|
||||
|
||||
if (timer > 1):
|
||||
@print("test hello world")
|
||||
@print("test")
|
||||
|
||||
timer = 0
|
||||
end
|
||||
|
@ -14,7 +14,7 @@ end)
|
|||
|
||||
return {
|
||||
.title = "Game",
|
||||
.width = 1280,
|
||||
.height = 800,
|
||||
.res_width = 1280,
|
||||
.res_height = 800,
|
||||
.tick_rate = 60,
|
||||
}
|
||||
|
|
|
@ -14,6 +14,17 @@ pub fn checked_add(a: anytype, b: anytype) ?@TypeOf(a + b) {
|
|||
return if (result.@"1" == 0) result.@"0" else null;
|
||||
}
|
||||
|
||||
pub fn checked_cast(comptime dest_int: std.builtin.Type.Int, value: anytype) ?@Type(.{.Int = dest_int}) {
|
||||
const dest_min = min_int(dest_int);
|
||||
const dest_max = max_int(dest_int);
|
||||
|
||||
if (value < dest_min or value > dest_max) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return @intCast(value);
|
||||
}
|
||||
|
||||
pub fn checked_mul(a: anytype, b: anytype) ?@TypeOf(a * b) {
|
||||
const result = @mulWithOverflow(a, b);
|
||||
|
||||
|
|
|
@ -332,6 +332,7 @@ noinline fn print_value(writer: io.Writer, value: anytype) ?usize {
|
|||
return switch (@typeInfo(Value)) {
|
||||
.Int => DecimalFormat.default.print(writer, value),
|
||||
.Float => DecimalFormat.default.print(writer, value),
|
||||
.ComptimeInt => print_value(writer, @as(@import("std").math.IntFittingRange(value, value), value)),
|
||||
|
||||
.Pointer => |pointer| switch (pointer.size) {
|
||||
.Many, .C => HexadecimalFormat.default.print(writer, @intFromPtr(value)),
|
||||
|
|
|
@ -1,181 +0,0 @@
|
|||
|
||||
pub const Vector2 = struct {
|
||||
x: f32,
|
||||
y: f32,
|
||||
|
||||
pub const Scalars = [2]f32;
|
||||
|
||||
pub fn equals(self: Vector2, vector: Vector2) bool {
|
||||
return self.x == vector.x and self.y == vector.y;
|
||||
}
|
||||
|
||||
pub fn from_scalar(scalar: f32) Vector2 {
|
||||
return .{
|
||||
.x = scalar,
|
||||
.y = scalar,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn from_scalars(scalars: Scalars) Vector2 {
|
||||
return .{
|
||||
.x = scalars[0],
|
||||
.y = scalars[1],
|
||||
};
|
||||
}
|
||||
|
||||
pub fn scalar_added(self: Vector2, scalar: f32) Vector2 {
|
||||
return .{
|
||||
.x = self.x + scalar,
|
||||
.y = self.y + scalar,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn scalar_divided(self: Vector2, scalar: f32) Vector2 {
|
||||
return .{
|
||||
.x = self.x / scalar,
|
||||
.y = self.y / scalar,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn scalar_multiplied(self: Vector2, scalar: f32) Vector2 {
|
||||
return .{
|
||||
.x = self.x * scalar,
|
||||
.y = self.y * scalar,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn to_scalars(self: Vector2) Scalars {
|
||||
return .{self.x, self.y};
|
||||
}
|
||||
|
||||
pub fn scalar_subtracted(self: Vector2, scalar: f32) Vector2 {
|
||||
return .{
|
||||
.x = self.x - scalar,
|
||||
.y = self.y - scalar,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vector_added(self: Vector2, vector: Vector2) Vector2 {
|
||||
return .{
|
||||
.x = self.x + vector.x,
|
||||
.y = self.y + vector.y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vector_divided(self: Vector2, vector: Vector2) Vector2 {
|
||||
return .{
|
||||
.x = self.x / vector.x,
|
||||
.y = self.y / vector.y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vector_multiplied(self: Vector2, vector: Vector2) Vector2 {
|
||||
return .{
|
||||
.x = self.x * vector.x,
|
||||
.y = self.y * vector.y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vector_subtracted(self: Vector2, vector: Vector2) Vector2 {
|
||||
return .{
|
||||
.x = self.x - vector.x,
|
||||
.y = self.y - vector.y,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const Vector3 = struct {
|
||||
x: f32,
|
||||
y: f32,
|
||||
z: f32,
|
||||
|
||||
pub const Scalars = [3]f32;
|
||||
|
||||
pub fn equals(self: Vector3, vector: Vector3) bool {
|
||||
return self.x == vector.x and self.y == vector.y and self.z == vector.z;
|
||||
}
|
||||
|
||||
pub fn from_scalar(scalar: f32) Vector3 {
|
||||
return .{
|
||||
.x = scalar,
|
||||
.y = scalar,
|
||||
.z = scalar,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn from_scalars(scalars: Scalars) Vector3 {
|
||||
return .{
|
||||
.x = scalars[0],
|
||||
.y = scalars[1],
|
||||
.z = scalars[2],
|
||||
};
|
||||
}
|
||||
|
||||
pub fn scalar_added(self: Vector3, scalar: f32) Vector3 {
|
||||
return .{
|
||||
.x = self.x + scalar,
|
||||
.y = self.y + scalar,
|
||||
.z = self.z + scalar,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn scalar_divided(self: Vector3, scalar: f32) Vector3 {
|
||||
return .{
|
||||
.x = self.x / scalar,
|
||||
.y = self.y / scalar,
|
||||
.z = self.z / scalar,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn scalar_multiplied(self: Vector3, scalar: f32) Vector3 {
|
||||
return .{
|
||||
.x = self.x * scalar,
|
||||
.y = self.y * scalar,
|
||||
.z = self.z * scalar,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn scalar_subtracted(self: Vector3, scalar: f32) Vector3 {
|
||||
return .{
|
||||
.x = self.x - scalar,
|
||||
.y = self.y - scalar,
|
||||
.z = self.z - scalar,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn to_scalars(self: Vector3) Scalars {
|
||||
return .{self.x, self.y, self.z};
|
||||
}
|
||||
|
||||
pub fn vector_added(self: Vector3, other: Vector3) Vector3 {
|
||||
return .{
|
||||
.x = self.x + other.x,
|
||||
.y = self.y + other.y,
|
||||
.z = self.z + other.z,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vector_divided(self: Vector3, other: Vector3) Vector3 {
|
||||
return .{
|
||||
.x = self.x / other.x,
|
||||
.y = self.y / other.y,
|
||||
.z = self.z / other.z,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vector_multiplied(self: Vector3, other: Vector3) Vector3 {
|
||||
return .{
|
||||
.x = self.x * other.x,
|
||||
.y = self.y * other.y,
|
||||
.z = self.z * other.z,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vector_subtracted(self: Vector3, other: Vector3) Vector3 {
|
||||
return .{
|
||||
.x = self.x - other.x,
|
||||
.y = self.y - other.y,
|
||||
.z = self.z - other.z,
|
||||
};
|
||||
}
|
||||
};
|
|
@ -4,14 +4,16 @@ const ext = @import("./ext.zig");
|
|||
|
||||
pub const file = @import("./file.zig");
|
||||
|
||||
const heap = @import("./heap.zig");
|
||||
pub const heap = @import("./heap.zig");
|
||||
|
||||
const kym = @import("./kym.zig");
|
||||
pub const script = @import("./script.zig");
|
||||
|
||||
const App = struct {
|
||||
renderer: *ext.SDL_Renderer,
|
||||
pub const GraphicsLoop = opaque {
|
||||
const Internal = struct {
|
||||
window: *ext.SDL_Window,
|
||||
systems: Systems,
|
||||
renderer: *ext.SDL_Renderer,
|
||||
loaders: GraphicsLoaderList,
|
||||
updaters: GraphicsUpdaterList,
|
||||
target_frame_time: f64,
|
||||
|
||||
keys: struct {
|
||||
|
@ -20,70 +22,16 @@ const App = struct {
|
|||
held: [ext.SDL_NUM_SCANCODES]bool = [_]bool{false} ** ext.SDL_NUM_SCANCODES,
|
||||
},
|
||||
|
||||
const DynamicObject = struct {
|
||||
self: *App,
|
||||
const GraphicsLoaderList = coral.list.Stack(GraphicsLoader);
|
||||
|
||||
fn get(context: kym.Typeinfo.GetContext) kym.RuntimeError!?*kym.RuntimeObj {
|
||||
const index = context.get_index();
|
||||
const GraphicsUpdaterList = coral.list.Stack(GraphicsUpdater);
|
||||
|
||||
defer context.env.release(index);
|
||||
|
||||
const on_update_symbol = (try context.env.new_symbol("on_update")).pop().?;
|
||||
|
||||
defer context.env.release(on_update_symbol);
|
||||
|
||||
if (index.equals(on_update_symbol)) {
|
||||
return (try context.env.new_syscall(on_update)).pop();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
fn on_update(env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeObj {
|
||||
const caller_object = try env.expect_object(try env.get_caller());
|
||||
|
||||
defer env.release(caller_object);
|
||||
|
||||
const app = @as(*DynamicObject, @ptrCast(@alignCast(try env.expect_dynamic(caller_object, typeinfo))));
|
||||
|
||||
const callable = try env.expect_object((try env.arg(0)).pop());
|
||||
|
||||
errdefer env.release(callable);
|
||||
|
||||
try app.self.systems.push_one(callable);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const typeinfo = &kym.Typeinfo{
|
||||
.name = "ona",
|
||||
.size = @sizeOf(DynamicObject),
|
||||
.get = get,
|
||||
};
|
||||
};
|
||||
|
||||
pub const InitError = error {
|
||||
RendererUnavailable,
|
||||
WindowUnavailable,
|
||||
};
|
||||
|
||||
const Systems = coral.list.Stack(*kym.RuntimeObj);
|
||||
|
||||
pub fn as_importer(self: *App) kym.Importer {
|
||||
return kym.Importer.bind(App, self, import);
|
||||
}
|
||||
|
||||
pub fn deinit(self: *App) void {
|
||||
fn deinit(self: *Internal) void {
|
||||
ext.SDL_DestroyWindow(self.window);
|
||||
ext.SDL_DestroyRenderer(self.renderer);
|
||||
self.systems.deinit();
|
||||
}
|
||||
|
||||
fn import(self: *App, env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeObj {
|
||||
return (try env.new_dynamic(DynamicObject.typeinfo, DynamicObject{.self = self})).pop();
|
||||
}
|
||||
|
||||
pub fn init() InitError!App {
|
||||
fn init(allocator: coral.io.Allocator) coral.io.AllocationError!Internal {
|
||||
const window = create: {
|
||||
const height = 480;
|
||||
const width = 640;
|
||||
|
@ -91,7 +39,7 @@ const App = struct {
|
|||
const flags = ext.SDL_WINDOW_HIDDEN;
|
||||
|
||||
break: create ext.SDL_CreateWindow("", pos, pos, width, height, flags) orelse {
|
||||
return error.WindowUnavailable;
|
||||
return error.OutOfMemory;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -102,85 +50,41 @@ const App = struct {
|
|||
const flags = ext.SDL_RENDERER_ACCELERATED;
|
||||
|
||||
break: create ext.SDL_CreateRenderer(window, default_driver_index, flags) orelse {
|
||||
return error.RendererUnavailable;
|
||||
return error.OutOfMemory;
|
||||
};
|
||||
};
|
||||
|
||||
errdefer ext.SDL_DestroyRenderer(renderer);
|
||||
|
||||
return .{
|
||||
.systems = Systems.init(heap.allocator),
|
||||
.loaders = GraphicsLoaderList.init(allocator),
|
||||
.updaters = GraphicsUpdaterList.init(allocator),
|
||||
.target_frame_time = 1.0 / 60.0,
|
||||
.renderer = renderer,
|
||||
.window = window,
|
||||
.renderer = renderer,
|
||||
.keys = .{},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn load(self: *App, env: *kym.RuntimeEnv) kym.RuntimeError!void {
|
||||
var previous_width = @as(c_int, 0);
|
||||
var previous_height = @as(c_int, 0);
|
||||
fn run(self: *Internal) void {
|
||||
var ticks_previous = ext.SDL_GetTicks64();
|
||||
var accumulated_time = @as(f64, 0);
|
||||
|
||||
ext.SDL_GetWindowSize(self.window, &previous_width, &previous_height);
|
||||
for (self.loaders.values) |loader| {
|
||||
loader.invoke(@ptrCast(self));
|
||||
}
|
||||
|
||||
var width = previous_width;
|
||||
var height = previous_height;
|
||||
|
||||
defer {
|
||||
const pos = ext.SDL_WINDOWPOS_CENTERED;
|
||||
|
||||
ext.SDL_SetWindowSize(self.window, width, height);
|
||||
ext.SDL_SetWindowPosition(self.window, pos, pos);
|
||||
ext.SDL_ShowWindow(self.window);
|
||||
}
|
||||
|
||||
if ((try env.import(file.Path.of(&.{"app.ona"}))).pop()) |manifest| {
|
||||
defer env.release(manifest);
|
||||
running_loop: while (true) {
|
||||
const ticks_current = ext.SDL_GetTicks64();
|
||||
const milliseconds_per_second = 1000.0;
|
||||
const delta_time = @as(f64, @floatFromInt(ticks_current - ticks_previous)) / milliseconds_per_second;
|
||||
|
||||
if (try kym.get_field(env, manifest, "width")) |object| {
|
||||
defer env.release(object);
|
||||
ticks_previous = ticks_current;
|
||||
accumulated_time += delta_time;
|
||||
|
||||
const fixed = try env.expect_fixed(object);
|
||||
|
||||
if (fixed > 0 and fixed < coral.math.max_int(@typeInfo(@TypeOf(width)).Int)) {
|
||||
width = @intCast(fixed);
|
||||
}
|
||||
}
|
||||
|
||||
if (try kym.get_field(env, manifest, "height")) |object| {
|
||||
defer env.release(object);
|
||||
|
||||
const fixed = try env.expect_fixed(object);
|
||||
|
||||
if (fixed > 0 and fixed < coral.math.max_int(@typeInfo(@TypeOf(height)).Int)) {
|
||||
height = @intCast(fixed);
|
||||
}
|
||||
}
|
||||
|
||||
if (try kym.get_field(env, manifest, "tick_rate")) |object| {
|
||||
defer env.release(object);
|
||||
|
||||
self.target_frame_time = 1.0 / try env.expect_float(object);
|
||||
}
|
||||
|
||||
if (try kym.get_field(env, manifest, "title")) |object| {
|
||||
defer env.release(object);
|
||||
|
||||
const title = coral.utf8.SmallString.from_units(try env.expect_string(object)) catch |from_error| {
|
||||
return switch (from_error) {
|
||||
error.InvalidUtf8 => env.raise(error.TypeMismatch, "`title` cannot contain invalid utf8", .{}),
|
||||
error.TooBig => env.raise(error.TypeMismatch, "`title` is too long", .{}),
|
||||
};
|
||||
};
|
||||
|
||||
ext.SDL_SetWindowTitle(self.window, title.to_units());
|
||||
} else {
|
||||
ext.SDL_SetWindowTitle(self.window, "Ona");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn poll(self: *App) bool {
|
||||
while (accumulated_time >= self.target_frame_time) : (accumulated_time -= self.target_frame_time) {
|
||||
var event = @as(ext.SDL_Event, undefined);
|
||||
|
||||
coral.io.zero(@ptrCast(&self.keys.pressed));
|
||||
|
@ -188,7 +92,7 @@ const App = struct {
|
|||
|
||||
while (ext.SDL_PollEvent(&event) != 0) {
|
||||
switch (event.type) {
|
||||
ext.SDL_QUIT => return false,
|
||||
ext.SDL_QUIT => break: running_loop,
|
||||
|
||||
ext.SDL_KEYDOWN => {
|
||||
self.keys.pressed[event.key.keysym.scancode] = true;
|
||||
|
@ -204,30 +108,78 @@ const App = struct {
|
|||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
for (self.updaters.values) |updater| {
|
||||
updater.invoke(.{
|
||||
.loop = @ptrCast(self),
|
||||
.delta_time = self.target_frame_time,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(self: *App) void {
|
||||
_ = ext.SDL_SetRenderDrawColor(self.renderer, 0, 0, 0, 255);
|
||||
_ = ext.SDL_RenderClear(self.renderer);
|
||||
_ = ext.SDL_RenderPresent(self.renderer);
|
||||
|
||||
ext.SDL_Delay(1);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(self: *App, env: *kym.RuntimeEnv, time_delta: f32) kym.RuntimeError!void {
|
||||
for (self.systems.values) |system| {
|
||||
(try (try (try env.new_float(time_delta)).push(system)).call(1, null)).discard();
|
||||
fn use(self: *Internal, middleware: GraphicsMiddleware) coral.io.AllocationError!void {
|
||||
try switch (middleware) {
|
||||
.loader => |loader| self.loaders.push_one(loader),
|
||||
.updater => |updater| self.updaters.push_one(updater),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
fn internal(self: *GraphicsLoop) *Internal {
|
||||
return @ptrCast(@alignCast(self));
|
||||
}
|
||||
|
||||
pub fn is_key_pressed(self: *GraphicsLoop, key: Key) bool {
|
||||
return self.internal().keys.pressed[@intFromEnum(key)];
|
||||
}
|
||||
|
||||
pub fn set_resolution(self: *GraphicsLoop, width: u16, height: u16) callconv(.C) void {
|
||||
const pos = ext.SDL_WINDOWPOS_CENTERED;
|
||||
const internal_state = self.internal();
|
||||
|
||||
ext.SDL_SetWindowSize(internal_state.window, width, height);
|
||||
ext.SDL_SetWindowPosition(internal_state.window, pos, pos);
|
||||
ext.SDL_ShowWindow(internal_state.window);
|
||||
}
|
||||
|
||||
pub fn set_tick_rate(self: *GraphicsLoop, tick_rate: f64) callconv(.C) void {
|
||||
self.internal().target_frame_time = 1.0 / tick_rate;
|
||||
}
|
||||
|
||||
pub fn set_title(self: *GraphicsLoop, title: coral.utf8.SmallString) callconv(.C) void {
|
||||
ext.SDL_SetWindowTitle(self.internal().window, title.to_units());
|
||||
}
|
||||
};
|
||||
|
||||
pub const GraphicsUpdate = struct {
|
||||
loop: *GraphicsLoop,
|
||||
delta_time: f64,
|
||||
};
|
||||
|
||||
pub const GraphicsMiddleware = union (enum) {
|
||||
loader: GraphicsLoader,
|
||||
updater: GraphicsUpdater,
|
||||
};
|
||||
|
||||
pub const GraphicsLoader = coral.io.Generator(void, *GraphicsLoop);
|
||||
|
||||
pub const GraphicsUpdater = coral.io.Generator(void, GraphicsUpdate);
|
||||
|
||||
pub const Key = enum (usize) {
|
||||
f5 = ext.SDL_SCANCODE_F5,
|
||||
};
|
||||
|
||||
fn last_sdl_error() [:0]const u8 {
|
||||
return coral.io.slice_sentineled(@as(u8, 0), @as([*:0]const u8, @ptrCast(ext.SDL_GetError())));
|
||||
}
|
||||
|
||||
fn load_prompt() void {
|
||||
|
||||
}
|
||||
|
||||
pub fn log_info(message: []const coral.io.Byte) void {
|
||||
ext.SDL_LogInfo(
|
||||
ext.SDL_LOG_CATEGORY_APPLICATION,
|
||||
|
@ -255,59 +207,32 @@ pub fn log_fail(message: []const coral.io.Byte) void {
|
|||
);
|
||||
}
|
||||
|
||||
const milliseconds_per_second = 1000;
|
||||
pub fn start_graphics(middlewares: []const GraphicsMiddleware) void {
|
||||
const init_flags = ext.SDL_INIT_EVERYTHING;
|
||||
|
||||
pub fn run_app(file_access: file.Access) void {
|
||||
defer heap.trace_leaks();
|
||||
if (ext.SDL_WasInit(init_flags) == init_flags) {
|
||||
return log_fail("a loop is already running");
|
||||
}
|
||||
|
||||
if (ext.SDL_Init(ext.SDL_INIT_EVERYTHING) != 0) {
|
||||
if (ext.SDL_Init(init_flags) != 0) {
|
||||
return log_fail(last_sdl_error());
|
||||
}
|
||||
|
||||
defer ext.SDL_Quit();
|
||||
|
||||
var app = App.init() catch |init_error| {
|
||||
return log_fail(switch (init_error) {
|
||||
error.WindowUnavailable => "unable to acquire window",
|
||||
error.RendererUnavailable => "unable to acquire renderer",
|
||||
var loop_internal = GraphicsLoop.Internal.init(heap.allocator) catch {
|
||||
return log_fail(last_sdl_error());
|
||||
};
|
||||
|
||||
defer loop_internal.deinit();
|
||||
|
||||
for (middlewares) |middleware| {
|
||||
loop_internal.use(middleware) catch |allocation_error| {
|
||||
return log_fail(switch (allocation_error) {
|
||||
error.OutOfMemory => "could not use middleware: out of memory",
|
||||
});
|
||||
};
|
||||
|
||||
defer app.deinit();
|
||||
|
||||
var script_env = kym.RuntimeEnv.init(heap.allocator, 255, .{
|
||||
.import_access = file_access,
|
||||
.print_out = log_info,
|
||||
.print_err = log_fail,
|
||||
}) catch {
|
||||
return log_fail("failed to initialize script runtime");
|
||||
};
|
||||
|
||||
defer script_env.deinit();
|
||||
|
||||
script_env.override_import(file.Path.of(&.{"ona"}), app.as_importer()) catch return;
|
||||
|
||||
app.load(&script_env) catch load_prompt();
|
||||
|
||||
var ticks_previous = ext.SDL_GetTicks64();
|
||||
var accumulated_time = @as(f64, 0);
|
||||
|
||||
running_loop: while (true) {
|
||||
const ticks_current = ext.SDL_GetTicks64();
|
||||
const delta_time = @as(f64, @floatFromInt(ticks_current - ticks_previous)) / milliseconds_per_second;
|
||||
|
||||
ticks_previous = ticks_current;
|
||||
accumulated_time += delta_time;
|
||||
|
||||
while (accumulated_time >= app.target_frame_time) : (accumulated_time -= app.target_frame_time) {
|
||||
if (!app.poll()) {
|
||||
break: running_loop;
|
||||
}
|
||||
|
||||
app.update(&script_env, @floatCast(app.target_frame_time)) catch return;
|
||||
}
|
||||
|
||||
app.render();
|
||||
ext.SDL_Delay(1);
|
||||
}
|
||||
loop_internal.run();
|
||||
}
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
const Chunk = @import("./kym/Chunk.zig");
|
||||
const Chunk = @import("./script/Chunk.zig");
|
||||
|
||||
const Table = @import("./kym/Table.zig");
|
||||
const Table = @import("./script/Table.zig");
|
||||
|
||||
const coral = @import("coral");
|
||||
|
||||
const file = @import("./file.zig");
|
||||
|
||||
const gfx = @import("./gfx.zig");
|
||||
const tokens = @import("./script/tokens.zig");
|
||||
|
||||
const tokens = @import("./kym/tokens.zig");
|
||||
|
||||
const tree = @import("./kym/tree.zig");
|
||||
const tree = @import("./script/tree.zig");
|
||||
|
||||
///
|
||||
/// Fixed-length integer number type.
|
||||
|
@ -78,15 +76,7 @@ pub const RuntimeEnv = struct {
|
|||
}
|
||||
});
|
||||
|
||||
const ImporterTable = coral.map.Table(file.Path, Importer, struct {
|
||||
pub fn hash(file_path: file.Path) usize {
|
||||
return coral.io.djb2_hash(@typeInfo(usize).Int, file_path.to_bytes());
|
||||
}
|
||||
|
||||
pub fn equals(a: file.Path, b: file.Path) bool {
|
||||
return coral.io.are_equal(a.to_bytes(), b.to_bytes());
|
||||
}
|
||||
});
|
||||
const ImporterTable = coral.map.StringTable(Importer);
|
||||
|
||||
const LocalList = coral.list.Stack(?*RuntimeObj);
|
||||
|
||||
|
@ -94,13 +84,22 @@ pub const RuntimeEnv = struct {
|
|||
/// Optional settings for a [RuntimeEnv].
|
||||
///
|
||||
pub const Options = struct {
|
||||
import_access: file.Access = .null,
|
||||
import_access: file.Access = .{.sandboxed_path = &file.Path.cwd},
|
||||
print_out: ?*const Print = null,
|
||||
print_err: ?*const Print = null,
|
||||
};
|
||||
|
||||
const SymbolSet = coral.map.StringTable([:0]coral.io.Byte);
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
pub fn acquire(_: *RuntimeEnv, object: *RuntimeObj) *RuntimeObj {
|
||||
object.internal().ref_count += 1;
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
///
|
||||
/// Attempts to push the argument located at `arg_index` to the top of `self`, pushing `null` instead if the given
|
||||
/// argument does not exist or was provided as a nil value.
|
||||
|
@ -148,10 +147,21 @@ pub const RuntimeEnv = struct {
|
|||
|
||||
defer self.release(callable);
|
||||
|
||||
if (callable.is_dynamic(Chunk.typeinfo)) |chunk_userdata| {
|
||||
const arity = @as(*Chunk, @ptrCast(@alignCast(chunk_userdata))).arity;
|
||||
|
||||
if (arg_count < arity) {
|
||||
return self.raise(error.BadOperation, "expected {expected} arguments, {provided} provided", .{
|
||||
.expected = arity,
|
||||
.provided = arg_count,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const result = get_result: {
|
||||
try self.frames.push_one(.{
|
||||
.locals_top = self.locals.values.len - arg_count,
|
||||
.callable = callable.internal().acquire(),
|
||||
.callable = self.acquire(callable),
|
||||
.caller = caller,
|
||||
.arg_count = arg_count,
|
||||
});
|
||||
|
@ -160,7 +170,7 @@ pub const RuntimeEnv = struct {
|
|||
const popped_frame = self.frames.pop().?;
|
||||
|
||||
self.release(popped_frame.callable);
|
||||
coral.debug.assert(popped_frame.locals_top < self.locals.values.len);
|
||||
coral.debug.assert(popped_frame.locals_top <= self.locals.values.len);
|
||||
|
||||
var to_discard = self.locals.values.len - popped_frame.locals_top;
|
||||
|
||||
|
@ -530,8 +540,8 @@ pub const RuntimeEnv = struct {
|
|||
self.new_float(@as(Float, @floatFromInt(lhs_fixed)) + @as(Float, @floatFromInt(rhs_fixed))),
|
||||
|
||||
.float => |lhs_float| self.new_float(lhs_float + @as(Float, @floatFromInt(rhs_fixed))),
|
||||
.vector2 => |lhs_vector2| self.new_vector2(lhs_vector2.scalar_added(@floatFromInt(rhs_fixed))),
|
||||
.vector3 => |lhs_vector3| self.new_vector3(lhs_vector3.scalar_added(@floatFromInt(rhs_fixed))),
|
||||
.vector2 => |lhs_vector2| self.new_vector2(lhs_vector2 + @as(Vector2, @splat(@floatFromInt(rhs_fixed)))),
|
||||
.vector3 => |lhs_vector3| self.new_vector3(lhs_vector3 + @as(Vector3, @splat(@floatFromInt(rhs_fixed)))),
|
||||
|
||||
else => self.raise(error.TypeMismatch, "fixed types are not addable with {typename}", .{
|
||||
.typename = addable.typename(),
|
||||
|
@ -558,8 +568,8 @@ pub const RuntimeEnv = struct {
|
|||
return switch (addable.internal().payload) {
|
||||
.fixed => |lhs_fixed| self.new_float(@as(Float, @floatFromInt(lhs_fixed)) / @as(Float, @floatFromInt(rhs_fixed))),
|
||||
.float => |lhs_float| self.new_float(lhs_float / @as(Float, @floatFromInt(rhs_fixed))),
|
||||
.vector2 => |lhs_vector2| self.new_vector2(lhs_vector2.scalar_divided(@floatFromInt(rhs_fixed))),
|
||||
.vector3 => |lhs_vector3| self.new_vector3(lhs_vector3.scalar_divided(@floatFromInt(rhs_fixed))),
|
||||
.vector2 => |lhs_vector2| self.new_vector2(lhs_vector2 / @as(Vector2, @splat(@floatFromInt(rhs_fixed)))),
|
||||
.vector3 => |lhs_vector3| self.new_vector3(lhs_vector3 / @as(Vector3, @splat(@floatFromInt(rhs_fixed)))),
|
||||
|
||||
else => self.raise(error.TypeMismatch, "fixed types are not divisible with {typename}", .{
|
||||
.typename = addable.typename(),
|
||||
|
@ -587,8 +597,8 @@ pub const RuntimeEnv = struct {
|
|||
self.new_float(@as(Float, @floatFromInt(lhs_fixed)) * @as(Float, @floatFromInt(rhs_fixed))),
|
||||
|
||||
.float => |lhs_float| self.new_float(lhs_float + @as(Float, @floatFromInt(rhs_fixed))),
|
||||
.vector2 => |lhs_vector2| self.new_vector2(lhs_vector2.scalar_multiplied(@floatFromInt(rhs_fixed))),
|
||||
.vector3 => |lhs_vector3| self.new_vector3(lhs_vector3.scalar_multiplied(@floatFromInt(rhs_fixed))),
|
||||
.vector2 => |lhs_vector2| self.new_vector2(lhs_vector2 * @as(Vector2, @splat(@floatFromInt(rhs_fixed)))),
|
||||
.vector3 => |lhs_vector3| self.new_vector3(lhs_vector3 * @as(Vector3, @splat(@floatFromInt(rhs_fixed)))),
|
||||
|
||||
else => self.raise(error.TypeMismatch, "fixed types are not multiplicable with {typename}", .{
|
||||
.typename = addable.typename(),
|
||||
|
@ -616,8 +626,8 @@ pub const RuntimeEnv = struct {
|
|||
self.new_float(@as(Float, @floatFromInt(lhs_fixed)) - @as(Float, @floatFromInt(rhs_fixed))),
|
||||
|
||||
.float => |lhs_float| self.new_float(lhs_float + @as(Float, @floatFromInt(rhs_fixed))),
|
||||
.vector2 => |lhs_vector2| self.new_vector2(lhs_vector2.scalar_subtracted(@floatFromInt(rhs_fixed))),
|
||||
.vector3 => |lhs_vector3| self.new_vector3(lhs_vector3.scalar_subtracted(@floatFromInt(rhs_fixed))),
|
||||
.vector2 => |lhs_vector2| self.new_vector2(lhs_vector2 - @as(Vector2, @splat(@floatFromInt(rhs_fixed)))),
|
||||
.vector3 => |lhs_vector3| self.new_vector3(lhs_vector3 - @as(Vector3, @splat(@floatFromInt(rhs_fixed)))),
|
||||
|
||||
else => self.raise(error.TypeMismatch, "fixed types are not multiplicable with {typename}", .{
|
||||
.typename = addable.typename(),
|
||||
|
@ -640,8 +650,8 @@ pub const RuntimeEnv = struct {
|
|||
return switch (addable.internal().payload) {
|
||||
.fixed => |lhs_fixed| self.new_float(@as(Float, @floatFromInt(lhs_fixed)) + rhs_float),
|
||||
.float => |lhs_float| self.new_float(lhs_float + rhs_float),
|
||||
.vector2 => |lhs_vector2| self.new_vector2(lhs_vector2.scalar_added(@floatCast(rhs_float))),
|
||||
.vector3 => |lhs_vector3| self.new_vector3(lhs_vector3.scalar_added(@floatCast(rhs_float))),
|
||||
.vector2 => |lhs_vector2| self.new_vector2(lhs_vector2 + @as(Vector2, @splat(@floatCast(rhs_float)))),
|
||||
.vector3 => |lhs_vector3| self.new_vector3(lhs_vector3 + @as(Vector3, @splat(@floatCast(rhs_float)))),
|
||||
|
||||
else => self.raise(error.TypeMismatch, "fixed types are not addable with {typename}", .{
|
||||
.typename = addable.typename(),
|
||||
|
@ -668,8 +678,8 @@ pub const RuntimeEnv = struct {
|
|||
return switch (addable.internal().payload) {
|
||||
.fixed => |lhs_fixed| self.new_float(@as(Float, @floatFromInt(lhs_fixed)) / rhs_float),
|
||||
.float => |lhs_float| self.new_float(lhs_float / lhs_float),
|
||||
.vector2 => |lhs_vector2| self.new_vector2(lhs_vector2.scalar_divided(@floatCast(rhs_float))),
|
||||
.vector3 => |lhs_vector3| self.new_vector3(lhs_vector3.scalar_divided(@floatCast(rhs_float))),
|
||||
.vector2 => |lhs_vector2| self.new_vector2(lhs_vector2 / @as(Vector2, @splat(@floatCast(rhs_float)))),
|
||||
.vector3 => |lhs_vector3| self.new_vector3(lhs_vector3 / @as(Vector3, @splat(@floatCast(rhs_float)))),
|
||||
|
||||
else => self.raise(error.TypeMismatch, "fixed types are not divisible with {typename}", .{
|
||||
.typename = addable.typename(),
|
||||
|
@ -693,8 +703,8 @@ pub const RuntimeEnv = struct {
|
|||
return switch (addable.internal().payload) {
|
||||
.fixed => |lhs_fixed| self.new_float(@as(Float, @floatFromInt(lhs_fixed)) * rhs_float),
|
||||
.float => |lhs_float| self.new_float(lhs_float * lhs_float),
|
||||
.vector2 => |lhs_vector2| self.new_vector2(lhs_vector2.scalar_multiplied(@floatCast(rhs_float))),
|
||||
.vector3 => |lhs_vector3| self.new_vector3(lhs_vector3.scalar_multiplied(@floatCast(rhs_float))),
|
||||
.vector2 => |lhs_vector2| self.new_vector2(lhs_vector2 * @as(Vector2, @splat(@floatCast(rhs_float)))),
|
||||
.vector3 => |lhs_vector3| self.new_vector3(lhs_vector3 * @as(Vector3, @splat(@floatCast(rhs_float)))),
|
||||
|
||||
else => self.raise(error.TypeMismatch, "fixed types are not multiplicable with {typename}", .{
|
||||
.typename = addable.typename(),
|
||||
|
@ -718,8 +728,8 @@ pub const RuntimeEnv = struct {
|
|||
return switch (addable.internal().payload) {
|
||||
.fixed => |lhs_fixed| self.new_float(@as(Float, @floatFromInt(lhs_fixed)) - rhs_float),
|
||||
.float => |lhs_float| self.new_float(lhs_float - lhs_float),
|
||||
.vector2 => |lhs_vector2| self.new_vector2(lhs_vector2.scalar_subtracted(@floatCast(rhs_float))),
|
||||
.vector3 => |lhs_vector3| self.new_vector3(lhs_vector3.scalar_subtracted(@floatCast(rhs_float))),
|
||||
.vector2 => |lhs_vector2| self.new_vector2(lhs_vector2 - @as(Vector2, @splat(@floatCast(rhs_float)))),
|
||||
.vector3 => |lhs_vector3| self.new_vector3(lhs_vector3 - @as(Vector3, @splat(@floatCast(rhs_float)))),
|
||||
|
||||
else => self.raise(error.TypeMismatch, "fixed types are not multiplicable with {typename}", .{
|
||||
.typename = addable.typename(),
|
||||
|
@ -742,7 +752,7 @@ pub const RuntimeEnv = struct {
|
|||
};
|
||||
|
||||
if (frame.caller) |object| {
|
||||
return object.internal().acquire();
|
||||
return self.acquire(object);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -776,18 +786,13 @@ pub const RuntimeEnv = struct {
|
|||
}
|
||||
|
||||
swizzle_buffer[swizzle_count] = switch (swizzle_symbol[swizzle_count]) {
|
||||
'x' => vector2.x,
|
||||
'y' => vector2.y,
|
||||
'x' => vector2[0],
|
||||
'y' => vector2[1],
|
||||
|
||||
0 => return switch (swizzle_count) {
|
||||
1 => self.new_float(swizzle_buffer[0]),
|
||||
|
||||
2 => self.new_vector2(.{
|
||||
.x = swizzle_buffer[0],
|
||||
.y = swizzle_buffer[1],
|
||||
}),
|
||||
|
||||
3 => self.new_vector3(Vector3.from_scalars(swizzle_buffer)),
|
||||
2 => self.new_vector2(.{swizzle_buffer[0], swizzle_buffer[1]}),
|
||||
3 => self.new_vector3(swizzle_buffer),
|
||||
else => unreachable,
|
||||
},
|
||||
|
||||
|
@ -811,19 +816,14 @@ pub const RuntimeEnv = struct {
|
|||
}
|
||||
|
||||
swizzle_buffer[swizzle_count] = switch (swizzle_symbol[swizzle_count]) {
|
||||
'x' => vector3.x,
|
||||
'y' => vector3.y,
|
||||
'z' => vector3.z,
|
||||
'x' => vector3[0],
|
||||
'y' => vector3[1],
|
||||
'z' => vector3[2],
|
||||
|
||||
0 => return switch (swizzle_count) {
|
||||
1 => self.new_float(swizzle_buffer[0]),
|
||||
|
||||
2 => self.new_vector2(.{
|
||||
.x = swizzle_buffer[0],
|
||||
.y = swizzle_buffer[1],
|
||||
}),
|
||||
|
||||
3 => self.new_vector3(Vector3.from_scalars(swizzle_buffer)),
|
||||
2 => self.new_vector2(.{swizzle_buffer[0], swizzle_buffer[1]}),
|
||||
3 => self.new_vector3(swizzle_buffer),
|
||||
else => unreachable,
|
||||
},
|
||||
|
||||
|
@ -897,7 +897,9 @@ pub const RuntimeEnv = struct {
|
|||
/// `self` is returned for function chaining.
|
||||
///
|
||||
pub fn import(self: *RuntimeEnv, file_path: file.Path) RuntimeError!*RuntimeEnv {
|
||||
if (self.importer_overrides.lookup(file_path)) |importer| {
|
||||
const file_name = file_path.to_bytes();
|
||||
|
||||
if (self.importer_overrides.lookup(file_name)) |importer| {
|
||||
if (try importer.invoke(self)) |object| {
|
||||
errdefer self.release(object);
|
||||
|
||||
|
@ -910,13 +912,8 @@ pub const RuntimeEnv = struct {
|
|||
}
|
||||
|
||||
const callable = new_chunk: {
|
||||
const file_name = file_path.to_bytes();
|
||||
|
||||
const file_data =
|
||||
(try file.allocate_and_load(self.allocator, self.options.import_access, file_path)) orelse {
|
||||
return self.raise(error.BadOperation, "failed to open or read `{name}`", .{
|
||||
.name = file_name,
|
||||
});
|
||||
const file_data = (try file.allocate_and_load(self.allocator, self.options.import_access, file_path)) orelse {
|
||||
return self.raise(error.BadOperation, "failed to open or read `{name}`", .{.name = file_name});
|
||||
};
|
||||
|
||||
defer self.allocator.deallocate(file_data);
|
||||
|
@ -933,7 +930,7 @@ pub const RuntimeEnv = struct {
|
|||
self.print_err(error_message);
|
||||
}
|
||||
|
||||
return self.raise(parse_error, "failed to parse `{name}`", .{.name = file_name});
|
||||
return self.raise(parse_error, "failed to parse `{path}`", .{.path = file_name});
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1035,7 +1032,7 @@ pub const RuntimeEnv = struct {
|
|||
self.release(previous_local);
|
||||
}
|
||||
|
||||
local.* = if (value) |object| object.internal().acquire() else null;
|
||||
local.* = if (value) |object| self.acquire(object) else null;
|
||||
}
|
||||
|
||||
///
|
||||
|
@ -1112,7 +1109,7 @@ pub const RuntimeEnv = struct {
|
|||
pub fn new_boxed(self: *RuntimeEnv, value: ?*RuntimeObj) RuntimeError!*RuntimeEnv {
|
||||
const boxed = try RuntimeObj.allocate(self.allocator, .{
|
||||
.ref_count = 1,
|
||||
.payload = .{.boxed = if (value) |object| object.internal().acquire() else null},
|
||||
.payload = .{.boxed = if (value) |object| self.acquire(object) else null},
|
||||
});
|
||||
|
||||
errdefer self.release(boxed);
|
||||
|
@ -1343,7 +1340,7 @@ pub const RuntimeEnv = struct {
|
|||
///
|
||||
/// A [RuntimeError] is returned if `self` is out of memory.
|
||||
///
|
||||
pub fn override_import(self: *RuntimeEnv, path: file.Path, importer: Importer) RuntimeError!void {
|
||||
pub fn override_import(self: *RuntimeEnv, path: []const coral.io.Byte, importer: Importer) RuntimeError!void {
|
||||
_ = try self.importer_overrides.replace(path, importer);
|
||||
}
|
||||
|
||||
|
@ -1388,7 +1385,7 @@ pub const RuntimeEnv = struct {
|
|||
///
|
||||
pub fn push(self: *RuntimeEnv, value: ?*RuntimeObj) RuntimeError!*RuntimeEnv {
|
||||
if (value) |object| {
|
||||
const acquired = object.internal().acquire();
|
||||
const acquired = self.acquire(object);
|
||||
|
||||
errdefer self.release(acquired);
|
||||
|
||||
|
@ -1542,7 +1539,7 @@ pub const RuntimeEnv = struct {
|
|||
.symbol => |symbol| self.new_string(coral.io.slice_sentineled(@as(coral.io.Byte, 0), symbol)),
|
||||
|
||||
.string => acquire: {
|
||||
try self.locals.push_one(value.internal().acquire());
|
||||
try self.locals.push_one(self.acquire(value));
|
||||
|
||||
break: acquire self;
|
||||
},
|
||||
|
@ -1550,7 +1547,11 @@ pub const RuntimeEnv = struct {
|
|||
.vector2 => |vector2| convert: {
|
||||
var string = [_:0]coral.io.Byte{0} ** 64;
|
||||
var buffer = coral.io.FixedBuffer{.bytes = &string};
|
||||
const length = coral.utf8.print_formatted(buffer.as_writer(), "@vec2({x}, {y})", vector2).?;
|
||||
|
||||
const length = coral.utf8.print_formatted(buffer.as_writer(), "@vec2({x}, {y})", .{
|
||||
.x = vector2[0],
|
||||
.y = vector2[1],
|
||||
}).?;
|
||||
|
||||
break: convert self.new_string(string[0 .. length]);
|
||||
},
|
||||
|
@ -1558,7 +1559,12 @@ pub const RuntimeEnv = struct {
|
|||
.vector3 => |vector3| convert: {
|
||||
var string = [_:0]coral.io.Byte{0} ** 96;
|
||||
var buffer = coral.io.FixedBuffer{.bytes = &string};
|
||||
const length = coral.utf8.print_formatted(buffer.as_writer(), "@vec3({x}, {y}, {z})", vector3).?;
|
||||
|
||||
const length = coral.utf8.print_formatted(buffer.as_writer(), "@vec3({x}, {y}, {z})", .{
|
||||
.x = vector3[0],
|
||||
.y = vector3[1],
|
||||
.z = vector3[2],
|
||||
}).?;
|
||||
|
||||
break: convert self.new_string(string[0 .. length]);
|
||||
},
|
||||
|
@ -1593,7 +1599,7 @@ pub const RuntimeEnv = struct {
|
|||
defer self.release(addable);
|
||||
|
||||
return if (addable.is_vector2()) |lhs_vector2|
|
||||
self.new_vector2(lhs_vector2.vector_added(rhs_vector2))
|
||||
self.new_vector2(lhs_vector2 + rhs_vector2)
|
||||
else
|
||||
self.raise(error.TypeMismatch, "vector2 types are not addable with {typename}", .{
|
||||
.typename = addable.typename(),
|
||||
|
@ -1609,7 +1615,7 @@ pub const RuntimeEnv = struct {
|
|||
/// `self` is returned for function chaining.
|
||||
///
|
||||
pub fn vector2_divide(self: *RuntimeEnv, rhs_vector2: Vector2) RuntimeError!*RuntimeEnv {
|
||||
if (rhs_vector2.x == 0 or rhs_vector2.y == 0) {
|
||||
if (rhs_vector2[0] == 0 or rhs_vector2[1] == 0) {
|
||||
return self.raise(error.TypeMismatch, "cannot divide by zero", .{});
|
||||
}
|
||||
|
||||
|
@ -1618,7 +1624,7 @@ pub const RuntimeEnv = struct {
|
|||
defer self.release(addable);
|
||||
|
||||
return if (addable.is_vector2()) |lhs_vector2|
|
||||
self.new_vector2(lhs_vector2.vector_divided(rhs_vector2))
|
||||
self.new_vector2(lhs_vector2 / rhs_vector2)
|
||||
else
|
||||
self.raise(error.TypeMismatch, "vector2 types are not divisible with {typename}", .{
|
||||
.typename = addable.typename(),
|
||||
|
@ -1639,7 +1645,7 @@ pub const RuntimeEnv = struct {
|
|||
defer self.release(addable);
|
||||
|
||||
return if (addable.is_vector2()) |lhs_vector2|
|
||||
self.new_vector2(lhs_vector2.vector_multiplied(rhs_vector2))
|
||||
self.new_vector2(lhs_vector2 * rhs_vector2)
|
||||
else
|
||||
self.raise(error.TypeMismatch, "vector2 types are not multiplicable with {typename}", .{
|
||||
.typename = addable.typename(),
|
||||
|
@ -1660,7 +1666,7 @@ pub const RuntimeEnv = struct {
|
|||
defer self.release(addable);
|
||||
|
||||
return if (addable.is_vector2()) |lhs_vector2|
|
||||
self.new_vector2(lhs_vector2.vector_subtracted(rhs_vector2))
|
||||
self.new_vector2(lhs_vector2 - rhs_vector2)
|
||||
else
|
||||
self.raise(error.TypeMismatch, "vector2 types are not multiplicable with {typename}", .{
|
||||
.typename = addable.typename(),
|
||||
|
@ -1680,7 +1686,7 @@ pub const RuntimeEnv = struct {
|
|||
defer self.release(addable);
|
||||
|
||||
return if (addable.is_vector3()) |lhs_vector3|
|
||||
self.new_vector3(lhs_vector3.vector_added(rhs_vector3))
|
||||
self.new_vector3(lhs_vector3 + rhs_vector3)
|
||||
else
|
||||
self.raise(error.TypeMismatch, "vector3 types are not addable with {typename}", .{
|
||||
.typename = addable.typename(),
|
||||
|
@ -1696,7 +1702,7 @@ pub const RuntimeEnv = struct {
|
|||
/// `self` is returned for function chaining.
|
||||
///
|
||||
pub fn vector3_divide(self: *RuntimeEnv, rhs_vector3: Vector3) RuntimeError!*RuntimeEnv {
|
||||
if (rhs_vector3.x == 0 or rhs_vector3.y == 0 or rhs_vector3.z == 0) {
|
||||
if (rhs_vector3[0] == 0 or rhs_vector3[1] == 0 or rhs_vector3[2] == 0) {
|
||||
return self.raise(error.TypeMismatch, "cannot divide by zero", .{});
|
||||
}
|
||||
|
||||
|
@ -1705,7 +1711,7 @@ pub const RuntimeEnv = struct {
|
|||
defer self.release(addable);
|
||||
|
||||
return if (addable.is_vector3()) |lhs_vector3|
|
||||
self.new_vector3(lhs_vector3.vector_divided(rhs_vector3))
|
||||
self.new_vector3(lhs_vector3 / rhs_vector3)
|
||||
else
|
||||
self.raise(error.TypeMismatch, "vector3 types are not divisible with {typename}", .{
|
||||
.typename = addable.typename(),
|
||||
|
@ -1726,7 +1732,7 @@ pub const RuntimeEnv = struct {
|
|||
defer self.release(addable);
|
||||
|
||||
return if (addable.is_vector3()) |lhs_vector3|
|
||||
self.new_vector3(lhs_vector3.vector_multiplied(rhs_vector3))
|
||||
self.new_vector3(lhs_vector3 * rhs_vector3)
|
||||
else
|
||||
self.raise(error.TypeMismatch, "vector3 types are not multiplicable with {typename}", .{
|
||||
.typename = addable.typename(),
|
||||
|
@ -1747,7 +1753,7 @@ pub const RuntimeEnv = struct {
|
|||
defer self.release(addable);
|
||||
|
||||
return if (addable.is_vector3()) |lhs_vector3|
|
||||
self.new_vector3(lhs_vector3.vector_subtracted(rhs_vector3))
|
||||
self.new_vector3(lhs_vector3 - rhs_vector3)
|
||||
else
|
||||
self.raise(error.TypeMismatch, "vector3 types are not multiplicable with {typename}", .{
|
||||
.typename = addable.typename(),
|
||||
|
@ -1791,7 +1797,7 @@ pub const RuntimeEnv = struct {
|
|||
self.release(unboxed_object);
|
||||
}
|
||||
|
||||
unboxed_value.* = if (value) |object| object.internal().acquire() else null;
|
||||
unboxed_value.* = if (value) |object| self.acquire(object) else null;
|
||||
},
|
||||
|
||||
else => return self.raise(error.TypeMismatch, "{typename} is not unboxable", .{
|
||||
|
@ -1865,12 +1871,6 @@ pub const RuntimeObj = opaque {
|
|||
}
|
||||
},
|
||||
},
|
||||
|
||||
fn acquire(self: *Internal) *RuntimeObj {
|
||||
self.ref_count += 1;
|
||||
|
||||
return @ptrCast(self);
|
||||
}
|
||||
};
|
||||
|
||||
fn allocate(allocator: coral.io.Allocator, data: Internal) coral.io.AllocationError!*RuntimeObj {
|
||||
|
@ -1909,12 +1909,12 @@ pub const RuntimeObj = opaque {
|
|||
.boxed => |boxed| if (boxed) |object| self.equals(object) else false,
|
||||
|
||||
.vector2 => |lhs_vector2| switch (other.internal().payload) {
|
||||
.vector2 => |rhs_vector2| lhs_vector2.equals(rhs_vector2),
|
||||
.vector2 => |rhs_vector2| lhs_vector2[0] == rhs_vector2[0] and lhs_vector2[1] == rhs_vector2[1],
|
||||
else => false,
|
||||
},
|
||||
|
||||
.vector3 => |lhs_vector3| switch (other.internal().payload) {
|
||||
.vector3 => |rhs_vector3| lhs_vector3.equals(rhs_vector3),
|
||||
.vector3 => |rhs_vector3| lhs_vector3[0] == rhs_vector3[0] and lhs_vector3[1] == rhs_vector3[1] and lhs_vector3[2] == rhs_vector3[2],
|
||||
else => false,
|
||||
},
|
||||
|
||||
|
@ -1948,8 +1948,8 @@ pub const RuntimeObj = opaque {
|
|||
.float => |float| @bitCast(float),
|
||||
.fixed => |fixed| @intCast(@as(u32, @bitCast(fixed))),
|
||||
.symbol => |symbol| @intFromPtr(symbol),
|
||||
.vector2 => |vector| @bitCast(vector.to_scalars()),
|
||||
.vector3 => |vector| coral.io.jenkins_hash(@typeInfo(usize).Int, coral.io.bytes_of(&vector.to_scalars())),
|
||||
.vector2 => |vector| @bitCast(vector),
|
||||
.vector3 => |vector| coral.io.jenkins_hash(@typeInfo(usize).Int, coral.io.bytes_of(&vector)),
|
||||
.syscall => |syscall| @intFromPtr(syscall),
|
||||
.boxed => |boxed| @intFromPtr(boxed),
|
||||
.string => |string| coral.io.djb2_hash(@typeInfo(usize).Int, string.unpack()),
|
||||
|
@ -2138,7 +2138,7 @@ pub const Typeinfo = struct {
|
|||
pub fn get_index(self: *const GetContext) *RuntimeObj {
|
||||
coral.debug.assert(self.env.locals.values.len > 0);
|
||||
|
||||
return self.env.locals.values[self.env.locals.values.len - 1].?.internal().acquire();
|
||||
return self.env.acquire(self.env.locals.values[self.env.locals.values.len - 1].?);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -2161,7 +2161,7 @@ pub const Typeinfo = struct {
|
|||
pub fn get_index(self: *const SetContext) *RuntimeObj {
|
||||
coral.debug.assert(self.env.locals.values.len > 0);
|
||||
|
||||
return self.env.locals.values[self.env.locals.values.len - 1].?.internal().acquire();
|
||||
return self.env.acquire(self.env.locals.values[self.env.locals.values.len - 1].?);
|
||||
}
|
||||
|
||||
///
|
||||
|
@ -2174,7 +2174,7 @@ pub const Typeinfo = struct {
|
|||
pub fn get_value(self: *const SetContext) ?*RuntimeObj {
|
||||
coral.debug.assert(self.env.locals.values.len > 1);
|
||||
|
||||
return if (self.env.locals.values[self.env.locals.values.len - 2]) |object| object.internal().acquire() else null;
|
||||
return if (self.env.locals.values[self.env.locals.values.len - 2]) |object| self.env.acquire(object) else null;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -2206,12 +2206,12 @@ pub const Typeinfo = struct {
|
|||
///
|
||||
/// 2-component vector type.
|
||||
///
|
||||
pub const Vector2 = gfx.lina.Vector2;
|
||||
pub const Vector2 = @Vector(2, f32);
|
||||
|
||||
///
|
||||
/// 3-component vector type.
|
||||
///
|
||||
pub const Vector3 = gfx.lina.Vector3;
|
||||
pub const Vector3 = @Vector(3, f32);
|
||||
|
||||
///
|
||||
/// Higher-level wrapper for [RuntimeEnv.index_get] that makes it easier to index [Fixed] keys of objects.
|
|
@ -2,19 +2,19 @@ const coral = @import("coral");
|
|||
|
||||
const file = @import("../file.zig");
|
||||
|
||||
const kym = @import("../kym.zig");
|
||||
const script = @import("../script.zig");
|
||||
|
||||
const tokens = @import("./tokens.zig");
|
||||
|
||||
const tree = @import("./tree.zig");
|
||||
|
||||
name: *kym.RuntimeObj,
|
||||
name: *script.RuntimeObj,
|
||||
arity: u8,
|
||||
opcodes: OpcodeList,
|
||||
lines: LineList,
|
||||
cursor: usize,
|
||||
constants: ConstList,
|
||||
bindings: []?*kym.RuntimeObj,
|
||||
bindings: []?*script.RuntimeObj,
|
||||
|
||||
const Builtin = enum {
|
||||
import,
|
||||
|
@ -25,9 +25,9 @@ const Builtin = enum {
|
|||
|
||||
const Compiler = struct {
|
||||
chunk: *Self,
|
||||
env: *kym.RuntimeEnv,
|
||||
env: *script.RuntimeEnv,
|
||||
|
||||
fn compile_argument(self: *const Compiler, environment: *const tree.Environment, initial_argument: ?*const tree.Expr) kym.RuntimeError!u8 {
|
||||
fn compile_argument(self: *const Compiler, environment: *const tree.Environment, initial_argument: ?*const tree.Expr) script.RuntimeError!u8 {
|
||||
// TODO: Exceeding 255 arguments will make the VM crash.
|
||||
var maybe_argument = initial_argument;
|
||||
var argument_count = @as(u8, 0);
|
||||
|
@ -42,7 +42,7 @@ const Compiler = struct {
|
|||
return argument_count;
|
||||
}
|
||||
|
||||
fn compile_expression(self: *const Compiler, environment: *const tree.Environment, expression: *const tree.Expr, name: ?[]const coral.io.Byte) kym.RuntimeError!void {
|
||||
fn compile_expression(self: *const Compiler, environment: *const tree.Environment, expression: *const tree.Expr, name: ?[]const coral.io.Byte) script.RuntimeError!void {
|
||||
const number_format = coral.utf8.DecimalFormat{
|
||||
.delimiter = "_",
|
||||
.positive_prefix = .none,
|
||||
|
@ -57,13 +57,13 @@ const Compiler = struct {
|
|||
for (literal) |codepoint| {
|
||||
if (codepoint == '.') {
|
||||
return self.chunk.write(expression.line, .{
|
||||
.push_const = try self.declare_float(number_format.parse(literal, kym.Float) orelse unreachable),
|
||||
.push_const = try self.declare_float(number_format.parse(literal, script.Float) orelse unreachable),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
try self.chunk.write(expression.line, .{
|
||||
.push_const = try self.declare_fixed(number_format.parse(literal, kym.Fixed) orelse unreachable),
|
||||
.push_const = try self.declare_fixed(number_format.parse(literal, script.Fixed) orelse unreachable),
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -181,11 +181,10 @@ const Compiler = struct {
|
|||
|
||||
.declaration_get => |declaration_get| {
|
||||
if (get_local_index(environment, declaration_get.declaration)) |index| {
|
||||
try self.chunk.write(expression.line, .{.push_local = index});
|
||||
|
||||
if (is_declaration_boxed(declaration_get.declaration)) {
|
||||
try self.chunk.write(expression.line, .{.push_local = index});
|
||||
try self.chunk.write(expression.line, .get_box);
|
||||
} else {
|
||||
try self.chunk.write(expression.line, .{.push_local = index});
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -270,7 +269,7 @@ const Compiler = struct {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn compile_environment(self: *const Compiler, environment: *const tree.Environment) kym.RuntimeError!void {
|
||||
pub fn compile_environment(self: *const Compiler, environment: *const tree.Environment) script.RuntimeError!void {
|
||||
if (environment.statement) |statement| {
|
||||
const last_statement = try self.compile_statement(environment, statement);
|
||||
|
||||
|
@ -280,7 +279,7 @@ const Compiler = struct {
|
|||
}
|
||||
}
|
||||
|
||||
fn compile_statement(self: *const Compiler, environment: *const tree.Environment, initial_statement: *const tree.Stmt) kym.RuntimeError!*const tree.Stmt {
|
||||
fn compile_statement(self: *const Compiler, environment: *const tree.Environment, initial_statement: *const tree.Stmt) script.RuntimeError!*const tree.Stmt {
|
||||
var current_statement = initial_statement;
|
||||
|
||||
while (true) {
|
||||
|
@ -346,7 +345,7 @@ const Compiler = struct {
|
|||
|
||||
const constants_max = @as(usize, coral.math.max_int(@typeInfo(u16).Int));
|
||||
|
||||
fn declare_chunk(self: *const Compiler, chunk: Self) kym.RuntimeError!u16 {
|
||||
fn declare_chunk(self: *const Compiler, chunk: Self) script.RuntimeError!u16 {
|
||||
if (self.chunk.constants.values.len == coral.math.max_int(@typeInfo(u16).Int)) {
|
||||
return self.env.raise(error.BadSyntax, "chunks cannot contain more than {max} constants", .{
|
||||
.max = @as(usize, coral.math.max_int(@typeInfo(u16).Int)),
|
||||
|
@ -362,7 +361,7 @@ const Compiler = struct {
|
|||
return @intCast(self.chunk.constants.values.len - 1);
|
||||
}
|
||||
|
||||
fn declare_fixed(self: *const Compiler, fixed: kym.Fixed) kym.RuntimeError!u16 {
|
||||
fn declare_fixed(self: *const Compiler, fixed: script.Fixed) script.RuntimeError!u16 {
|
||||
if (self.chunk.constants.values.len == constants_max) {
|
||||
return self.env.raise(error.BadSyntax, "chunks cannot contain more than {max} constants", .{
|
||||
.max = constants_max,
|
||||
|
@ -378,7 +377,7 @@ const Compiler = struct {
|
|||
return @intCast(self.chunk.constants.values.len - 1);
|
||||
}
|
||||
|
||||
fn declare_float(self: *const Compiler, float: kym.Float) kym.RuntimeError!u16 {
|
||||
fn declare_float(self: *const Compiler, float: script.Float) script.RuntimeError!u16 {
|
||||
if (self.chunk.constants.values.len == constants_max) {
|
||||
return self.env.raise(error.BadSyntax, "chunks cannot contain more than {max} constants", .{
|
||||
.max = constants_max,
|
||||
|
@ -394,7 +393,7 @@ const Compiler = struct {
|
|||
return @intCast(self.chunk.constants.values.len - 1);
|
||||
}
|
||||
|
||||
fn declare_string(self: *const Compiler, string: []const coral.io.Byte) kym.RuntimeError!u16 {
|
||||
fn declare_string(self: *const Compiler, string: []const coral.io.Byte) script.RuntimeError!u16 {
|
||||
if (self.chunk.constants.values.len == constants_max) {
|
||||
return self.env.raise(error.BadSyntax, "chunks cannot contain more than {max} constants", .{
|
||||
.max = constants_max,
|
||||
|
@ -410,7 +409,7 @@ const Compiler = struct {
|
|||
return @intCast(self.chunk.constants.values.len - 1);
|
||||
}
|
||||
|
||||
fn declare_symbol(self: *const Compiler, symbol: []const coral.io.Byte) kym.RuntimeError!u16 {
|
||||
fn declare_symbol(self: *const Compiler, symbol: []const coral.io.Byte) script.RuntimeError!u16 {
|
||||
if (self.chunk.constants.values.len == constants_max) {
|
||||
return self.env.raise(error.BadSyntax, "chunks cannot contain more than {max} constants", .{
|
||||
.max = constants_max,
|
||||
|
@ -426,7 +425,7 @@ const Compiler = struct {
|
|||
return @intCast(self.chunk.constants.values.len - 1);
|
||||
}
|
||||
|
||||
fn get_binding_index(environment: *const tree.Environment, declaration: *const tree.Declaration) kym.RuntimeError!?u8 {
|
||||
fn get_binding_index(environment: *const tree.Environment, declaration: *const tree.Declaration) script.RuntimeError!?u8 {
|
||||
var binding_index = @as(u8, 0);
|
||||
|
||||
while (binding_index < environment.capture_count) : (binding_index += 1) {
|
||||
|
@ -467,7 +466,7 @@ const Compiler = struct {
|
|||
}
|
||||
};
|
||||
|
||||
const ConstList = coral.list.Stack(*kym.RuntimeObj);
|
||||
const ConstList = coral.list.Stack(*script.RuntimeObj);
|
||||
|
||||
const LineList = coral.list.Stack(tokens.Line);
|
||||
|
||||
|
@ -516,7 +515,7 @@ const OpcodeList = coral.list.Stack(Opcode);
|
|||
|
||||
const Self = @This();
|
||||
|
||||
pub fn deinit(self: *Self, env: *kym.RuntimeEnv) void {
|
||||
pub fn deinit(self: *Self, env: *script.RuntimeEnv) void {
|
||||
while (self.constants.pop()) |constant| {
|
||||
env.release(constant);
|
||||
}
|
||||
|
@ -538,7 +537,7 @@ pub fn deinit(self: *Self, env: *kym.RuntimeEnv) void {
|
|||
self.bindings = &.{};
|
||||
}
|
||||
|
||||
pub fn dump(chunk: Self, env: *kym.RuntimeEnv) kym.RuntimeError!*kym.RuntimeObj {
|
||||
pub fn dump(chunk: Self, env: *script.RuntimeEnv) script.RuntimeError!*script.RuntimeObj {
|
||||
var opcode_cursor = @as(u32, 0);
|
||||
var buffer = coral.list.ByteStack.init(env.allocator);
|
||||
|
||||
|
@ -629,7 +628,7 @@ pub fn dump(chunk: Self, env: *kym.RuntimeEnv) kym.RuntimeError!*kym.RuntimeObj
|
|||
return (try env.new_string(buffer.values)).pop().?;
|
||||
}
|
||||
|
||||
pub fn execute(self: *Self, env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeObj {
|
||||
pub fn execute(self: *Self, env: *script.RuntimeEnv) script.RuntimeError!?*script.RuntimeObj {
|
||||
self.cursor = 0;
|
||||
|
||||
while (self.cursor < self.opcodes.values.len) : (self.cursor += 1) {
|
||||
|
@ -693,7 +692,7 @@ pub fn execute(self: *Self, env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.Runtime
|
|||
return env.raise(error.IllegalState, "cannot bind values to an already-bound chunk", .{});
|
||||
}
|
||||
|
||||
chunk.bindings = try coral.io.allocate_many(env.allocator, bind, @as(?*kym.RuntimeObj, null));
|
||||
chunk.bindings = try coral.io.allocate_many(env.allocator, bind, @as(?*script.RuntimeObj, null));
|
||||
|
||||
for (0 .. bind) |index| {
|
||||
const value = env.pop();
|
||||
|
@ -897,7 +896,7 @@ pub fn execute(self: *Self, env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.Runtime
|
|||
return env.pop();
|
||||
}
|
||||
|
||||
fn get_binding(self: *Self, env: *kym.RuntimeEnv, index: usize) kym.RuntimeError!?*kym.RuntimeObj {
|
||||
fn get_binding(self: *Self, env: *script.RuntimeEnv, index: usize) script.RuntimeError!?*script.RuntimeObj {
|
||||
if (index >= self.bindings.len) {
|
||||
return env.raise(error.IllegalState, "invalid binding", .{});
|
||||
}
|
||||
|
@ -905,7 +904,7 @@ fn get_binding(self: *Self, env: *kym.RuntimeEnv, index: usize) kym.RuntimeError
|
|||
return self.bindings[index];
|
||||
}
|
||||
|
||||
fn get_constant(self: *const Self, env: *kym.RuntimeEnv, index: usize) kym.RuntimeError!*kym.RuntimeObj {
|
||||
fn get_constant(self: *const Self, env: *script.RuntimeEnv, index: usize) script.RuntimeError!*script.RuntimeObj {
|
||||
if (index >= self.constants.values.len) {
|
||||
return env.raise(error.IllegalState, "invalid constant", .{});
|
||||
}
|
||||
|
@ -913,7 +912,7 @@ fn get_constant(self: *const Self, env: *kym.RuntimeEnv, index: usize) kym.Runti
|
|||
return self.constants.values[index];
|
||||
}
|
||||
|
||||
pub fn init(env: *kym.RuntimeEnv, name: []const coral.io.Byte, environment: *const tree.Environment) kym.RuntimeError!Self {
|
||||
pub fn init(env: *script.RuntimeEnv, name: []const coral.io.Byte, environment: *const tree.Environment) script.RuntimeError!Self {
|
||||
var chunk = Self{
|
||||
.name = (try env.new_symbol(name)).pop().?,
|
||||
.opcodes = OpcodeList.init(env.allocator),
|
||||
|
@ -934,17 +933,19 @@ pub fn init(env: *kym.RuntimeEnv, name: []const coral.io.Byte, environment: *con
|
|||
return chunk;
|
||||
}
|
||||
|
||||
fn syscall_import(env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeObj {
|
||||
const arg = (try env.arg(0)).pop() orelse {
|
||||
return env.raise(error.BadOperation, "`@import` requires one argument to be a valid import path", .{});
|
||||
};
|
||||
fn syscall_import(env: *script.RuntimeEnv) script.RuntimeError!?*script.RuntimeObj {
|
||||
const arg = try env.expect_object((try env.arg(0)).pop());
|
||||
|
||||
defer env.release(arg);
|
||||
|
||||
return (try env.import(file.Path.of(&.{try env.expect_string(arg)}))).pop();
|
||||
const file_path = file.Path.from_bytes(try env.expect_string(arg)) catch {
|
||||
return env.raise(error.TypeMismatch, "`@import` requires argument `0` to be a valid import path", .{});
|
||||
};
|
||||
|
||||
return (try env.import(file_path)).pop();
|
||||
}
|
||||
|
||||
fn syscall_print(env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeObj {
|
||||
fn syscall_print(env: *script.RuntimeEnv) script.RuntimeError!?*script.RuntimeObj {
|
||||
const string = (try (try env.arg(0)).to_string()).pop().?;
|
||||
|
||||
defer env.release(string);
|
||||
|
@ -954,7 +955,7 @@ fn syscall_print(env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeObj {
|
|||
return null;
|
||||
}
|
||||
|
||||
fn syscall_vec2(env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeObj {
|
||||
fn syscall_vec2(env: *script.RuntimeEnv) script.RuntimeError!?*script.RuntimeObj {
|
||||
const x = @as(f32, get_x: {
|
||||
const x = (try env.arg(0)).pop() orelse {
|
||||
return env.raise(error.BadOperation, "a first argument is required to create a vector", .{});
|
||||
|
@ -968,16 +969,13 @@ fn syscall_vec2(env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeObj {
|
|||
if ((try env.arg(1)).pop()) |y| {
|
||||
defer env.release(y);
|
||||
|
||||
return (try env.new_vector2(.{
|
||||
.y = @floatCast(try env.expect_float(y)),
|
||||
.x = x,
|
||||
})).pop();
|
||||
return (try env.new_vector2(.{x, @as(f32, @floatCast(try env.expect_float(y)))})).pop();
|
||||
}
|
||||
|
||||
return (try env.new_vector2(kym.Vector2.from_scalar(x))).pop();
|
||||
return (try env.new_vector2(@splat(x))).pop();
|
||||
}
|
||||
|
||||
fn syscall_vec3(env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeObj {
|
||||
fn syscall_vec3(env: *script.RuntimeEnv) script.RuntimeError!?*script.RuntimeObj {
|
||||
const x = @as(f32, get_x: {
|
||||
const x = (try env.arg(0)).pop() orelse {
|
||||
return env.raise(error.BadOperation, "a first argument is required to create a vector", .{});
|
||||
|
@ -991,8 +989,7 @@ fn syscall_vec3(env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeObj {
|
|||
if ((try env.arg(1)).pop()) |y| {
|
||||
defer env.release(y);
|
||||
|
||||
return (try env.new_vector3(.{
|
||||
.z = @as(f32, get_z: {
|
||||
return (try env.new_vector3(.{x, @as(f32, @floatCast(try env.expect_float(y))), @as(f32, get_z: {
|
||||
const z = (try env.arg(0)).pop() orelse {
|
||||
return env.raise(error.BadOperation,
|
||||
"a third argument is required to create a vector if a first and second exist", .{});
|
||||
|
@ -1001,17 +998,13 @@ fn syscall_vec3(env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeObj {
|
|||
defer env.release(z);
|
||||
|
||||
break: get_z @floatCast(try env.expect_float(z));
|
||||
}),
|
||||
|
||||
.y = @floatCast(try env.expect_float(y)),
|
||||
.x = x,
|
||||
})).pop();
|
||||
})})).pop();
|
||||
}
|
||||
|
||||
return (try env.new_vector3(kym.Vector3.from_scalar(x))).pop();
|
||||
return (try env.new_vector3(@splat(x))).pop();
|
||||
}
|
||||
|
||||
pub const typeinfo = &kym.Typeinfo{
|
||||
pub const typeinfo = &script.Typeinfo{
|
||||
.size = @sizeOf(Self),
|
||||
.name = "lambda",
|
||||
.destruct = typeinfo_destruct,
|
||||
|
@ -1019,15 +1012,15 @@ pub const typeinfo = &kym.Typeinfo{
|
|||
.to_string = typeinfo_to_string,
|
||||
};
|
||||
|
||||
fn typeinfo_call(context: kym.Typeinfo.CallContext) kym.RuntimeError!?*kym.RuntimeObj {
|
||||
fn typeinfo_call(context: script.Typeinfo.CallContext) script.RuntimeError!?*script.RuntimeObj {
|
||||
return @as(*Self, @ptrCast(@alignCast(context.userdata))).execute(context.env);
|
||||
}
|
||||
|
||||
fn typeinfo_destruct(context: kym.Typeinfo.DestructContext) void {
|
||||
fn typeinfo_destruct(context: script.Typeinfo.DestructContext) void {
|
||||
@as(*Self, @ptrCast(@alignCast(context.userdata))).deinit(context.env);
|
||||
}
|
||||
|
||||
fn typeinfo_to_string(context: kym.Typeinfo.ToStringContext) kym.RuntimeError!*kym.RuntimeObj {
|
||||
fn typeinfo_to_string(context: script.Typeinfo.ToStringContext) script.RuntimeError!*script.RuntimeObj {
|
||||
return (try (try context.env.push(@as(*Self, @ptrCast(@alignCast(context.userdata))).name)).to_string()).pop().?;
|
||||
}
|
||||
|
|
@ -1,21 +1,21 @@
|
|||
const coral = @import("coral");
|
||||
|
||||
const kym = @import("../kym.zig");
|
||||
const script = @import("../script.zig");
|
||||
|
||||
associative: RefTable,
|
||||
contiguous: RefList,
|
||||
|
||||
const RefList = coral.list.Stack(?*kym.RuntimeObj);
|
||||
const RefList = coral.list.Stack(?*script.RuntimeObj);
|
||||
|
||||
const RefTable = coral.map.Table(*kym.RuntimeObj, *kym.RuntimeObj, struct {
|
||||
pub const hash = kym.RuntimeObj.hash;
|
||||
const RefTable = coral.map.Table(*script.RuntimeObj, *script.RuntimeObj, struct {
|
||||
pub const hash = script.RuntimeObj.hash;
|
||||
|
||||
pub const equals = kym.RuntimeObj.equals;
|
||||
pub const equals = script.RuntimeObj.equals;
|
||||
});
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn deinit(self: *Self, env: *kym.RuntimeEnv) void {
|
||||
pub fn deinit(self: *Self, env: *script.RuntimeEnv) void {
|
||||
{
|
||||
var field_iterable = self.associative.as_iterable();
|
||||
|
||||
|
@ -36,14 +36,14 @@ pub fn deinit(self: *Self, env: *kym.RuntimeEnv) void {
|
|||
self.contiguous.deinit();
|
||||
}
|
||||
|
||||
pub fn init(env: *kym.RuntimeEnv) Self {
|
||||
pub fn init(env: *script.RuntimeEnv) Self {
|
||||
return .{
|
||||
.associative = RefTable.init(env.allocator, .{}),
|
||||
.contiguous = RefList.init(env.allocator),
|
||||
};
|
||||
}
|
||||
|
||||
pub const typeinfo = &kym.Typeinfo{
|
||||
pub const typeinfo = &script.Typeinfo{
|
||||
.size = @sizeOf(Self),
|
||||
.name = "table",
|
||||
.destruct = typeinfo_destruct,
|
||||
|
@ -51,11 +51,11 @@ pub const typeinfo = &kym.Typeinfo{
|
|||
.set = typeinfo_set,
|
||||
};
|
||||
|
||||
fn typeinfo_destruct(context: kym.Typeinfo.DestructContext) void {
|
||||
fn typeinfo_destruct(context: script.Typeinfo.DestructContext) void {
|
||||
@as(*Self, @ptrCast(@alignCast(context.userdata))).deinit(context.env);
|
||||
}
|
||||
|
||||
fn typeinfo_get(context: kym.Typeinfo.GetContext) kym.RuntimeError!?*kym.RuntimeObj {
|
||||
fn typeinfo_get(context: script.Typeinfo.GetContext) script.RuntimeError!?*script.RuntimeObj {
|
||||
const table = @as(*Self, @ptrCast(@alignCast(context.userdata)));
|
||||
const index = context.get_index();
|
||||
|
||||
|
@ -79,7 +79,7 @@ fn typeinfo_get(context: kym.Typeinfo.GetContext) kym.RuntimeError!?*kym.Runtime
|
|||
return null;
|
||||
}
|
||||
|
||||
fn typeinfo_set(context: kym.Typeinfo.SetContext) kym.RuntimeError!void {
|
||||
fn typeinfo_set(context: script.Typeinfo.SetContext) script.RuntimeError!void {
|
||||
const table = @as(*Self, @ptrCast(@alignCast(context.userdata)));
|
||||
const index = context.get_index();
|
||||
|
|
@ -1,5 +1,185 @@
|
|||
const coral = @import("coral");
|
||||
|
||||
const ona = @import("ona");
|
||||
|
||||
pub const ScriptPlugin = struct {
|
||||
env: ona.script.RuntimeEnv,
|
||||
events: ?*ona.script.RuntimeObj,
|
||||
|
||||
const EventsObject = struct {
|
||||
updaters: RuntimeObjList,
|
||||
loop: *ona.GraphicsLoop,
|
||||
|
||||
fn get(context: ona.script.Typeinfo.GetContext) ona.script.RuntimeError!?*ona.script.RuntimeObj {
|
||||
const index = context.get_index();
|
||||
|
||||
defer context.env.release(index);
|
||||
|
||||
inline for ([_][]const u8{"on_update"}) |name| {
|
||||
const symbol = (try context.env.new_symbol(name)).pop().?;
|
||||
|
||||
defer context.env.release(symbol);
|
||||
|
||||
if (index.equals(symbol)) {
|
||||
return (try context.env.new_syscall(@field(EventsObject, name))).pop();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
fn on_update(env: *ona.script.RuntimeEnv) ona.script.RuntimeError!?*ona.script.RuntimeObj {
|
||||
const caller = try env.expect_object(try env.get_caller());
|
||||
|
||||
defer env.release(caller);
|
||||
|
||||
const updater = try env.expect_object((try env.arg(0)).pop());
|
||||
|
||||
errdefer env.release(updater);
|
||||
|
||||
try @as(*EventsObject, @ptrCast(@alignCast(try env.expect_dynamic(caller, typeinfo)))).updaters.push_one(updater);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const typeinfo = &ona.script.Typeinfo{
|
||||
.name = "events",
|
||||
.size = @sizeOf(EventsObject),
|
||||
.get = get,
|
||||
};
|
||||
|
||||
fn update(env: *ona.script.RuntimeEnv, _: *ona.GraphicsLoop) void {
|
||||
return ((env.arg(0) catch return).call(0, null) catch return).discard();
|
||||
}
|
||||
};
|
||||
|
||||
const RuntimeObjList = coral.list.Stack(*ona.script.RuntimeObj);
|
||||
|
||||
pub fn deinit(self: *ScriptPlugin) void {
|
||||
if (self.events) |object| {
|
||||
self.env.release(object);
|
||||
}
|
||||
|
||||
self.env.deinit();
|
||||
}
|
||||
|
||||
fn import_events(self: *ScriptPlugin, env: *ona.script.RuntimeEnv) ona.script.RuntimeError!?*ona.script.RuntimeObj {
|
||||
return env.acquire(self.events.?);
|
||||
}
|
||||
|
||||
pub fn init() coral.io.AllocationError!ScriptPlugin {
|
||||
var env = try ona.script.RuntimeEnv.init(ona.heap.allocator, 255, .{
|
||||
.print_out = ona.log_info,
|
||||
.print_err = ona.log_fail,
|
||||
});
|
||||
|
||||
errdefer env.deinit();
|
||||
|
||||
return .{
|
||||
.env = env,
|
||||
.events = null,
|
||||
};
|
||||
}
|
||||
|
||||
fn load(self: *ScriptPlugin, loop: *ona.GraphicsLoop) void {
|
||||
self.events = (self.env.new_dynamic(EventsObject.typeinfo, EventsObject{
|
||||
.updaters = RuntimeObjList.init(ona.heap.allocator),
|
||||
.loop = loop,
|
||||
}) catch {
|
||||
return ona.log_fail("failed to instantiate events object");
|
||||
}).pop();
|
||||
|
||||
self.env.override_import("events", ona.script.Importer.bind(ScriptPlugin, self, import_events)) catch {
|
||||
return ona.log_fail("failed to overide native import paths");
|
||||
};
|
||||
|
||||
self.reload(loop) catch {};
|
||||
}
|
||||
|
||||
fn reload(self: *ScriptPlugin, loop: *ona.GraphicsLoop) ona.script.RuntimeError!void {
|
||||
var title = comptime try coral.utf8.SmallString.from_units("Ona");
|
||||
var res_width = @as(u16, 640);
|
||||
var res_height = @as(u16, 480);
|
||||
var tick_rate = @as(f64, 60);
|
||||
|
||||
if ((try self.env.import(comptime try ona.file.Path.from_bytes("app.ona"))).pop()) |manifest| {
|
||||
defer self.env.release(manifest);
|
||||
|
||||
if (try ona.script.get_field(&self.env, manifest, "res_width")) |object| {
|
||||
defer self.env.release(object);
|
||||
|
||||
const int = @typeInfo(@TypeOf(res_width)).Int;
|
||||
|
||||
res_width = coral.math.checked_cast(int, try self.env.expect_fixed(object)) orelse {
|
||||
return self.env.raise(error.TypeMismatch, "`res_width` property cannot be greater than `{max}`", .{
|
||||
.max = coral.math.max_int(int),
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
if (try ona.script.get_field(&self.env, manifest, "res_height")) |object| {
|
||||
defer self.env.release(object);
|
||||
|
||||
const int = @typeInfo(@TypeOf(res_height)).Int;
|
||||
|
||||
res_height = coral.math.checked_cast(int, try self.env.expect_fixed(object)) orelse {
|
||||
return self.env.raise(error.TypeMismatch, "`res_height` property cannot be greater than `{max}`", .{
|
||||
.max = coral.math.max_int(int),
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
if (try ona.script.get_field(&self.env, manifest, "tick_rate")) |object| {
|
||||
defer self.env.release(object);
|
||||
|
||||
tick_rate = try self.env.expect_float(object);
|
||||
}
|
||||
|
||||
if (try ona.script.get_field(&self.env, manifest, "title")) |object| {
|
||||
defer self.env.release(object);
|
||||
|
||||
title = coral.utf8.SmallString.from_units(try self.env.expect_string(object)) catch |from_error| {
|
||||
return switch (from_error) {
|
||||
error.InvalidUtf8 => self.env.raise(error.TypeMismatch, "`title` cannot contain invalid utf8", .{}),
|
||||
error.TooBig => self.env.raise(error.TypeMismatch, "`title` is too long", .{}),
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
loop.set_resolution(res_width, res_height);
|
||||
loop.set_title(title);
|
||||
loop.set_tick_rate(tick_rate);
|
||||
}
|
||||
}
|
||||
|
||||
fn update(self: *ScriptPlugin, graphics_update: ona.GraphicsUpdate) void {
|
||||
if (graphics_update.loop.is_key_pressed(ona.Key.f5)) {
|
||||
self.reload(graphics_update.loop) catch {};
|
||||
}
|
||||
|
||||
const eventsect = @as(*EventsObject, @ptrCast(@alignCast(self.env.expect_dynamic(self.events.?, EventsObject.typeinfo) catch {
|
||||
return;
|
||||
})));
|
||||
|
||||
const dt = (self.env.new_float(graphics_update.delta_time) catch return).pop().?;
|
||||
|
||||
defer self.env.release(dt);
|
||||
|
||||
for (eventsect.updaters.values) |updater| {
|
||||
(((self.env.push(dt) catch return).push(updater) catch return).call(1, null) catch return).discard();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub fn main() void {
|
||||
ona.run_app(.{.sandboxed_path = &ona.file.Path.cwd});
|
||||
var script_plugin = ScriptPlugin.init() catch {
|
||||
return ona.log_fail("failed to initialize script plugin");
|
||||
};
|
||||
|
||||
defer script_plugin.deinit();
|
||||
|
||||
ona.start_graphics(&.{
|
||||
.{.loader = ona.GraphicsLoader.bind(ScriptPlugin, &script_plugin, ScriptPlugin.load)},
|
||||
.{.updater = ona.GraphicsUpdater.bind(ScriptPlugin, &script_plugin, ScriptPlugin.update)},
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue