From d5d5b69f5480f568d58d7cd5bb93092af9b15476 Mon Sep 17 00:00:00 2001 From: kayomn Date: Tue, 9 May 2023 22:57:19 +0000 Subject: [PATCH] Add partial work on new VM interface --- source/coral/format.zig | 1 + source/kym/vm.zig | 160 ++++++++++++++++++++++------------------ source/runner.zig | 112 +++++++++++++++------------- 3 files changed, 148 insertions(+), 125 deletions(-) diff --git a/source/coral/format.zig b/source/coral/format.zig index 4f072d3..153f9ad 100755 --- a/source/coral/format.zig +++ b/source/coral/format.zig @@ -4,6 +4,7 @@ pub const Value = union(enum) { newline: void, string: []const u8, unsigned: u128, + signed: i128, }; pub fn print(writer: io.Writer, values: []const Value) io.WriteError!usize { diff --git a/source/kym/vm.zig b/source/kym/vm.zig index 76fc09c..796d6b0 100755 --- a/source/kym/vm.zig +++ b/source/kym/vm.zig @@ -22,8 +22,8 @@ pub const Environment = struct { }; pub const NewOptions = struct { - userdata_size: usize = 0, - userinfo: usize = 0, + userdata: []const u8 = &.{}, + identity: ?*const anyopaque = null, behavior: *const Object.Behavior = &.{}, }; @@ -35,19 +35,30 @@ pub const Environment = struct { pub fn call(self: *Environment, object: *Object, arguments: []const Value) RuntimeError!Value { var global_object = Object{ .ref_count = 0, - .userinfo = 0, + .identity = &self.globals, .userdata = &.{}, .fields = self.globals, .behavior = &.{}, }; return object.behavior.caller(.{ - .environment = self, - .arguments = arguments, - .object = &global_object, + .env = self, + .args = arguments, + .caller = &global_object, + .callee = object, }); } + pub fn check(self: Environment, condition: bool, failure_message: []const u8) RuntimeError!void { + if (condition) return; + + // TODO: Emit failure message. + _ = self; + _ = failure_message; + + return error.CheckFailure; + } + pub fn deinit(self: *Environment) void { self.stack.deinit(self.allocator); self.calls.deinit(self.allocator); @@ -57,7 +68,7 @@ pub const Environment = struct { try self.globals.assign(self.allocator, global_name, value); } - pub fn init(allocator: coral.io.Allocator, init_options: InitOptions) !Environment { + pub fn init(allocator: coral.io.Allocator, options: InitOptions) !Environment { var environment = Environment{ .allocator = allocator, .globals = .{}, @@ -70,8 +81,8 @@ pub const Environment = struct { environment.calls.deinit(allocator); } - try environment.stack.grow(allocator, init_options.stack_max); - try environment.calls.grow(allocator, init_options.calls_max); + try environment.stack.grow(allocator, options.stack_max); + try environment.calls.grow(allocator, options.calls_max); return environment; } @@ -81,14 +92,16 @@ pub const Environment = struct { errdefer coral.io.deallocate(self.allocator, object); - const userdata = try coral.io.allocate_many(u8, options.userdata_size, self.allocator); + const userdata = try coral.io.allocate_many(u8, options.userdata.len, self.allocator); errdefer coral.io.deallocate(self.allocator, userdata); + coral.io.copy(userdata, options.userdata); + object.* = .{ .userdata = userdata, .ref_count = 1, - .userinfo = options.userinfo, + .identity = options.identity orelse userdata.ptr, .behavior = options.behavior, .fields = .{}, }; @@ -101,7 +114,7 @@ pub const Environment = struct { return self.new(.none, .{}); } - pub fn new_script(self: *Environment, options: NewScriptOptions) RuntimeError!*Object { + pub fn new_script(self: *Environment, options: NewScriptOptions) coral.io.AllocationError!*Object { // TODO: Implement. _ = self; _ = options; @@ -110,30 +123,21 @@ pub const Environment = struct { } pub fn new_string(self: *Environment, string_data: []const u8) coral.io.AllocationError!*Object { - const string_behavior = &.{}; - - if (string_data.len == 0) { - // Empty string. - return self.new(.{.behavior = string_behavior}); - } - - const string_object = try self.new(.{ - .userdata_size = string_data.len, - .behavior = string_behavior + const object = try self.new(.{ + .userdata = string_data, + .behavior = &.{}, }); - errdefer self.release(string_object); + errdefer self.release(object); - coral.io.copy(string_object.userdata, string_data); - - return string_object; + return object; } - pub fn new_string_from_float(self: *Environment, float: Float) coral.io.AllocationError!*Object { + pub fn new_string_from(self: *Environment, formats: []const coral.format.Value) coral.io.AllocationError!*Object { // TODO: Implement. - _ = float; + coral.format.print(coral.io.null_writer, formats); - return self.new_string("0.0"); + return self.new_string(""); } pub fn new_string_from_integer(self: *Environment, integer: Integer) coral.io.AllocationError!*Object { @@ -143,13 +147,6 @@ pub const Environment = struct { return self.new_string("0"); } - pub fn new_string_from_object(self: *Environment, object: *Object) coral.io.AllocationError!*Object { - // TODO: Implement. - _ = object; - - return self.new_string(""); - } - pub fn new_table(self: *Environment) coral.io.AllocationError!*Object { // TODO: Implement. return self.new(.none, .{}); @@ -174,6 +171,23 @@ pub const Environment = struct { } } + pub fn user_cast(self: *Environment, object: *Object, expected_identity: *const anyopaque, comptime CastTarget: type) RuntimeError!*CastTarget { + // TODO: Emit failure message. + _ = self; + + if (object.identity != expected_identity) { + // Identity does not match what was expected. + return error.InvalidOperation; + } + + if (object.userdata.len != @sizeOf(CastTarget)) { + // Userdata size does not match target type. + return error.InvalidOperation; + } + + return @ptrCast(*CastTarget, @alignCast(@alignOf(CastTarget), object.userdata)); + } + pub fn virtual_call(self: *Environment, object: *Object, index: Value, arguments: []const Value) RuntimeError!Value { const value = try self.virtual_get(object, index); @@ -217,39 +231,43 @@ pub const Integer = i32; pub const Object = struct { ref_count: usize, - userinfo: usize, + identity: *const anyopaque, userdata: []u8, behavior: *const Behavior, fields: ValueTable, pub const Behavior = struct { caller: *const Caller = default_call, + deinitialize: *const Deinitializer = default_deinitialize, getter: *const Getter = default_get, setter: *const Setter = default_set, - destructor: *const Destructor = default_destruct, fn default_call(_: CallContext) RuntimeError!Value { return error.InvalidOperation; } - fn default_destruct(_: DestructContext) void { - + fn default_deinitialize(_: DeinitializeContext) void { + // Nothing to deinitialize by default. } fn default_get(context: GetContext) RuntimeError!Value { - const index = try switch (context.index) { - .object => |object| context.environment.new_string_from_object(object), - .integer => |integer| context.environment.new_string_from_integer(integer), - .float => |float| context.environment.new_string_from_float(float), + switch (context.index) { + .object => |index| { + if (!index.is_string()) return error.InvalidOperation; + + return context.obj.fields.lookup(index.userdata) orelse .nil; + }, + + .integer => |integer| { + const index = context.env.new_string_from(&.{.{.signed = integer}}); + + defer context.env.release(index); + + return context.obj.fields.lookup(index.userdata) orelse .nil; + }, + else => return error.InvalidOperation, - }; - - defer context.environment.release(index); - - // A string is just a serious of bytes (i.e. userdata with no userinfo). - coral.debug.assert(index.userinfo == 0); - - return context.object.fields.lookup(index.userdata) orelse .nil; + } } fn default_set(_: SetContext) RuntimeError!void { @@ -258,41 +276,32 @@ pub const Object = struct { }; pub const CallContext = struct { - environment: *Environment, - object: *Object, - arguments: []const Value, - - pub fn check(self: CallContext, condition: bool, failure_message: []const u8) RuntimeError!void { - if (condition) return; - - // TODO: Emit failure message. - _ = self; - _ = failure_message; - - return error.CheckFailure; - } + env: *Environment, + caller: *Object, + callee: *Object, + args: []const Value, }; pub const Caller = fn (context: CallContext) RuntimeError!Value; - pub const DestructContext = struct { - environment: *Environment, - object: *Object, + pub const DeinitializeContext = struct { + env: *Environment, + obj: *Object, }; - pub const Destructor = fn (context: DestructContext) void; + pub const Deinitializer = fn (context: DeinitializeContext) void; pub const GetContext = struct { - environment: *Environment, - object: *const Object, + env: *Environment, + obj: *const Object, index: Value, }; pub const Getter = fn (context: GetContext) RuntimeError!Value; pub const SetContext = struct { - environment: *Environment, - object: *Object, + env: *Environment, + obj: *Object, index: Value, value: Value, }; @@ -302,6 +311,11 @@ pub const Object = struct { pub fn as_value(self: *Object) Value { return .{.object = self}; } + + pub fn is_string(self: Object) bool { + // Userdata represents a string (of bytes) if it's identity is it's userdata. + return self.identity == @ptrCast(*const anyopaque, self.userdata.ptr); + } }; pub const RuntimeError = coral.io.AllocationError || error { diff --git a/source/runner.zig b/source/runner.zig index a40f9d2..7088c2b 100755 --- a/source/runner.zig +++ b/source/runner.zig @@ -4,72 +4,82 @@ const kym = @import("kym"); const ona = @import("ona"); -const transform2d_typename = @typeName(ona.gfx.Transform2D); +const transform_typeid = @typeName(ona.canvas.Transform); -fn bind_canvas(environment: *kym.vm.Environment, canvas: *ona.gfx.Canvas) !void { - const object = try environment.new(.{}); +fn new_canvas_item(environment: *kym.vm.Environment, canvas_item: ona.CanvasItem) !*kym.vm.Object { + const typeid = @typeName(ona.CanvasItem); - defer environment.release(object); - - const sprite_creator = try environment.new(.{ - .userinfo = @ptrToInt(canvas), - - .behavior = &.{ - .caller = struct { - fn call(context: kym.vm.Object.CallContext) kym.vm.RuntimeError!kym.vm.Value { - try context.check(context.arguments.len == 2, "2 arguments expected"); - - const transform = context.arguments[0].object; - - try context.check(transform.userinfo == @ptrToInt(transform2d_typename), "`transform2d` expected"); - - _ = try @intToPtr(*ona.gfx.Canvas, context.object.userinfo).create_sprite( - coral.io.bytes_to(ona.gfx.Transform2D, transform.userdata).?); - - return .nil; - } - }.call, - }, + var object = try environment.new(.{ + .userdata = coral.io.bytes_of(&canvas_item), + .identity = typeid, }); - defer environment.release(sprite_creator); + errdefer environment.release(object); - try environment.raw_set(object, "create_sprite", sprite_creator.as_value()); - try environment.global_set("canvas", object.as_value()); + return object; } -// fn bind_transform_2d(environment: *kym.Environment) !*kym.Object { -// const allocator = environment.allocator(); -// const transform_2d = try allocator.allocate_one(ona.gfx.Transform2D); +fn new_transform(environment: *kym.Environment, transform: ona.canvas.Transform) !*kym.vm.Object { + var object = try environment.new(.{ + .userdata = coral.io.bytes_of(&transform), + .identity = transform_typeid, + }); -// errdefer allocator.deallocate(transform_2d); + errdefer environment.release(object); -// const object = try environment.new(@ptrToInt(transform_2d), .{ -// .destructor = struct { -// fn destruct() void { -// allocator.deallocate(transform_2d); -// } -// }.destruct, -// }); - -// defer environment.release(object); - -// try environment.set_field(environment.globals(), object); -// } + return object; +} pub fn main() anyerror!void { return ona.App.run(anyerror, start); } fn start(app: *ona.App) anyerror!void { - var kym_env = try kym.vm.Environment.init(ona.allocator, .{ + var kym_environment = try kym.vm.Environment.init(ona.allocator, .{ .stack_max = 256, .calls_max = 256, }); - defer kym_env.deinit(); + defer kym_environment.deinit(); - try bind_canvas(&kym_env, app.canvas()); + const Ona = struct { + app: *ona.App, + + const typeid = @typeName(ona.App); + }; + + var ona_lib = try kym_environment.new(.{ + .userdata = coral.io.bytes_of(&Ona{ + .app = app, + }), + + .identity = Ona.typeid, + }); + + defer kym_environment.release(ona_lib); + + try kym_environment.global_set("ona", ona_lib.as_value()); + + { + var sprite_creator = try kym_environment.new(.{.behavior = &.{.caller = struct { + fn call(context: kym.vm.Object.CallContext) kym.vm.RuntimeError!kym.vm.Value { + try context.env.check(context.args.len == 2, "2 arguments expected"); + + var item = try ona.CanvasItem.init((try context.env.user_cast(context.caller, Ona.typeid, Ona)).app, .{ + .transform = (try context.env.user_cast(context.args[0].object, transform_typeid, ona.canvas.Transform)).*, + .options = .{.sprite = .{}}, + }); + + errdefer item.deinit(); + + return (try new_canvas_item(context.env, item)).as_value(); + } + }.call}}); + + defer kym_environment.release(sprite_creator); + + try kym_environment.raw_set(ona_lib, "create_sprite", sprite_creator.as_value()); + } { const index_path = "index.kym"; @@ -92,18 +102,16 @@ fn start(app: *ona.App) anyerror!void { } { - const index_script = try kym_env.new_script(.{ + const index_script = try kym_environment.new_script(.{ .name = index_path, .data = index_buffer.data }); - defer kym_env.release(index_script); + defer kym_environment.release(index_script); - _ = try kym_env.call(index_script, &.{}); + _ = try kym_environment.call(index_script, &.{}); } } - while (app.poll()) { - app.present(); - } + app.loop(); }