From 8b0182f03cc39a025849fb6a59895e83ec1c3342 Mon Sep 17 00:00:00 2001 From: kayomn Date: Sun, 5 Nov 2023 16:06:26 +0000 Subject: [PATCH 1/5] add dynamic to_string support to dynamic object typeinfo --- source/ona/kym.zig | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/source/ona/kym.zig b/source/ona/kym.zig index 5004f9c..880617d 100644 --- a/source/ona/kym.zig +++ b/source/ona/kym.zig @@ -683,8 +683,8 @@ pub const RuntimeEnv = struct { break: convert self.new_string(string[0 .. length.?]); }, - .syscall => self.new_string(""), - .dynamic => self.new_string(""), + .syscall => self.new_string("syscall"), + .dynamic => |dynamic| dynamic.typeinfo().to_string(self, dynamic.userdata()), }; } @@ -957,6 +957,7 @@ pub const Typeinfo = struct { name: []const coral.io.Byte, size: usize, destruct: ?*const fn (env: *RuntimeEnv, userdata: []coral.io.Byte) void = null, + to_string: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte) RuntimeError!*RuntimeRef = default_to_string, call: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, frame: Frame) RuntimeError!?*RuntimeRef = default_call, get: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, index: *const RuntimeRef) RuntimeError!?*RuntimeRef = default_get, set: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, value: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void = default_set, @@ -972,6 +973,10 @@ pub const Typeinfo = struct { fn default_set(env: *RuntimeEnv, _: []coral.io.Byte, _: *const RuntimeRef, _: ?*const RuntimeRef) RuntimeError!void { return env.raise(error.BadOperation, "this dynamic object is not set-indexable", .{}); } + + fn default_to_string(env: *RuntimeEnv, _: []coral.io.Byte) RuntimeError!*RuntimeRef { + return env.raise(error.BadOperation, "this dynamic object is not stringable", .{}); + } }; pub fn assert(env: *RuntimeEnv, condition: bool) RuntimeError!void { From fd159032605bd50db7b3e5818805d3243058fd5b Mon Sep 17 00:00:00 2001 From: kayomn Date: Sun, 5 Nov 2023 16:18:36 +0000 Subject: [PATCH 2/5] Fix recursive to_string calls --- debug/app.ona | 2 +- source/ona/kym.zig | 40 ++++++++++++++++++++-------------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/debug/app.ona b/debug/app.ona index bb14b64..30b28bf 100644 --- a/debug/app.ona +++ b/debug/app.ona @@ -1,7 +1,7 @@ let test_param = "monkey wrench" -let printer = lambda (pfx): +let printer = lambda (pfx, anotha): @print(test_param) return lambda (msg): diff --git a/source/ona/kym.zig b/source/ona/kym.zig index 880617d..4b411f7 100644 --- a/source/ona/kym.zig +++ b/source/ona/kym.zig @@ -11,7 +11,7 @@ const tokens = @import("./kym/tokens.zig"); const tree = @import("./kym/tree.zig"); pub const Frame = struct { - name_stringable: *RuntimeRef, + callable: *RuntimeRef, arg_count: u8, locals_top: usize, @@ -381,14 +381,14 @@ pub const RuntimeEnv = struct { }; } - pub fn new_boolean(self: *RuntimeEnv, value: bool) RuntimeError!*RuntimeRef { + pub fn new_boolean(self: *RuntimeEnv, value: bool) coral.io.AllocationError!*RuntimeRef { return RuntimeRef.allocate(self.allocator, .{ .ref_count = 1, .payload = if (value) .true else .false, }); } - pub fn new_boxed(self: *RuntimeEnv, value: ?*const RuntimeRef) RuntimeError!*RuntimeRef { + pub fn new_boxed(self: *RuntimeEnv, value: ?*const RuntimeRef) coral.io.AllocationError!*RuntimeRef { return RuntimeRef.allocate(self.allocator, .{ .ref_count = 1, .payload = .{.boxed = if (value) |ref| ref.acquire() else null}, @@ -399,7 +399,7 @@ pub const RuntimeEnv = struct { self: *RuntimeEnv, userdata: []const coral.io.Byte, typeinfo: *const Typeinfo, - ) RuntimeError!*RuntimeRef { + ) coral.io.AllocationError!*RuntimeRef { coral.debug.assert(userdata.len == typeinfo.size); const dynamic = try self.allocator.reallocate(null, @sizeOf(usize) + typeinfo.size); @@ -415,21 +415,21 @@ pub const RuntimeEnv = struct { }); } - pub fn new_fixed(self: *RuntimeEnv, value: Fixed) RuntimeError!*RuntimeRef { + pub fn new_fixed(self: *RuntimeEnv, value: Fixed) coral.io.AllocationError!*RuntimeRef { return RuntimeRef.allocate(self.allocator, .{ .ref_count = 1, .payload = .{.fixed = value}, }); } - pub fn new_float(self: *RuntimeEnv, value: Float) RuntimeError!*RuntimeRef { + pub fn new_float(self: *RuntimeEnv, value: Float) coral.io.AllocationError!*RuntimeRef { return RuntimeRef.allocate(self.allocator, .{ .ref_count = 1, .payload = .{.float = value}, }); } - pub fn new_string(self: *RuntimeEnv, value: []const coral.io.Byte) RuntimeError!*RuntimeRef { + pub fn new_string(self: *RuntimeEnv, value: []const coral.io.Byte) coral.io.AllocationError!*RuntimeRef { if (value.len > coral.math.max_int(@typeInfo(Fixed).Int)) { return error.OutOfMemory; } @@ -450,7 +450,7 @@ pub const RuntimeEnv = struct { }); } - pub fn new_symbol(self: *RuntimeEnv, value: []const coral.io.Byte) RuntimeError!*RuntimeRef { + pub fn new_symbol(self: *RuntimeEnv, value: []const coral.io.Byte) coral.io.AllocationError!*RuntimeRef { return RuntimeRef.allocate(self.allocator, .{ .ref_count = 1, @@ -468,14 +468,14 @@ pub const RuntimeEnv = struct { }); } - pub fn new_syscall(self: *RuntimeEnv, value: *const Syscall) RuntimeError!*RuntimeRef { + pub fn new_syscall(self: *RuntimeEnv, value: *const Syscall) coral.io.AllocationError!*RuntimeRef { return RuntimeRef.allocate(self.allocator, .{ .ref_count = 1, .payload = .{.syscall = value}, }); } - pub fn new_table(self: *RuntimeEnv) RuntimeError!*RuntimeRef { + pub fn new_table(self: *RuntimeEnv) coral.io.AllocationError!*RuntimeRef { var table = Table.make(self); errdefer table.free(self); @@ -483,14 +483,14 @@ pub const RuntimeEnv = struct { return try self.new_dynamic(coral.io.bytes_of(&table), Table.typeinfo); } - pub fn new_vector2(self: *RuntimeEnv, x: f32, y: f32) RuntimeError!*RuntimeRef { + pub fn new_vector2(self: *RuntimeEnv, x: f32, y: f32) coral.io.AllocationError!*RuntimeRef { return RuntimeRef.allocate(self.allocator, .{ .ref_count = 1, .payload = .{.vector2 = .{x, y}}, }); } - pub fn new_vector3(self: *RuntimeEnv, x: f32, y: f32, z: f32) RuntimeError!*RuntimeRef { + pub fn new_vector3(self: *RuntimeEnv, x: f32, y: f32, z: f32) coral.io.AllocationError!*RuntimeRef { return RuntimeRef.allocate(self.allocator, .{ .ref_count = 1, .payload = .{.vector3 = .{x, y, z}}, @@ -513,7 +513,7 @@ pub const RuntimeEnv = struct { const popped_frame = self.frames.pop(); coral.debug.assert(popped_frame != null); - self.discard(popped_frame.?.name_stringable); + self.discard(popped_frame.?.callable); var locals_to_pop = self.locals.values.len - popped_frame.?.locals_top; @@ -534,9 +534,9 @@ pub const RuntimeEnv = struct { return self.locals.pop() orelse self.raise(error.IllegalState, "stack underflow", .{}); } - pub fn push_frame(self: *RuntimeEnv, name_stringable: *RuntimeRef, arg_count: u8) RuntimeError!Frame { + pub fn push_frame(self: *RuntimeEnv, callable: *const RuntimeRef, arg_count: u8) RuntimeError!Frame { const frame = Frame{ - .name_stringable = name_stringable.acquire(), + .callable = callable.acquire(), .arg_count = arg_count, .locals_top = self.locals.values.len - arg_count, }; @@ -563,7 +563,7 @@ pub const RuntimeEnv = struct { while (remaining_frames != 0) { remaining_frames -= 1; - const callable_string = try self.to_string(self.frames.values[remaining_frames].name_stringable); + const callable_string = try self.to_string(self.frames.values[remaining_frames].callable); defer self.discard(callable_string); @@ -623,7 +623,7 @@ pub const RuntimeEnv = struct { }; } - pub fn to_string(self: *RuntimeEnv, value: *const RuntimeRef) RuntimeError!*RuntimeRef { + pub fn to_string(self: *RuntimeEnv, value: *const RuntimeRef) coral.io.AllocationError!*RuntimeRef { const decimal_format = coral.utf8.DecimalFormat.default; return switch (value.object().payload) { @@ -957,7 +957,7 @@ pub const Typeinfo = struct { name: []const coral.io.Byte, size: usize, destruct: ?*const fn (env: *RuntimeEnv, userdata: []coral.io.Byte) void = null, - to_string: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte) RuntimeError!*RuntimeRef = default_to_string, + to_string: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte) coral.io.AllocationError!*RuntimeRef = default_to_string, call: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, frame: Frame) RuntimeError!?*RuntimeRef = default_call, get: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, index: *const RuntimeRef) RuntimeError!?*RuntimeRef = default_get, set: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, value: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void = default_set, @@ -974,8 +974,8 @@ pub const Typeinfo = struct { return env.raise(error.BadOperation, "this dynamic object is not set-indexable", .{}); } - fn default_to_string(env: *RuntimeEnv, _: []coral.io.Byte) RuntimeError!*RuntimeRef { - return env.raise(error.BadOperation, "this dynamic object is not stringable", .{}); + fn default_to_string(env: *RuntimeEnv, _: []coral.io.Byte) coral.io.AllocationError!*RuntimeRef { + return env.new_string("{}"); } }; From 00631e66a5ad684db62526607ffa417d0df2d593 Mon Sep 17 00:00:00 2001 From: kayomn Date: Sun, 5 Nov 2023 16:24:06 +0000 Subject: [PATCH 3/5] Make named lambdas report their names in errors --- source/ona/kym.zig | 6 +----- source/ona/kym/Chunk.zig | 9 +++++++-- source/ona/kym/Compiler.zig | 6 +----- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/source/ona/kym.zig b/source/ona/kym.zig index 4b411f7..1da2ddd 100644 --- a/source/ona/kym.zig +++ b/source/ona/kym.zig @@ -308,11 +308,7 @@ pub const RuntimeEnv = struct { }; } - const chunk_name = try self.new_string(file_name); - - defer self.discard(chunk_name); - - break: make_chunk try Chunk.make(self, chunk_name, &root.environment); + break: make_chunk try Chunk.make(self, file_name, &root.environment); }; defer chunk.free(self); diff --git a/source/ona/kym/Chunk.zig b/source/ona/kym/Chunk.zig index f763819..e7a5f9b 100644 --- a/source/ona/kym/Chunk.zig +++ b/source/ona/kym/Chunk.zig @@ -576,9 +576,9 @@ pub fn free(self: *Self, env: *kym.RuntimeEnv) void { self.bindings = &.{}; } -pub fn make(env: *kym.RuntimeEnv, name: *const kym.RuntimeRef, environment: *const tree.Environment) kym.RuntimeError!Self { +pub fn make(env: *kym.RuntimeEnv, name: []const coral.io.Byte, environment: *const tree.Environment) kym.RuntimeError!Self { var chunk = Self{ - .name = name.acquire(), + .name = try env.new_symbol(name), .opcodes = OpcodeList.make(env.allocator), .constants = ConstList.make(env.allocator), .bindings = &.{}, @@ -634,6 +634,7 @@ pub const typeinfo = &kym.Typeinfo{ .name = "lambda", .destruct = typeinfo_destruct, .call = typeinfo_call, + .to_string = typeinfo_to_string, }; fn typeinfo_call(env: *kym.RuntimeEnv, userdata: []coral.io.Byte, frame: kym.Frame) kym.RuntimeError!?*kym.RuntimeRef { @@ -653,3 +654,7 @@ fn typeinfo_call(env: *kym.RuntimeEnv, userdata: []coral.io.Byte, frame: kym.Fra fn typeinfo_destruct(env: *kym.RuntimeEnv, userdata: []coral.io.Byte) void { @as(*Self, @ptrCast(@alignCast(userdata))).free(env); } + +fn typeinfo_to_string(env: *kym.RuntimeEnv, userdata: []coral.io.Byte) coral.io.AllocationError!*kym.RuntimeRef { + return env.to_string(@as(*Self, @ptrCast(@alignCast(userdata))).name); +} diff --git a/source/ona/kym/Compiler.zig b/source/ona/kym/Compiler.zig index c34ea56..c963884 100644 --- a/source/ona/kym/Compiler.zig +++ b/source/ona/kym/Compiler.zig @@ -86,11 +86,7 @@ fn compile_expression(self: Self, environment: *const tree.Environment, expressi }, .lambda_construct => |lambda_construct| { - const anonymous_chunk_name = try self.env.new_symbol(name orelse ""); - - defer self.env.discard(anonymous_chunk_name); - - var chunk = try Chunk.make(self.env, anonymous_chunk_name, lambda_construct.environment); + var chunk = try Chunk.make(self.env, name orelse "", lambda_construct.environment); errdefer chunk.free(self.env); From 75ddf3f2bb6cf0bed6383bb75c77f0ae4db3183e Mon Sep 17 00:00:00 2001 From: kayomn Date: Sun, 5 Nov 2023 22:46:24 +0000 Subject: [PATCH 4/5] Add line numbers to stack traces --- debug/app.ona | 2 +- source/coral/io.zig | 2 +- source/ona/kym.zig | 147 +++++++++++++++++++++++------------- source/ona/kym/Chunk.zig | 105 +++++++++++++------------- source/ona/kym/Compiler.zig | 80 ++++++++++---------- source/ona/kym/Expr.zig | 99 +++++++++++++++++++----- source/ona/kym/Stmt.zig | 22 ++++-- source/ona/kym/Table.zig | 8 +- source/ona/kym/tokens.zig | 2 +- 9 files changed, 292 insertions(+), 175 deletions(-) diff --git a/debug/app.ona b/debug/app.ona index 30b28bf..bb14b64 100644 --- a/debug/app.ona +++ b/debug/app.ona @@ -1,7 +1,7 @@ let test_param = "monkey wrench" -let printer = lambda (pfx, anotha): +let printer = lambda (pfx): @print(test_param) return lambda (msg): diff --git a/source/coral/io.zig b/source/coral/io.zig index 6e21d88..2f7de22 100644 --- a/source/coral/io.zig +++ b/source/coral/io.zig @@ -374,7 +374,7 @@ pub fn tag_of(comptime value: anytype) Tag(@TypeOf(value)) { return @as(Tag(@TypeOf(value)), value); } -pub fn write_null(buffer: []const u8) ?usize { +fn write_null(buffer: []const u8) ?usize { return buffer.len; } diff --git a/source/ona/kym.zig b/source/ona/kym.zig index 1da2ddd..a3e463e 100644 --- a/source/ona/kym.zig +++ b/source/ona/kym.zig @@ -89,21 +89,21 @@ pub const RuntimeEnv = struct { pub fn call(self: *RuntimeEnv, callable: *const RuntimeRef, args: []const *RuntimeRef) RuntimeError!?*RuntimeRef { // TODO: Handle errors. for (args) |arg| { - try self.locals.push_one(try self.acquire(arg)); + try self.locals.push_one(arg.acquire()); } - const frame = try self.push_frame(callable, args.len); + const frame = try self.push_frame(callable, @intCast(args.len)); defer self.pop_frame(); - return self.call_frame(frame); + return self.call_frame(&frame); } - pub fn call_frame(self: *RuntimeEnv, callable: *const RuntimeRef, frame: Frame) RuntimeError!?*RuntimeRef { - return switch (callable.object().payload) { + pub fn call_frame(self: *RuntimeEnv, frame: *const Frame) RuntimeError!?*RuntimeRef { + return switch (frame.callable.object().payload) { .syscall => |syscall| syscall(self, frame), .dynamic => |dynamic| dynamic.typeinfo().call(self, dynamic.userdata(), frame), - else => self.raise(error.TypeMismatch, "{typename} is not callable", .{.typename = callable.typename()}), + else => self.raise(error.TypeMismatch, "{typename} is not callable", .{.typename = frame.callable.typename()}), }; } @@ -271,7 +271,7 @@ pub const RuntimeEnv = struct { } }, - .dynamic => |dynamic| dynamic.typeinfo().get(self, dynamic.userdata(), index), + .dynamic => |dynamic| dynamic.typeinfo().get_index(self, dynamic.userdata(), index), else => self.raise(error.TypeMismatch, "{typename} is not get-indexable", .{ .typename = indexable.typename(), @@ -279,10 +279,17 @@ pub const RuntimeEnv = struct { }; } - pub fn import(self: *RuntimeEnv, file_path: file.Path) RuntimeError!?*RuntimeRef { - const file_name = file_path.get_string(); + pub fn get_boxed(self: *RuntimeEnv, boxable: *RuntimeRef) RuntimeError!?*RuntimeRef { + return switch (boxable.object().payload) { + .boxed => |boxed| if (boxed) |boxed_value| boxed_value.acquire() else null, + else => self.raise(error.TypeMismatch, "{typename} is not boxable", .{.typename = boxable.typename()}), + }; + } + + pub fn import(self: *RuntimeEnv, file_path: file.Path) RuntimeError!?*RuntimeRef { + var callable = try self.new_dynamic(coral.io.bytes_of(&make_chunk: { + const file_name = file_path.get_string(); - var chunk = make_chunk: { 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}`", .{ @@ -309,21 +316,11 @@ pub const RuntimeEnv = struct { } break: make_chunk try Chunk.make(self, file_name, &root.environment); - }; + }), Chunk.typeinfo); - defer chunk.free(self); + defer self.discard(callable); - return execute_chunk: { - const name = try self.new_string(file_name); - - defer self.discard(name); - - const frame = try self.push_frame(name, 0); - - defer self.pop_frame(); - - break: execute_chunk chunk.execute(self, frame); - }; + return self.call(callable, &.{}); } pub fn make(allocator: coral.io.Allocator, options: Options) coral.io.AllocationError!RuntimeEnv { @@ -531,6 +528,19 @@ pub const RuntimeEnv = struct { } pub fn push_frame(self: *RuntimeEnv, callable: *const RuntimeRef, arg_count: u8) RuntimeError!Frame { + const arity = switch (callable.object().payload) { + .dynamic => |dynamic| dynamic.typeinfo().get_arity(dynamic.userdata()), + else => 0, + }; + + if (arg_count < arity) { + return self.raise(error.BadOperation, "expected `{expected}` {noun}, {provided} provided", .{ + .expected = arity, + .provided = arg_count, + .noun = if (arity == 1) "argument" else "arguments", + }); + } + const frame = Frame{ .callable = callable.acquire(), .arg_count = arg_count, @@ -543,13 +553,11 @@ pub const RuntimeEnv = struct { } pub fn raise(self: *RuntimeEnv, error_value: RuntimeError, comptime format: []const coral.io.Byte, args: anytype) RuntimeError { - { - const formatted_message = try coral.utf8.alloc_formatted(self.allocator, format, args); + const formatted_message = try coral.utf8.alloc_formatted(self.allocator, format, args); - defer self.allocator.deallocate(formatted_message); + defer self.allocator.deallocate(formatted_message); - self.print_error(formatted_message); - } + self.print_error(formatted_message); if (!self.frames.is_empty()) { self.print_error("stack trace:"); @@ -559,17 +567,38 @@ pub const RuntimeEnv = struct { while (remaining_frames != 0) { remaining_frames -= 1; - const callable_string = try self.to_string(self.frames.values[remaining_frames].callable); + const callable = self.frames.values[remaining_frames].callable; + const name = try self.to_string(callable); - defer self.discard(callable_string); + defer self.discard(name); - self.print_error(get_name: { - const string = callable_string.as_string(); + if (callable.as_dynamic(Chunk.typeinfo)) |chunk_userdata| { + const chunk = @as(*Chunk, @ptrCast(@alignCast(chunk_userdata))); - coral.debug.assert(string != null); + const chunk_name = try coral.utf8.alloc_formatted(self.allocator, "{name}@{line}", .{ + .name = get_name: { + const string = name.as_string(); - break: get_name string.?; - }); + coral.debug.assert(string != null); + + break: get_name string.?; + }, + + .line = chunk.lines.values[chunk.cursor], + }); + + defer self.allocator.deallocate(chunk_name); + + self.print_error(chunk_name); + } else { + self.print_error(get_name: { + const string = name.as_string(); + + coral.debug.assert(string != null); + + break: get_name string.?; + }); + } } } @@ -578,7 +607,7 @@ pub const RuntimeEnv = struct { pub fn set(self: *RuntimeEnv, indexable: *RuntimeRef, index: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void { return switch (indexable.object().payload) { - .dynamic => |dynamic| dynamic.typeinfo().set(self, dynamic.userdata(), index, value), + .dynamic => |dynamic| dynamic.typeinfo().set_index(self, dynamic.userdata(), index, value), else => self.raise(error.TypeMismatch, "{typename} is not set-indexable", .{ .typename = indexable.typename(), @@ -586,6 +615,22 @@ pub const RuntimeEnv = struct { }; } + pub fn set_boxed(self: *RuntimeEnv, boxable: *RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void { + switch (boxable.object().payload) { + .boxed => |*boxed| { + if (boxed.*) |unboxed_value| { + self.discard(unboxed_value); + } + + boxed.* = if (value) |ref| ref.acquire() else null; + }, + + else => return self.raise(error.TypeMismatch, "{typename} is not boxable", .{ + .typename = boxable.typename(), + }), + } + } + pub fn sub(self: *RuntimeEnv, lhs: *const RuntimeRef, rhs: *const RuntimeRef) RuntimeError!*RuntimeRef { return switch (lhs.object().payload) { .fixed => |lhs_fixed| switch (rhs.object().payload) { @@ -789,13 +834,6 @@ pub const RuntimeRef = opaque { return @ptrCast(try coral.io.allocate_one(allocator, data)); } - pub fn as_boxed(self: *const RuntimeRef) ?*?*RuntimeRef { - return switch (self.object().payload) { - .boxed => |*boxed| boxed, - else => null, - }; - } - pub fn as_dynamic(self: *const RuntimeRef, typeinfo: *const Typeinfo) ?[]u8 { return switch (self.object().payload) { .dynamic => |dynamic| if (dynamic.typeinfo() == typeinfo) dynamic.userdata() else null, @@ -861,8 +899,8 @@ pub const RuntimeRef = opaque { break: unbox boxed_value.equals(other); } - if (other.as_boxed()) |boxed_value| { - break: unbox boxed_value.* == null; + if (other.object().payload == .boxed) { + break: unbox other.object().payload.boxed == null; } break: unbox false; @@ -947,26 +985,31 @@ pub const RuntimeRef = opaque { } }; -pub const Syscall = fn (env: *RuntimeEnv, frame: Frame) RuntimeError!?*RuntimeRef; +pub const Syscall = fn (env: *RuntimeEnv, frame: *const Frame) RuntimeError!?*RuntimeRef; pub const Typeinfo = struct { name: []const coral.io.Byte, size: usize, destruct: ?*const fn (env: *RuntimeEnv, userdata: []coral.io.Byte) void = null, to_string: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte) coral.io.AllocationError!*RuntimeRef = default_to_string, - call: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, frame: Frame) RuntimeError!?*RuntimeRef = default_call, - get: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, index: *const RuntimeRef) RuntimeError!?*RuntimeRef = default_get, - set: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, value: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void = default_set, + get_arity: *const fn (userdata: []coral.io.Byte) u8 = default_get_arity, + call: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, frame: *const Frame) RuntimeError!?*RuntimeRef = default_call, + get_index: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, index: *const RuntimeRef) RuntimeError!?*RuntimeRef = default_get_index, + set_index: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, value: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void = default_set_index, - fn default_call(env: *RuntimeEnv, _: []coral.io.Byte, _: Frame) RuntimeError!?*RuntimeRef { + fn default_call(env: *RuntimeEnv, _: []coral.io.Byte, _: *const Frame) RuntimeError!?*RuntimeRef { return env.raise(error.BadOperation, "this dynamic object is not callable", .{}); } - fn default_get(env: *RuntimeEnv, _: []coral.io.Byte, _: *const RuntimeRef) RuntimeError!?*RuntimeRef { + fn default_get_arity(_: []coral.io.Byte) u8 { + return 0; + } + + fn default_get_index(env: *RuntimeEnv, _: []coral.io.Byte, _: *const RuntimeRef) RuntimeError!?*RuntimeRef { return env.raise(error.BadOperation, "this dynamic object is not get-indexable", .{}); } - fn default_set(env: *RuntimeEnv, _: []coral.io.Byte, _: *const RuntimeRef, _: ?*const RuntimeRef) RuntimeError!void { + fn default_set_index(env: *RuntimeEnv, _: []coral.io.Byte, _: *const RuntimeRef, _: ?*const RuntimeRef) RuntimeError!void { return env.raise(error.BadOperation, "this dynamic object is not set-indexable", .{}); } diff --git a/source/ona/kym/Chunk.zig b/source/ona/kym/Chunk.zig index e7a5f9b..499d8dc 100644 --- a/source/ona/kym/Chunk.zig +++ b/source/ona/kym/Chunk.zig @@ -2,8 +2,6 @@ const app = @import("../app.zig"); const Compiler = @import("./Compiler.zig"); -const builtin = @import("builtin"); - const coral = @import("coral"); const file = @import("../file.zig"); @@ -15,6 +13,8 @@ const tree = @import("./tree.zig"); name: *kym.RuntimeRef, arity: u8, opcodes: OpcodeList, +lines: LineList, +cursor: usize, constants: ConstList, bindings: []?*kym.RuntimeRef, @@ -27,7 +27,9 @@ const Builtin = enum { const ConstList = coral.list.Stack(*kym.RuntimeRef); -const OpcodeList = coral.list.Stack(union (enum) { +const LineList = coral.list.Stack(u32); + +pub const Opcode = union (enum) { ret, pop, push_nil, @@ -64,7 +66,9 @@ const OpcodeList = coral.list.Stack(union (enum) { jt: u32, jf: u32, -}); +}; + +const OpcodeList = coral.list.Stack(Opcode); const Self = @This(); @@ -99,26 +103,26 @@ pub fn dump(chunk: Self, env: *kym.RuntimeEnv) kym.RuntimeError!*kym.RuntimeRef coral.debug.assert(string != null); - break: print coral.utf8.print_formatted(writer, "push const <{value}>\n", .{.value = string.?}); + break: print coral.utf8.print_formatted(writer, "push const ({value})\n", .{.value = string.?}); }, - .push_local => |push_local| coral.utf8.print_formatted(writer, "push local <{local}>\n", .{ + .push_local => |push_local| coral.utf8.print_formatted(writer, "push local ({local})\n", .{ .local = push_local, }), .push_top => coral.utf8.print_string(writer, "push top\n"), - .push_table => |push_table| coral.utf8.print_formatted(writer, "push table <{count}>\n", .{ + .push_table => |push_table| coral.utf8.print_formatted(writer, "push table ({count})\n", .{ .count = push_table, }), .push_boxed => coral.utf8.print_string(writer, "push boxed\n"), - .push_binding => |push_binding| coral.utf8.print_formatted(writer, "push binding <{binding}>\n", .{ + .push_binding => |push_binding| coral.utf8.print_formatted(writer, "push binding ({binding})\n", .{ .binding = push_binding, }), - .push_builtin => |push_builtin| coral.utf8.print_formatted(writer, "push builtin <{builtin}>\n", .{ + .push_builtin => |push_builtin| coral.utf8.print_formatted(writer, "push builtin ({builtin})\n", .{ .builtin = switch (push_builtin) { .import => "import", .print => "print", @@ -127,11 +131,11 @@ pub fn dump(chunk: Self, env: *kym.RuntimeEnv) kym.RuntimeError!*kym.RuntimeRef }, }), - .bind => |bind| coral.utf8.print_formatted(writer, "bind <{count}>\n", .{ + .bind => |bind| coral.utf8.print_formatted(writer, "bind ({count})\n", .{ .count = bind, }), - .set_local => |local_set| coral.utf8.print_formatted(writer, "set local <{local}>\n", .{ + .set_local => |local_set| coral.utf8.print_formatted(writer, "set local ({local})\n", .{ .local = local_set, }), @@ -139,7 +143,7 @@ pub fn dump(chunk: Self, env: *kym.RuntimeEnv) kym.RuntimeError!*kym.RuntimeRef .set_box => coral.utf8.print_string(writer, "set box\n"), .get_dynamic => coral.utf8.print_string(writer, "get dynamic\n"), .set_dynamic => coral.utf8.print_string(writer, "set dynamic\n"), - .call => |call| coral.utf8.print_formatted(writer, "call <{count}>\n", .{.count = call}), + .call => |call| coral.utf8.print_formatted(writer, "call ({count})\n", .{.count = call}), .not => coral.utf8.print_string(writer, "not\n"), .neg => coral.utf8.print_string(writer, "neg\n"), .add => coral.utf8.print_string(writer, "add\n"), @@ -151,19 +155,19 @@ pub fn dump(chunk: Self, env: *kym.RuntimeEnv) kym.RuntimeError!*kym.RuntimeRef .clt => coral.utf8.print_string(writer, "clt\n"), .cge => coral.utf8.print_string(writer, "cge\n"), .cle => coral.utf8.print_string(writer, "cle\n"), - .jf => |jf| coral.utf8.print_formatted(writer, "jf <{instruction}>\n", .{.instruction = jf}), - .jt => |jt| coral.utf8.print_formatted(writer, "jt <{instruction}>\n", .{.instruction = jt}), + .jf => |jf| coral.utf8.print_formatted(writer, "jf ({instruction})\n", .{.instruction = jf}), + .jt => |jt| coral.utf8.print_formatted(writer, "jt ({instruction})\n", .{.instruction = jt}), }; } return env.new_string(buffer.values); } -pub fn execute(self: Self, env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeError!?*kym.RuntimeRef { - var opcode_cursor = @as(u32, 0); +pub fn execute(self: *Self, env: *kym.RuntimeEnv, frame: *const kym.Frame) kym.RuntimeError!?*kym.RuntimeRef { + self.cursor = 0; - while (opcode_cursor < self.opcodes.values.len) : (opcode_cursor += 1) { - switch (self.opcodes.values[opcode_cursor]) { + while (self.cursor < self.opcodes.values.len) : (self.cursor += 1) { + switch (self.opcodes.values[self.cursor]) { .ret => break, .pop => { @@ -306,27 +310,24 @@ pub fn execute(self: Self, env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeEr defer env.discard(box); - const boxed = box.as_boxed() orelse { - return env.raise(error.TypeMismatch, "{typename} is not unboxable", .{.typename = box.typename()}); - }; + if (try env.get_boxed(box)) |unboxed| { + errdefer env.discard(unboxed); - try env.locals.push_one(if (boxed.*) |value| value.acquire() else null); + try env.locals.push_one(unboxed); + } else { + try env.locals.push_one(null); + } }, .set_box => { const box = try env.expect(try env.pop_local()); - errdefer env.discard(box); + defer env.discard(box); - const boxed = box.as_boxed() orelse { - return env.raise(error.TypeMismatch, "{typename} is not unboxable", .{.typename = box.typename()}); - }; + const value = try env.expect(try env.pop_local()); - if (boxed.*) |value| { - env.discard(value); - } - - boxed.* = box; + defer env.discard(box); + try env.set_boxed(box, value); }, .get_dynamic => { @@ -379,7 +380,7 @@ pub fn execute(self: Self, env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeEr defer env.pop_frame(); - break: call try env.call_frame(callable, call_frame); + break: call try env.call_frame(&call_frame); }; errdefer { @@ -532,10 +533,10 @@ pub fn execute(self: Self, env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeEr defer env.discard(condition); if (!condition.is_truthy()) { - opcode_cursor = jf; + self.cursor = jf; } } else { - opcode_cursor = jf; + self.cursor = jf; } }, @@ -544,7 +545,7 @@ pub fn execute(self: Self, env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeEr defer env.discard(condition); if (condition.is_truthy()) { - opcode_cursor = jt; + self.cursor = jt; } } }, @@ -581,8 +582,10 @@ pub fn make(env: *kym.RuntimeEnv, name: []const coral.io.Byte, environment: *con .name = try env.new_symbol(name), .opcodes = OpcodeList.make(env.allocator), .constants = ConstList.make(env.allocator), + .lines = LineList.make(env.allocator), .bindings = &.{}, .arity = environment.argument_count, + .cursor = 0, }; var compiler = Compiler{ @@ -595,17 +598,17 @@ pub fn make(env: *kym.RuntimeEnv, name: []const coral.io.Byte, environment: *con return chunk; } -fn syscall_import(env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeError!?*kym.RuntimeRef { +fn syscall_import(env: *kym.RuntimeEnv, frame: *const kym.Frame) kym.RuntimeError!?*kym.RuntimeRef { return env.import(file.Path.from(&.{try env.unwrap_string(try frame.expect_arg(env, 0))})); } -fn syscall_print(env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeError!?*kym.RuntimeRef { +fn syscall_print(env: *kym.RuntimeEnv, frame: *const kym.Frame) kym.RuntimeError!?*kym.RuntimeRef { env.print(try env.unwrap_string(try frame.expect_arg(env, 0))); return null; } -fn syscall_vec2(env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeError!?*kym.RuntimeRef { +fn syscall_vec2(env: *kym.RuntimeEnv, frame: *const kym.Frame) kym.RuntimeError!?*kym.RuntimeRef { const x = @as(f32, @floatCast(try env.unwrap_float(try frame.expect_arg(env, 0)))); if (frame.has_arg(env, 1)) |y| { @@ -615,7 +618,7 @@ fn syscall_vec2(env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeError!?*kym.R return env.new_vector2(x, x); } -fn syscall_vec3(env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeError!?*kym.RuntimeRef { +fn syscall_vec3(env: *kym.RuntimeEnv, frame: *const kym.Frame) kym.RuntimeError!?*kym.RuntimeRef { const x = @as(f32, @floatCast(try env.unwrap_float(try frame.expect_arg(env, 0)))); if (frame.has_arg(env, 1)) |y| { @@ -635,26 +638,26 @@ pub const typeinfo = &kym.Typeinfo{ .destruct = typeinfo_destruct, .call = typeinfo_call, .to_string = typeinfo_to_string, + .get_arity = typeinfo_get_arity, }; -fn typeinfo_call(env: *kym.RuntimeEnv, userdata: []coral.io.Byte, frame: kym.Frame) kym.RuntimeError!?*kym.RuntimeRef { - const chunk = @as(*Self, @ptrCast(@alignCast(userdata))); - - if (frame.arg_count < chunk.arity) { - return env.raise(error.BadOperation, "expected `{expected_count}` {noun}, {provided_count} provided", .{ - .expected_count = frame.arg_count, - .provided_count = chunk.arity, - .noun = if (frame.arg_count == 1) "argument" else "arguments", - }); - } - - return chunk.execute(env, frame); +fn typeinfo_call(env: *kym.RuntimeEnv, userdata: []coral.io.Byte, frame: *const kym.Frame) kym.RuntimeError!?*kym.RuntimeRef { + return @as(*Self, @ptrCast(@alignCast(userdata))).execute(env, frame); } fn typeinfo_destruct(env: *kym.RuntimeEnv, userdata: []coral.io.Byte) void { @as(*Self, @ptrCast(@alignCast(userdata))).free(env); } +fn typeinfo_get_arity(userdata: []coral.io.Byte) u8 { + return @as(*Self, @ptrCast(@alignCast(userdata))).arity; +} + fn typeinfo_to_string(env: *kym.RuntimeEnv, userdata: []coral.io.Byte) coral.io.AllocationError!*kym.RuntimeRef { return env.to_string(@as(*Self, @ptrCast(@alignCast(userdata))).name); } + +pub fn write(self: *Self, line: u32, opcode: Opcode) coral.io.AllocationError!void { + try self.opcodes.push_one(opcode); + try self.lines.push_one(line); +} diff --git a/source/ona/kym/Compiler.zig b/source/ona/kym/Compiler.zig index c963884..771331f 100644 --- a/source/ona/kym/Compiler.zig +++ b/source/ona/kym/Compiler.zig @@ -37,30 +37,30 @@ fn compile_expression(self: Self, environment: *const tree.Environment, expressi }; switch (expression.kind) { - .nil_literal => try self.chunk.opcodes.push_one(.push_nil), - .true_literal => try self.chunk.opcodes.push_one(.push_true), - .false_literal => try self.chunk.opcodes.push_one(.push_false), + .nil_literal => try self.chunk.write(expression.line, .push_nil), + .true_literal => try self.chunk.write(expression.line, .push_true), + .false_literal => try self.chunk.write(expression.line, .push_false), .number_literal => |literal| { for (literal) |codepoint| { if (codepoint == '.') { - return self.chunk.opcodes.push_one(.{ + return self.chunk.write(expression.line, .{ .push_const = try self.declare_float(number_format.parse(literal, kym.Float) orelse unreachable), }); } } - try self.chunk.opcodes.push_one(.{ + try self.chunk.write(expression.line, .{ .push_const = try self.declare_fixed(number_format.parse(literal, kym.Fixed) orelse unreachable), }); }, .string_literal => |literal| { - try self.chunk.opcodes.push_one(.{.push_const = try self.declare_string(literal)}); + try self.chunk.write(expression.line, .{.push_const = try self.declare_string(literal)}); }, .symbol_literal => |literal| { - try self.chunk.opcodes.push_one(.{.push_const = try self.declare_symbol(literal)}); + try self.chunk.write(expression.line, .{.push_const = try self.declare_symbol(literal)}); }, .table_construct => |table_construct| { @@ -71,13 +71,13 @@ fn compile_expression(self: Self, environment: *const tree.Environment, expressi try self.compile_expression(environment, entry, null); if (entry.kind != .key_value) { - try self.chunk.opcodes.push_one(.push_top); + try self.chunk.write(expression.line, .push_top); } field_count += 1; } - try self.chunk.opcodes.push_one(.{.push_table = field_count}); + try self.chunk.write(expression.line, .{.push_table = field_count}); }, .key_value => |key_value| { @@ -91,7 +91,7 @@ fn compile_expression(self: Self, environment: *const tree.Environment, expressi errdefer chunk.free(self.env); if (lambda_construct.environment.capture_count == 0) { - try self.chunk.opcodes.push_one(.{.push_const = try self.declare_chunk(chunk)}); + try self.chunk.write(expression.line, .{.push_const = try self.declare_chunk(chunk)}); } else { const lambda_captures = lambda_construct.environment.get_captures(); var index = lambda_captures.len; @@ -99,14 +99,14 @@ fn compile_expression(self: Self, environment: *const tree.Environment, expressi while (index != 0) { index -= 1; - try self.chunk.opcodes.push_one(switch (lambda_captures[index]) { + try self.chunk.write(expression.line, switch (lambda_captures[index]) { .declaration_index => |declaration_index| .{.push_local = declaration_index}, .capture_index => |capture_index| .{.push_binding = capture_index}, }); } - try self.chunk.opcodes.push_one(.{.push_const = try self.declare_chunk(chunk)}); - try self.chunk.opcodes.push_one(.{.bind = lambda_construct.environment.capture_count}); + try self.chunk.write(expression.line, .{.push_const = try self.declare_chunk(chunk)}); + try self.chunk.write(expression.line, .{.bind = lambda_construct.environment.capture_count}); } }, @@ -114,7 +114,7 @@ fn compile_expression(self: Self, environment: *const tree.Environment, expressi try self.compile_expression(environment, binary_op.lhs_operand, null); try self.compile_expression(environment, binary_op.rhs_operand, null); - try self.chunk.opcodes.push_one(switch (binary_op.operation) { + try self.chunk.write(expression.line, switch (binary_op.operation) { .addition => .add, .subtraction => .sub, .multiplication => .mul, @@ -130,7 +130,7 @@ fn compile_expression(self: Self, environment: *const tree.Environment, expressi .unary_op => |unary_op| { try self.compile_expression(environment, unary_op.operand, null); - try self.chunk.opcodes.push_one(switch (unary_op.operation) { + try self.chunk.write(expression.line, switch (unary_op.operation) { .boolean_negation => .not, .numeric_negation => .neg, }); @@ -140,25 +140,25 @@ fn compile_expression(self: Self, environment: *const tree.Environment, expressi const argument_count = try self.compile_argument(environment, invoke.argument); try self.compile_expression(environment, invoke.object, null); - try self.chunk.opcodes.push_one(.{.call = argument_count}); + try self.chunk.write(expression.line, .{.call = argument_count}); }, .group => |group| try self.compile_expression(environment, group, null), - .import_builtin => try self.chunk.opcodes.push_one(.{.push_builtin = .import}), - .print_builtin => try self.chunk.opcodes.push_one(.{.push_builtin = .print}), - .vec2_builtin => try self.chunk.opcodes.push_one(.{.push_builtin = .vec2}), - .vec3_builtin => try self.chunk.opcodes.push_one(.{.push_builtin = .vec3}), + .import_builtin => try self.chunk.write(expression.line, .{.push_builtin = .import}), + .print_builtin => try self.chunk.write(expression.line, .{.push_builtin = .print}), + .vec2_builtin => try self.chunk.write(expression.line, .{.push_builtin = .vec2}), + .vec3_builtin => try self.chunk.write(expression.line, .{.push_builtin = .vec3}), .declaration_get => |declaration_get| { if (get_local_index(environment, declaration_get.declaration)) |index| { - return self.chunk.opcodes.push_one(.{.push_local = index}); + return self.chunk.write(expression.line, .{.push_local = index}); } if (try self.get_binding_index(environment, declaration_get.declaration)) |index| { - try self.chunk.opcodes.push_one(.{.push_binding = index}); + try self.chunk.write(expression.line, .{.push_binding = index}); if (is_declaration_boxed(declaration_get.declaration)) { - try self.chunk.opcodes.push_one(.get_box); + try self.chunk.write(expression.line, .get_box); } return; @@ -171,15 +171,15 @@ fn compile_expression(self: Self, environment: *const tree.Environment, expressi if (get_local_index(environment, declaration_set.declaration)) |index| { try self.compile_expression(environment, declaration_set.assign, null); - return self.chunk.opcodes.push_one(.{.set_local = index}); + return self.chunk.write(expression.line, .{.set_local = index}); } if (try self.get_binding_index(environment, declaration_set.declaration)) |index| { - try self.chunk.opcodes.push_one(.{.push_binding = index}); + try self.chunk.write(expression.line, .{.push_binding = index}); try self.compile_expression(environment, declaration_set.assign, null); if (is_declaration_boxed(declaration_set.declaration)) { - try self.chunk.opcodes.push_one(.set_box); + try self.chunk.write(expression.line, .set_box); } return; @@ -190,28 +190,28 @@ fn compile_expression(self: Self, environment: *const tree.Environment, expressi .field_get => |field_get| { try self.compile_expression(environment, field_get.object, null); - try self.chunk.opcodes.push_one(.{.push_const = try self.declare_symbol(field_get.identifier)}); - try self.chunk.opcodes.push_one(.get_dynamic); + try self.chunk.write(expression.line, .{.push_const = try self.declare_symbol(field_get.identifier)}); + try self.chunk.write(expression.line, .get_dynamic); }, .field_set => |field_set| { try self.compile_expression(environment, field_set.object, null); - try self.chunk.opcodes.push_one(.{.push_const = try self.declare_symbol(field_set.identifier)}); + try self.chunk.write(expression.line, .{.push_const = try self.declare_symbol(field_set.identifier)}); try self.compile_expression(environment, field_set.assign, null); - try self.chunk.opcodes.push_one(.set_dynamic); + try self.chunk.write(expression.line, .set_dynamic); }, .subscript_get => |subscript_get| { try self.compile_expression(environment, subscript_get.object, null); try self.compile_expression(environment, subscript_get.index, null); - try self.chunk.opcodes.push_one(.get_dynamic); + try self.chunk.write(expression.line, .get_dynamic); }, .subscript_set => |subscript_set| { try self.compile_expression(environment, subscript_set.object, null); try self.compile_expression(environment, subscript_set.index, null); try self.compile_expression(environment, subscript_set.assign, null); - try self.chunk.opcodes.push_one(.set_dynamic); + try self.chunk.write(expression.line, .set_dynamic); }, } } @@ -221,7 +221,7 @@ pub fn compile_environment(self: Self, environment: *const tree.Environment) kym const last_statement = try self.compile_statement(environment, statement); if (last_statement.kind != .@"return") { - try self.chunk.opcodes.push_one(.push_nil); + try self.chunk.write(last_statement.line, .push_nil); } } } @@ -235,16 +235,16 @@ fn compile_statement(self: Self, environment: *const tree.Environment, initial_s if (@"return".returned_expression) |expression| { try self.compile_expression(environment, expression, null); } else { - try self.chunk.opcodes.push_one(.push_nil); + try self.chunk.write(current_statement.line, .push_nil); } // TODO: Omit ret calls at ends of chunk. - try self.chunk.opcodes.push_one(.ret); + try self.chunk.write(current_statement.line, .ret); }, .@"while" => |@"while"| { try self.compile_expression(environment, @"while".loop_expression, null); - try self.chunk.opcodes.push_one(.{.jf = 0}); + try self.chunk.write(current_statement.line, .{.jf = 0}); const origin_index = @as(u32, @intCast(self.chunk.opcodes.values.len - 1)); @@ -252,12 +252,12 @@ fn compile_statement(self: Self, environment: *const tree.Environment, initial_s self.chunk.opcodes.values[origin_index].jf = @intCast(self.chunk.opcodes.values.len - 1); try self.compile_expression(environment, @"while".loop_expression, null); - try self.chunk.opcodes.push_one(.{.jt = origin_index}); + try self.chunk.write(current_statement.line, .{.jt = origin_index}); }, .@"if" => |@"if"| { try self.compile_expression(environment, @"if".then_expression, null); - try self.chunk.opcodes.push_one(.{.jf = 0}); + try self.chunk.write(current_statement.line, .{.jf = 0}); const origin_index = @as(u32, @intCast(self.chunk.opcodes.values.len - 1)); @@ -273,7 +273,7 @@ fn compile_statement(self: Self, environment: *const tree.Environment, initial_s try self.compile_expression(environment, declare.initial_expression, declare.declaration.identifier); if (is_declaration_boxed(declare.declaration)) { - try self.chunk.opcodes.push_one(.push_boxed); + try self.chunk.write(current_statement.line, .push_boxed); } }, @@ -281,7 +281,7 @@ fn compile_statement(self: Self, environment: *const tree.Environment, initial_s try self.compile_expression(environment, top_expression, null); if (top_expression.kind == .invoke) { - try self.chunk.opcodes.push_one(.pop); + try self.chunk.write(current_statement.line, .pop); } }, } diff --git a/source/ona/kym/Expr.zig b/source/ona/kym/Expr.zig index 5a84539..3213191 100644 --- a/source/ona/kym/Expr.zig +++ b/source/ona/kym/Expr.zig @@ -7,6 +7,7 @@ const tokens = @import("./tokens.zig"); const tree = @import("./tree.zig"); next: ?*const Self = null, +line: u32, kind: union (enum) { nil_literal, @@ -80,6 +81,8 @@ pub const BinaryOp = struct { const unnecessary_temp = expression; expression = try root.create_expr(.{ + .line = stream.lines_stepped, + .kind = .{ .binary_op = .{ .rhs_operand = try parse_next(root, stream, environment), @@ -173,6 +176,8 @@ pub fn parse(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Enviro } return root.create_expr(.{ + .line = stream.lines_stepped, + .kind = switch (expression.kind) { .declaration_get => |declaration_get| convert: { if (declaration_get.declaration.is.readonly) { @@ -239,7 +244,7 @@ fn parse_factor(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Env const unnecessary_temp = expression; expression = try root.create_expr(.{ - .next = null, + .line = stream.lines_stepped, .kind = .{ .field_get = .{ @@ -263,7 +268,7 @@ fn parse_factor(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Env const unnecessary_temp = expression; expression = try root.create_expr(.{ - .next = null, + .line = stream.lines_stepped, .kind = .{ .subscript_get = .{ @@ -281,6 +286,8 @@ fn parse_factor(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Env }, .symbol_paren_left => { + const lines_stepped = stream.lines_stepped; + stream.skip_newlines(); var first_argument = @as(?*Self, null); @@ -310,7 +317,7 @@ fn parse_factor(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Env const unnecessary_temp = expression; expression = try root.create_expr(.{ - .next = null, + .line = lines_stepped, .kind = .{ .invoke = .{ @@ -341,37 +348,55 @@ fn parse_operand(root: *tree.Root, stream: *tokens.Stream, environment: *tree.En stream.skip_newlines(); - return root.create_expr(.{.kind = .{.group = expression}}); + return root.create_expr(.{ + .line = stream.lines_stepped, + .kind = .{.group = expression}, + }); }, .keyword_nil => { stream.skip_newlines(); - return root.create_expr(.{.kind = .nil_literal}); + return root.create_expr(.{ + .line = stream.lines_stepped, + .kind = .nil_literal, + }); }, .keyword_true => { stream.skip_newlines(); - return root.create_expr(.{.kind = .true_literal}); + return root.create_expr(.{ + .line = stream.lines_stepped, + .kind = .true_literal, + }); }, .keyword_false => { stream.skip_newlines(); - return root.create_expr(.{.kind = .false_literal}); + return root.create_expr(.{ + .line = stream.lines_stepped, + .kind = .false_literal, + }); }, .number => |value| { stream.skip_newlines(); - return root.create_expr(.{.kind = .{.number_literal = value}}); + return root.create_expr(.{ + .line = stream.lines_stepped, + .kind = .{.number_literal = value}, + }); }, .string => |value| { stream.skip_newlines(); - return root.create_expr(.{.kind = .{.string_literal = value}}); + return root.create_expr(.{ + .line = stream.lines_stepped, + .kind = .{.string_literal = value}, + }); }, .symbol_at => { @@ -385,19 +410,31 @@ fn parse_operand(root: *tree.Root, stream: *tokens.Stream, environment: *tree.En stream.skip_newlines(); if (coral.io.are_equal(identifier, "import")) { - return root.create_expr(.{.kind = .import_builtin}); + return root.create_expr(.{ + .line = stream.lines_stepped, + .kind = .import_builtin, + }); } if (coral.io.are_equal(identifier, "print")) { - return root.create_expr(.{.kind = .print_builtin}); + return root.create_expr(.{ + .line = stream.lines_stepped, + .kind = .print_builtin, + }); } if (coral.io.are_equal(identifier, "vec2")) { - return root.create_expr(.{.kind = .vec2_builtin}); + return root.create_expr(.{ + .line = stream.lines_stepped, + .kind = .vec2_builtin, + }); } if (coral.io.are_equal(identifier, "vec3")) { - return root.create_expr(.{.kind = .vec3_builtin}); + return root.create_expr(.{ + .line = stream.lines_stepped, + .kind = .vec3_builtin, + }); } return root.report_error(stream, "unexpected identifier after `@`", .{}); @@ -413,13 +450,18 @@ fn parse_operand(root: *tree.Root, stream: *tokens.Stream, environment: *tree.En stream.skip_newlines(); - return root.create_expr(.{.kind = .{.symbol_literal = identifier}}); + return root.create_expr(.{ + .line = stream.lines_stepped, + .kind = .{.symbol_literal = identifier}, + }); }, .identifier => |identifier| { stream.skip_newlines(); return root.create_expr(.{ + .line = stream.lines_stepped, + .kind = .{ .declaration_get = .{ .declaration = (try environment.resolve_declaration(identifier)) orelse { @@ -486,14 +528,20 @@ fn parse_operand(root: *tree.Root, stream: *tokens.Stream, environment: *tree.En stream.skip_newlines(); - return root.create_expr(.{.kind = .{.lambda_construct = .{.environment = lambda_environment}}}); + return root.create_expr(.{ + .line = stream.lines_stepped, + .kind = .{.lambda_construct = .{.environment = lambda_environment}}, + }); }, .symbol_brace_left => { stream.skip_newlines(); if (stream.token == .symbol_brace_right) { - return root.create_expr(.{.kind = .{.table_construct = .{.entry = null}}}); + return root.create_expr(.{ + .line = stream.lines_stepped, + .kind = .{.table_construct = .{.entry = null}}, + }); } const first_entry = try parse_table_entry(root, stream, environment); @@ -518,14 +566,17 @@ fn parse_operand(root: *tree.Root, stream: *tokens.Stream, environment: *tree.En stream.skip_newlines(); - return root.create_expr(.{.kind = .{.table_construct = .{.entry = first_entry}}}); + return root.create_expr(.{ + .line = stream.lines_stepped, + .kind = .{.table_construct = .{.entry = first_entry}}, + }); }, .symbol_minus => { stream.skip_newlines(); return root.create_expr(.{ - .next = null, + .line = stream.lines_stepped, .kind = .{ .unary_op = .{ @@ -540,7 +591,7 @@ fn parse_operand(root: *tree.Root, stream: *tokens.Stream, environment: *tree.En stream.skip_newlines(); return root.create_expr(.{ - .next = null, + .line = stream.lines_stepped, .kind = .{ .unary_op = .{ @@ -574,10 +625,16 @@ fn parse_table_entry(root: *tree.Root, stream: *tokens.Stream, environment: *tre stream.skip_newlines(); return root.create_expr(.{ + .line = stream.lines_stepped, + .kind = .{ .key_value = .{ .value = try parse(root, stream, environment), - .key = try root.create_expr(.{.kind = .{.symbol_literal = field}}), + + .key = try root.create_expr(.{ + .line = stream.lines_stepped, + .kind = .{.symbol_literal = field}, + }), }, }, }); @@ -601,6 +658,8 @@ fn parse_table_entry(root: *tree.Root, stream: *tokens.Stream, environment: *tre stream.skip_newlines(); return root.create_expr(.{ + .line = stream.lines_stepped, + .kind = .{ .key_value = .{ .value = try parse(root, stream, environment), diff --git a/source/ona/kym/Stmt.zig b/source/ona/kym/Stmt.zig index c31059b..df49893 100644 --- a/source/ona/kym/Stmt.zig +++ b/source/ona/kym/Stmt.zig @@ -7,6 +7,7 @@ const tokens = @import("./tokens.zig"); const tree = @import("./tree.zig"); next: ?*const Self = null, +line: u32, kind: union (enum) { top_expression: *const Expr, @@ -45,6 +46,7 @@ pub fn parse(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Enviro if (stream.token != .end and stream.token != .newline) { return root.create_stmt(.{ + .line = stream.lines_stepped, .kind = .{.@"return" = .{.returned_expression = try Expr.parse(root, stream, environment)}}, }); } @@ -53,7 +55,10 @@ pub fn parse(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Enviro return root.report_error(stream, "expected end or newline after return statement", .{}); } - return root.create_stmt(.{.kind = .{.@"return" = .{.returned_expression = null}}}); + return root.create_stmt(.{ + .line = stream.lines_stepped, + .kind = .{.@"return" = .{.returned_expression = null}}, + }); }, .keyword_while => { @@ -83,6 +88,8 @@ pub fn parse(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Enviro } return root.create_stmt(.{ + .line = stream.lines_stepped, + .kind = .{ .@"while" = .{ .loop = first_statement, @@ -113,6 +120,8 @@ pub fn parse(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Enviro stream.skip_newlines(); return root.create_stmt(.{ + .line = stream.lines_stepped, + .kind = .{ .declare = .{ .initial_expression = try Expr.parse(root, stream, environment), @@ -135,7 +144,10 @@ pub fn parse(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Enviro .keyword_if => return parse_branch(root, stream, environment), - else => return root.create_stmt(.{.kind = .{.top_expression = try Expr.parse(root, stream, environment)}}), + else => return root.create_stmt(.{ + .line = stream.lines_stepped, + .kind = .{.top_expression = try Expr.parse(root, stream, environment)}, + }), } } @@ -159,7 +171,7 @@ fn parse_branch(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Env stream.skip_newlines(); return root.create_stmt(.{ - .next = null, + .line = stream.lines_stepped, .kind = .{ .@"if" = .{ @@ -193,7 +205,7 @@ fn parse_branch(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Env stream.skip_newlines(); return root.create_stmt(.{ - .next = null, + .line = stream.lines_stepped, .kind = .{ .@"if" = .{ @@ -207,7 +219,7 @@ fn parse_branch(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Env .keyword_elif => { return root.create_stmt(.{ - .next = null, + .line = stream.lines_stepped, .kind = .{ .@"if" = .{ diff --git a/source/ona/kym/Table.zig b/source/ona/kym/Table.zig index aea1a72..4638ff5 100644 --- a/source/ona/kym/Table.zig +++ b/source/ona/kym/Table.zig @@ -47,15 +47,15 @@ pub const typeinfo = &kym.Typeinfo{ .size = @sizeOf(Self), .name = "table", .destruct = typeinfo_destruct, - .get = typeinfo_get, - .set = typeinfo_set, + .get_index = typeinfo_get_index, + .set_index = typeinfo_set_index, }; fn typeinfo_destruct(env: *kym.RuntimeEnv, userdata: []coral.io.Byte) void { @as(*Self, @ptrCast(@alignCast(userdata))).free(env); } -fn typeinfo_get(env: *kym.RuntimeEnv, userdata: []coral.io.Byte, index: *const kym.RuntimeRef) kym.RuntimeError!?*kym.RuntimeRef { +fn typeinfo_get_index(env: *kym.RuntimeEnv, userdata: []coral.io.Byte, index: *const kym.RuntimeRef) kym.RuntimeError!?*kym.RuntimeRef { const table = @as(*Self, @ptrCast(@alignCast(userdata))); const acquired_index = index.acquire(); @@ -79,7 +79,7 @@ fn typeinfo_get(env: *kym.RuntimeEnv, userdata: []coral.io.Byte, index: *const k return null; } -fn typeinfo_set(env: *kym.RuntimeEnv, userdata: []coral.io.Byte, index: *const kym.RuntimeRef, value: ?*const kym.RuntimeRef) kym.RuntimeError!void { +fn typeinfo_set_index(env: *kym.RuntimeEnv, userdata: []coral.io.Byte, index: *const kym.RuntimeRef, value: ?*const kym.RuntimeRef) kym.RuntimeError!void { const table = @as(*Self, @ptrCast(@alignCast(userdata))); const acquired_index = index.acquire(); diff --git a/source/ona/kym/tokens.zig b/source/ona/kym/tokens.zig index 5ad4a58..4acf6e6 100755 --- a/source/ona/kym/tokens.zig +++ b/source/ona/kym/tokens.zig @@ -103,7 +103,7 @@ pub const Token = union(enum) { pub const Stream = struct { source: []const coral.io.Byte, - lines_stepped: usize = 1, + lines_stepped: u32 = 1, token: Token = .newline, pub fn skip_newlines(self: *Stream) void { From 19e1fb26344bd2af90e59b0055a6e74cf39c63cd Mon Sep 17 00:00:00 2001 From: kayomn Date: Sun, 5 Nov 2023 23:46:07 +0000 Subject: [PATCH 5/5] Streamline arity detection --- source/ona/kym.zig | 36 +++++++++++++++--------------------- source/ona/kym/Chunk.zig | 5 ----- source/ona/kym/Table.zig | 8 ++++---- 3 files changed, 19 insertions(+), 30 deletions(-) diff --git a/source/ona/kym.zig b/source/ona/kym.zig index a3e463e..2fc8f88 100644 --- a/source/ona/kym.zig +++ b/source/ona/kym.zig @@ -271,7 +271,7 @@ pub const RuntimeEnv = struct { } }, - .dynamic => |dynamic| dynamic.typeinfo().get_index(self, dynamic.userdata(), index), + .dynamic => |dynamic| dynamic.typeinfo().get(self, dynamic.userdata(), index), else => self.raise(error.TypeMismatch, "{typename} is not get-indexable", .{ .typename = indexable.typename(), @@ -528,17 +528,16 @@ pub const RuntimeEnv = struct { } pub fn push_frame(self: *RuntimeEnv, callable: *const RuntimeRef, arg_count: u8) RuntimeError!Frame { - const arity = switch (callable.object().payload) { - .dynamic => |dynamic| dynamic.typeinfo().get_arity(dynamic.userdata()), - else => 0, - }; + if (callable.as_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}` {noun}, {provided} provided", .{ - .expected = arity, - .provided = arg_count, - .noun = if (arity == 1) "argument" else "arguments", - }); + if (arg_count < arity) { + return self.raise(error.BadOperation, "expected `{expected}` {noun}, {provided} provided", .{ + .expected = arity, + .provided = arg_count, + .noun = if (arity == 1) "argument" else "arguments", + }); + } } const frame = Frame{ @@ -607,7 +606,7 @@ pub const RuntimeEnv = struct { pub fn set(self: *RuntimeEnv, indexable: *RuntimeRef, index: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void { return switch (indexable.object().payload) { - .dynamic => |dynamic| dynamic.typeinfo().set_index(self, dynamic.userdata(), index, value), + .dynamic => |dynamic| dynamic.typeinfo().set(self, dynamic.userdata(), index, value), else => self.raise(error.TypeMismatch, "{typename} is not set-indexable", .{ .typename = indexable.typename(), @@ -992,24 +991,19 @@ pub const Typeinfo = struct { size: usize, destruct: ?*const fn (env: *RuntimeEnv, userdata: []coral.io.Byte) void = null, to_string: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte) coral.io.AllocationError!*RuntimeRef = default_to_string, - get_arity: *const fn (userdata: []coral.io.Byte) u8 = default_get_arity, call: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, frame: *const Frame) RuntimeError!?*RuntimeRef = default_call, - get_index: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, index: *const RuntimeRef) RuntimeError!?*RuntimeRef = default_get_index, - set_index: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, value: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void = default_set_index, + get: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, index: *const RuntimeRef) RuntimeError!?*RuntimeRef = default_get, + set: *const fn (env: *RuntimeEnv, userdata: []coral.io.Byte, value: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void = default_set, fn default_call(env: *RuntimeEnv, _: []coral.io.Byte, _: *const Frame) RuntimeError!?*RuntimeRef { return env.raise(error.BadOperation, "this dynamic object is not callable", .{}); } - fn default_get_arity(_: []coral.io.Byte) u8 { - return 0; - } - - fn default_get_index(env: *RuntimeEnv, _: []coral.io.Byte, _: *const RuntimeRef) RuntimeError!?*RuntimeRef { + fn default_get(env: *RuntimeEnv, _: []coral.io.Byte, _: *const RuntimeRef) RuntimeError!?*RuntimeRef { return env.raise(error.BadOperation, "this dynamic object is not get-indexable", .{}); } - fn default_set_index(env: *RuntimeEnv, _: []coral.io.Byte, _: *const RuntimeRef, _: ?*const RuntimeRef) RuntimeError!void { + fn default_set(env: *RuntimeEnv, _: []coral.io.Byte, _: *const RuntimeRef, _: ?*const RuntimeRef) RuntimeError!void { return env.raise(error.BadOperation, "this dynamic object is not set-indexable", .{}); } diff --git a/source/ona/kym/Chunk.zig b/source/ona/kym/Chunk.zig index 499d8dc..bcfd649 100644 --- a/source/ona/kym/Chunk.zig +++ b/source/ona/kym/Chunk.zig @@ -638,7 +638,6 @@ pub const typeinfo = &kym.Typeinfo{ .destruct = typeinfo_destruct, .call = typeinfo_call, .to_string = typeinfo_to_string, - .get_arity = typeinfo_get_arity, }; fn typeinfo_call(env: *kym.RuntimeEnv, userdata: []coral.io.Byte, frame: *const kym.Frame) kym.RuntimeError!?*kym.RuntimeRef { @@ -649,10 +648,6 @@ fn typeinfo_destruct(env: *kym.RuntimeEnv, userdata: []coral.io.Byte) void { @as(*Self, @ptrCast(@alignCast(userdata))).free(env); } -fn typeinfo_get_arity(userdata: []coral.io.Byte) u8 { - return @as(*Self, @ptrCast(@alignCast(userdata))).arity; -} - fn typeinfo_to_string(env: *kym.RuntimeEnv, userdata: []coral.io.Byte) coral.io.AllocationError!*kym.RuntimeRef { return env.to_string(@as(*Self, @ptrCast(@alignCast(userdata))).name); } diff --git a/source/ona/kym/Table.zig b/source/ona/kym/Table.zig index 4638ff5..aea1a72 100644 --- a/source/ona/kym/Table.zig +++ b/source/ona/kym/Table.zig @@ -47,15 +47,15 @@ pub const typeinfo = &kym.Typeinfo{ .size = @sizeOf(Self), .name = "table", .destruct = typeinfo_destruct, - .get_index = typeinfo_get_index, - .set_index = typeinfo_set_index, + .get = typeinfo_get, + .set = typeinfo_set, }; fn typeinfo_destruct(env: *kym.RuntimeEnv, userdata: []coral.io.Byte) void { @as(*Self, @ptrCast(@alignCast(userdata))).free(env); } -fn typeinfo_get_index(env: *kym.RuntimeEnv, userdata: []coral.io.Byte, index: *const kym.RuntimeRef) kym.RuntimeError!?*kym.RuntimeRef { +fn typeinfo_get(env: *kym.RuntimeEnv, userdata: []coral.io.Byte, index: *const kym.RuntimeRef) kym.RuntimeError!?*kym.RuntimeRef { const table = @as(*Self, @ptrCast(@alignCast(userdata))); const acquired_index = index.acquire(); @@ -79,7 +79,7 @@ fn typeinfo_get_index(env: *kym.RuntimeEnv, userdata: []coral.io.Byte, index: *c return null; } -fn typeinfo_set_index(env: *kym.RuntimeEnv, userdata: []coral.io.Byte, index: *const kym.RuntimeRef, value: ?*const kym.RuntimeRef) kym.RuntimeError!void { +fn typeinfo_set(env: *kym.RuntimeEnv, userdata: []coral.io.Byte, index: *const kym.RuntimeRef, value: ?*const kym.RuntimeRef) kym.RuntimeError!void { const table = @as(*Self, @ptrCast(@alignCast(userdata))); const acquired_index = index.acquire();