Add global function calling syntax support
This commit is contained in:
parent
1828eb3b45
commit
84e5236ba1
|
@ -73,7 +73,7 @@ pub fn Stack(comptime Value: type) type {
|
||||||
self.capacity = packed_size;
|
self.capacity = packed_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn peek(self: *Self) ?Value {
|
pub fn peek(self: Self) ?Value {
|
||||||
if (self.values.len == 0) {
|
if (self.values.len == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ const tokens = @import("./kym/tokens.zig");
|
||||||
pub const CallContext = struct {
|
pub const CallContext = struct {
|
||||||
env: *RuntimeEnv,
|
env: *RuntimeEnv,
|
||||||
caller: ?*const RuntimeRef,
|
caller: ?*const RuntimeRef,
|
||||||
userdata: []u8,
|
userdata: []coral.io.Byte,
|
||||||
args: []const ?*const RuntimeRef = &.{},
|
args: []const ?*const RuntimeRef = &.{},
|
||||||
|
|
||||||
pub fn arg_at(self: CallContext, index: u8) RuntimeError!*const RuntimeRef {
|
pub fn arg_at(self: CallContext, index: u8) RuntimeError!*const RuntimeRef {
|
||||||
|
@ -25,6 +25,8 @@ pub const CallContext = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const Callable = coral.io.Generator(RuntimeError!?*RuntimeRef, CallContext);
|
||||||
|
|
||||||
pub const DynamicObject = struct {
|
pub const DynamicObject = struct {
|
||||||
userdata: []coral.io.Byte,
|
userdata: []coral.io.Byte,
|
||||||
typeinfo: *const Typeinfo,
|
typeinfo: *const Typeinfo,
|
||||||
|
@ -46,6 +48,7 @@ pub const RuntimeEnv = struct {
|
||||||
ref_values: RefSlab,
|
ref_values: RefSlab,
|
||||||
|
|
||||||
const FrameStack = coral.list.Stack(struct {
|
const FrameStack = coral.list.Stack(struct {
|
||||||
|
arg_count: u8,
|
||||||
locals_top: usize,
|
locals_top: usize,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -74,7 +77,7 @@ pub const RuntimeEnv = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn acquire(self: *RuntimeEnv, ref: ?*const RuntimeRef) ?*RuntimeRef {
|
pub fn acquire(self: *RuntimeEnv, ref: ?*const RuntimeRef) ?*RuntimeRef {
|
||||||
const key = @intFromPtr(ref);
|
const key = @intFromPtr(ref orelse return null);
|
||||||
var ref_data = self.ref_values.remove(key);
|
var ref_data = self.ref_values.remove(key);
|
||||||
|
|
||||||
coral.debug.assert(ref_data != null);
|
coral.debug.assert(ref_data != null);
|
||||||
|
@ -88,23 +91,24 @@ pub const RuntimeEnv = struct {
|
||||||
|
|
||||||
pub fn call(
|
pub fn call(
|
||||||
self: *RuntimeEnv,
|
self: *RuntimeEnv,
|
||||||
caller_ref: *const RuntimeRef,
|
caller_ref: ?*const RuntimeRef,
|
||||||
callable_ref: *const RuntimeRef,
|
callable_ref: ?*const RuntimeRef,
|
||||||
arg_refs: []const *const RuntimeRef,
|
arg_refs: []const *const RuntimeRef,
|
||||||
) RuntimeError!*RuntimeRef {
|
) RuntimeError!?*RuntimeRef {
|
||||||
const callable = (try callable_ref.fetch(self)).to_object();
|
return switch (self.unbox(callable_ref)) {
|
||||||
|
.dynamic => |dynamic| dynamic.typeinfo.call(.{
|
||||||
|
.env = self,
|
||||||
|
.caller = caller_ref,
|
||||||
|
.userdata = dynamic.userdata,
|
||||||
|
.args = arg_refs,
|
||||||
|
}),
|
||||||
|
|
||||||
return callable.userinfo.call(.{
|
else => self.raise(error.TypeMismatch, "type is not callable"),
|
||||||
.env = self,
|
};
|
||||||
.caller = caller_ref,
|
|
||||||
.callable = callable_ref,
|
|
||||||
.userdata = callable.userdata,
|
|
||||||
.args = arg_refs,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn discard(self: *RuntimeEnv, ref: *?*RuntimeRef) void {
|
pub fn discard(self: *RuntimeEnv, ref: ?*RuntimeRef) void {
|
||||||
const key = @intFromPtr(ref.* orelse return);
|
const key = @intFromPtr(ref orelse return);
|
||||||
var ref_data = self.ref_values.remove(key) orelse unreachable;
|
var ref_data = self.ref_values.remove(key) orelse unreachable;
|
||||||
|
|
||||||
coral.debug.assert(ref_data.ref_count != 0);
|
coral.debug.assert(ref_data.ref_count != 0);
|
||||||
|
@ -120,8 +124,36 @@ pub const RuntimeEnv = struct {
|
||||||
} else {
|
} else {
|
||||||
coral.debug.assert(self.ref_values.insert_at(key, ref_data));
|
coral.debug.assert(self.ref_values.insert_at(key, ref_data));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ref.* = null;
|
pub fn dynamic_get(self: *RuntimeEnv, indexable_ref: ?*const RuntimeRef, index_ref: ?*const RuntimeRef) RuntimeError!?*RuntimeRef {
|
||||||
|
return switch (self.unbox(indexable_ref)) {
|
||||||
|
.nil => self.raise(error.BadOperation, "nil has no such index"),
|
||||||
|
.boolean => self.raise(error.BadOperation, "boolean has no such index"),
|
||||||
|
.number => self.raise(error.BadOperation, "number has no such index"),
|
||||||
|
.string => self.raise(error.BadOperation, "string has no such index"),
|
||||||
|
|
||||||
|
.dynamic => |dynamic| dynamic.typeinfo.get(.{
|
||||||
|
.userdata = dynamic.userdata,
|
||||||
|
.env = self,
|
||||||
|
.index_ref = index_ref,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dynamic_set(self: *RuntimeEnv, indexable_ref: ?*const RuntimeRef, index_ref: ?*const RuntimeRef, value_ref: ?*const RuntimeRef) RuntimeError!void {
|
||||||
|
return switch (self.unbox(indexable_ref)) {
|
||||||
|
.nil => self.raise(error.BadOperation, "nil is immutable"),
|
||||||
|
.boolean => self.raise(error.BadOperation, "boolean is immutable"),
|
||||||
|
.number => self.raise(error.BadOperation, "number is immutable"),
|
||||||
|
.string => self.raise(error.BadOperation, "string is immutable"),
|
||||||
|
|
||||||
|
.dynamic => |dynamic| dynamic.typeinfo.set(.{
|
||||||
|
.userdata = dynamic.userdata,
|
||||||
|
.env = self,
|
||||||
|
.index_ref = index_ref,
|
||||||
|
}, value_ref),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute_file(self: *RuntimeEnv, global_ref: ?*const RuntimeRef, file_access: file.Access, file_path: file.Path) RuntimeError!?*RuntimeRef {
|
pub fn execute_file(self: *RuntimeEnv, global_ref: ?*const RuntimeRef, file_access: file.Access, file_path: file.Path) RuntimeError!?*RuntimeRef {
|
||||||
|
@ -162,16 +194,20 @@ pub const RuntimeEnv = struct {
|
||||||
return exe.execute(global_ref, source.name);
|
return exe.execute(global_ref, source.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn frame_pop(self: *RuntimeEnv) RuntimeError!void {
|
pub fn frame_pop(self: *RuntimeEnv) void {
|
||||||
const frame = self.frames.pop() orelse return self.raise(error.IllegalState, "stack underflow");
|
const frame = self.frames.pop();
|
||||||
|
|
||||||
coral.debug.assert(self.local_refs.drop(self.local_refs.values.len - frame.locals_top));
|
coral.debug.assert(frame != null);
|
||||||
|
coral.debug.assert(self.local_refs.drop((self.local_refs.values.len - frame.?.locals_top) + frame.?.arg_count));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn frame_push(self: *RuntimeEnv) RuntimeError!void {
|
pub fn frame_push(self: *RuntimeEnv, arg_count: u8) RuntimeError![]const *const RuntimeRef {
|
||||||
return self.frames.push_one(.{
|
try self.frames.push_one(.{
|
||||||
|
.arg_count = arg_count,
|
||||||
.locals_top = self.local_refs.values.len,
|
.locals_top = self.local_refs.values.len,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return @as([]const *const RuntimeRef, @ptrCast(self.local_refs.values[(self.local_refs.values.len - arg_count) .. self.local_refs.values.len]));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn free(self: *RuntimeEnv) void {
|
pub fn free(self: *RuntimeEnv) void {
|
||||||
|
@ -179,27 +215,16 @@ pub const RuntimeEnv = struct {
|
||||||
self.ref_values.free();
|
self.ref_values.free();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_dynamic(self: *RuntimeEnv, indexable_ref: ?*const RuntimeRef, index_ref: ?*const RuntimeRef) RuntimeError!?*RuntimeRef {
|
pub fn local_get(self: *RuntimeEnv, local: u8) ?*RuntimeRef {
|
||||||
return switch (self.unbox(indexable_ref)) {
|
|
||||||
.nil => self.raise(error.BadOperation, "nil is immutable"),
|
|
||||||
.boolean => self.raise(error.BadOperation, "boolean has no such index"),
|
|
||||||
.number => self.raise(error.BadOperation, "number has no such index"),
|
|
||||||
.string => self.raise(error.BadOperation, "string has no such index"),
|
|
||||||
|
|
||||||
.dynamic => |dynamic| dynamic.typeinfo.get(.{
|
|
||||||
.userdata = dynamic.userdata,
|
|
||||||
.env = self,
|
|
||||||
.index_ref = index_ref,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn local_get(self: *RuntimeEnv, local: u8) RuntimeError!?*RuntimeRef {
|
|
||||||
return self.local_refs.values[local];
|
return self.local_refs.values[local];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn local_pop(self: *RuntimeEnv) RuntimeError!?*RuntimeRef {
|
pub fn local_pop(self: *RuntimeEnv) ?*RuntimeRef {
|
||||||
return self.local_refs.pop() orelse self.raise(error.IllegalState, "stack underflow");
|
const ref = self.local_refs.pop();
|
||||||
|
|
||||||
|
coral.debug.assert(ref != null);
|
||||||
|
|
||||||
|
return ref.?;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn local_push_ref(self: *RuntimeEnv, ref: ?*RuntimeRef) RuntimeError!void {
|
pub fn local_push_ref(self: *RuntimeEnv, ref: ?*RuntimeRef) RuntimeError!void {
|
||||||
|
@ -214,7 +239,7 @@ pub const RuntimeEnv = struct {
|
||||||
return self.local_refs.push_one(try self.new_number(number));
|
return self.local_refs.push_one(try self.new_number(number));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn local_set(self: *RuntimeEnv, local: u8, value: ?*RuntimeRef) RuntimeError!void {
|
pub fn local_set(self: *RuntimeEnv, local: u8, value: ?*RuntimeRef) void {
|
||||||
self.local_refs.values[local] = self.acquire(value);
|
self.local_refs.values[local] = self.acquire(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,7 +296,7 @@ pub const RuntimeEnv = struct {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn raise(self: *RuntimeEnv, error_value: RuntimeError, error_message: []const u8) RuntimeError {
|
pub fn raise(self: *RuntimeEnv, error_value: RuntimeError, error_message: []const coral.io.Byte) RuntimeError {
|
||||||
// TODO: Print stack trace from state.
|
// TODO: Print stack trace from state.
|
||||||
coral.utf8.print_formatted(self.err_writer, "{name}@{line}: {message}", .{
|
coral.utf8.print_formatted(self.err_writer, "{name}@{line}: {message}", .{
|
||||||
.name = "???",
|
.name = "???",
|
||||||
|
@ -282,21 +307,6 @@ pub const RuntimeEnv = struct {
|
||||||
return error_value;
|
return error_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_dynamic(self: *RuntimeEnv, indexable_ref: ?*const RuntimeRef, index_ref: ?*const RuntimeRef, value_ref: ?*const RuntimeRef) RuntimeError!void {
|
|
||||||
return switch (self.unbox(indexable_ref)) {
|
|
||||||
.nil => self.raise(error.BadOperation, "nil is immutable"),
|
|
||||||
.boolean => self.raise(error.BadOperation, "boolean is immutable"),
|
|
||||||
.number => self.raise(error.BadOperation, "number is immutable"),
|
|
||||||
.string => self.raise(error.BadOperation, "string is immutable"),
|
|
||||||
|
|
||||||
.dynamic => |dynamic| dynamic.typeinfo.set(.{
|
|
||||||
.userdata = dynamic.userdata,
|
|
||||||
.env = self,
|
|
||||||
.index_ref = index_ref,
|
|
||||||
}, value_ref),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unbox(self: *RuntimeEnv, ref: ?*const RuntimeRef) Unboxed {
|
pub fn unbox(self: *RuntimeEnv, ref: ?*const RuntimeRef) Unboxed {
|
||||||
const ref_data = self.ref_values.lookup(@intFromPtr(ref orelse return .nil));
|
const ref_data = self.ref_values.lookup(@intFromPtr(ref orelse return .nil));
|
||||||
|
|
||||||
|
@ -329,13 +339,29 @@ pub const TestContext = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Typeinfo = struct {
|
pub const Typeinfo = struct {
|
||||||
call: *const fn (context: CallContext) RuntimeError!?*RuntimeRef,
|
call: *const fn (context: CallContext) RuntimeError!?*RuntimeRef = default_call,
|
||||||
clean: *const fn (userdata: []u8) void,
|
clean: *const fn (userdata: []coral.io.Byte) void = default_clean,
|
||||||
get: *const fn (context: IndexContext) RuntimeError!?*RuntimeRef,
|
get: *const fn (context: IndexContext) RuntimeError!?*RuntimeRef = default_get,
|
||||||
set: *const fn (context: IndexContext, value: ?*const RuntimeRef) RuntimeError!void,
|
set: *const fn (context: IndexContext, value: ?*const RuntimeRef) RuntimeError!void = default_set,
|
||||||
test_difference: *const fn (context: TestContext) RuntimeError!Float = default_test_difference,
|
test_difference: *const fn (context: TestContext) RuntimeError!Float = default_test_difference,
|
||||||
test_equality: *const fn (context: TestContext) RuntimeError!bool = default_test_equality,
|
test_equality: *const fn (context: TestContext) RuntimeError!bool = default_test_equality,
|
||||||
|
|
||||||
|
fn default_call(context: CallContext) RuntimeError!?*RuntimeRef {
|
||||||
|
return context.env.raise(error.TypeMismatch, "object is not callable");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_clean(_: []coral.io.Byte) void {
|
||||||
|
// Nothing to clean by default.
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_get(context: IndexContext) RuntimeError!?*RuntimeRef {
|
||||||
|
return context.env.raise(error.TypeMismatch, "object is not indexable");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_set(context: IndexContext, _: ?*const RuntimeRef) RuntimeError!void {
|
||||||
|
return context.env.raise(error.TypeMismatch, "object is not indexable");
|
||||||
|
}
|
||||||
|
|
||||||
fn default_test_difference(context: TestContext) RuntimeError!Float {
|
fn default_test_difference(context: TestContext) RuntimeError!Float {
|
||||||
return context.env.raise(error.TypeMismatch, "object is not comparable");
|
return context.env.raise(error.TypeMismatch, "object is not comparable");
|
||||||
}
|
}
|
||||||
|
@ -352,16 +378,28 @@ pub const Unboxed = union (enum) {
|
||||||
nil,
|
nil,
|
||||||
boolean: bool,
|
boolean: bool,
|
||||||
number: Float,
|
number: Float,
|
||||||
string: []const u8,
|
string: []const coral.io.Byte,
|
||||||
dynamic: *const DynamicObject,
|
dynamic: *const DynamicObject,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn get_dynamic_field(env: *RuntimeEnv, indexable: ?*const RuntimeRef, field: []const u8) RuntimeError!?*RuntimeRef {
|
pub fn get_dynamic_field(env: *RuntimeEnv, indexable_ref: ?*const RuntimeRef, field_name: []const coral.io.Byte) RuntimeError!?*RuntimeRef {
|
||||||
var interned_field = try env.new_string(field);
|
const field_name_ref = try env.new_string(field_name);
|
||||||
|
|
||||||
defer env.discard(&interned_field);
|
defer env.discard(field_name_ref);
|
||||||
|
|
||||||
return env.get_dynamic(indexable, interned_field);
|
return env.dynamic_get(indexable_ref, field_name_ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_callable(env: *RuntimeEnv, generator: Callable) RuntimeError!?*RuntimeRef {
|
||||||
|
const Concrete = struct {
|
||||||
|
fn call(context: CallContext) RuntimeError!?*RuntimeRef {
|
||||||
|
return @as(*Callable, @ptrCast(@alignCast(context.userdata.ptr))).invoke(context);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return env.new_dynamic(coral.io.bytes_of(&generator), &.{
|
||||||
|
.call = Concrete.call,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_table(env: *RuntimeEnv) RuntimeError!?*RuntimeRef {
|
pub fn new_table(env: *RuntimeEnv) RuntimeError!?*RuntimeRef {
|
||||||
|
@ -372,6 +410,14 @@ pub fn new_table(env: *RuntimeEnv) RuntimeError!?*RuntimeRef {
|
||||||
return try env.new_dynamic(coral.io.bytes_of(&table), &Table.typeinfo);
|
return try env.new_dynamic(coral.io.bytes_of(&table), &Table.typeinfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_dynamic_field(env: *RuntimeEnv, indexable_ref: ?*const RuntimeRef, field_name: []const coral.io.Byte, value_ref: ?*const RuntimeRef) RuntimeError!void {
|
||||||
|
const field_name_ref = try env.new_string(field_name);
|
||||||
|
|
||||||
|
defer env.discard(field_name_ref);
|
||||||
|
|
||||||
|
return env.dynamic_set(indexable_ref, field_name_ref, value_ref);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn test_difference(env: *RuntimeEnv, lhs_ref: ?*const RuntimeRef, rhs_ref: ?*const RuntimeRef) RuntimeError!Float {
|
pub fn test_difference(env: *RuntimeEnv, lhs_ref: ?*const RuntimeRef, rhs_ref: ?*const RuntimeRef) RuntimeError!Float {
|
||||||
return switch (env.unbox(lhs_ref)) {
|
return switch (env.unbox(lhs_ref)) {
|
||||||
.nil => env.raise(error.TypeMismatch, "cannot compare nil objects"),
|
.nil => env.raise(error.TypeMismatch, "cannot compare nil objects"),
|
||||||
|
|
|
@ -51,8 +51,7 @@ const AstCompiler = struct {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
try self.chunk.append_opcode(.{
|
try self.chunk.append_opcode(.{.push_table = @intCast(fields.values.len)});
|
||||||
.push_table = .{.field_count = @intCast(fields.values.len)}});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
.binary_operation => |operation| {
|
.binary_operation => |operation| {
|
||||||
|
@ -87,7 +86,7 @@ const AstCompiler = struct {
|
||||||
|
|
||||||
.get_local => |local| {
|
.get_local => |local| {
|
||||||
try self.chunk.append_opcode(.{
|
try self.chunk.append_opcode(.{
|
||||||
.local_push_ref = self.resolve_local(local) orelse return self.chunk.env.raise(error.OutOfMemory, "undefined local"),
|
.push_local = self.resolve_local(local) orelse return self.chunk.env.raise(error.OutOfMemory, "undefined local"),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -101,12 +100,7 @@ const AstCompiler = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
try self.chunk.append_opcode(.{.push_const = try self.chunk.declare_constant_string(call.identifier)});
|
try self.chunk.append_opcode(.{.push_const = try self.chunk.declare_constant_string(call.identifier)});
|
||||||
|
try self.chunk.append_opcode(.{.call = @intCast(call.argument_expressions.values.len)});
|
||||||
try self.chunk.append_opcode(.{
|
|
||||||
.global_call = .{
|
|
||||||
.arg_count = @intCast(call.argument_expressions.values.len),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -136,12 +130,8 @@ const AstCompiler = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
try self.chunk.append_opcode(.{.push_const = try self.chunk.declare_constant_string(call.identifier)});
|
try self.chunk.append_opcode(.{.push_const = try self.chunk.declare_constant_string(call.identifier)});
|
||||||
|
try self.chunk.append_opcode(.get_global);
|
||||||
try self.chunk.append_opcode(.{
|
try self.chunk.append_opcode(.{.call = @intCast(call.argument_expressions.values.len)});
|
||||||
.global_call = .{
|
|
||||||
.arg_count = @intCast(call.argument_expressions.values.len),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -185,17 +175,11 @@ pub const Opcode = union (enum) {
|
||||||
push_true,
|
push_true,
|
||||||
push_false,
|
push_false,
|
||||||
push_const: Constant,
|
push_const: Constant,
|
||||||
local_push_ref: u8,
|
push_local: u8,
|
||||||
|
push_table: u32,
|
||||||
set_local: u8,
|
set_local: u8,
|
||||||
get_global,
|
get_global,
|
||||||
|
call: u8,
|
||||||
push_table: struct {
|
|
||||||
field_count: u32,
|
|
||||||
},
|
|
||||||
|
|
||||||
global_call: struct {
|
|
||||||
arg_count: u8,
|
|
||||||
},
|
|
||||||
|
|
||||||
not,
|
not,
|
||||||
neg,
|
neg,
|
||||||
|
@ -242,9 +226,9 @@ pub fn declare_constant_number(self: *Self, constant: kym.Float) kym.RuntimeErro
|
||||||
return self.env.raise(error.BadSyntax, "functions may contain a maximum of 65,535 constants");
|
return self.env.raise(error.BadSyntax, "functions may contain a maximum of 65,535 constants");
|
||||||
}
|
}
|
||||||
|
|
||||||
var constant_ref = try self.env.new_number(constant);
|
const constant_ref = try self.env.new_number(constant);
|
||||||
|
|
||||||
errdefer self.env.discard(&constant_ref);
|
errdefer self.env.discard(constant_ref);
|
||||||
|
|
||||||
try self.constant_refs.push_one(constant_ref);
|
try self.constant_refs.push_one(constant_ref);
|
||||||
|
|
||||||
|
@ -258,9 +242,9 @@ pub fn declare_constant_string(self: *Self, constant: []const coral.io.Byte) kym
|
||||||
return self.env.raise(error.BadSyntax, "functions may contain a maximum of 65,535 constants");
|
return self.env.raise(error.BadSyntax, "functions may contain a maximum of 65,535 constants");
|
||||||
}
|
}
|
||||||
|
|
||||||
var constant_ref = try self.env.new_string(constant);
|
const constant_ref = try self.env.new_string(constant);
|
||||||
|
|
||||||
errdefer self.env.discard(&constant_ref);
|
errdefer self.env.discard(constant_ref);
|
||||||
|
|
||||||
try self.constant_refs.push_one(constant_ref);
|
try self.constant_refs.push_one(constant_ref);
|
||||||
|
|
||||||
|
@ -269,8 +253,9 @@ pub fn declare_constant_string(self: *Self, constant: []const coral.io.Byte) kym
|
||||||
|
|
||||||
pub fn execute(self: *Self, global_ref: ?*const kym.RuntimeRef, name: []const coral.io.Byte) kym.RuntimeError!?*kym.RuntimeRef {
|
pub fn execute(self: *Self, global_ref: ?*const kym.RuntimeRef, name: []const coral.io.Byte) kym.RuntimeError!?*kym.RuntimeRef {
|
||||||
_ = name;
|
_ = name;
|
||||||
|
_ = try self.env.frame_push(0);
|
||||||
|
|
||||||
try self.env.frame_push();
|
defer self.env.frame_pop();
|
||||||
|
|
||||||
for (self.opcodes.values) |opcode| {
|
for (self.opcodes.values) |opcode| {
|
||||||
switch (opcode) {
|
switch (opcode) {
|
||||||
|
@ -279,72 +264,84 @@ pub fn execute(self: *Self, global_ref: ?*const kym.RuntimeRef, name: []const co
|
||||||
.push_false => try self.env.local_push_boolean(false),
|
.push_false => try self.env.local_push_boolean(false),
|
||||||
.push_const => |constant| try self.env.local_push_ref(self.constant_refs.values[constant]),
|
.push_const => |constant| try self.env.local_push_ref(self.constant_refs.values[constant]),
|
||||||
|
|
||||||
.push_table => |push_table| {
|
.push_table => |field_count| {
|
||||||
var table_ref = try kym.new_table(self.env);
|
const table_ref = try kym.new_table(self.env);
|
||||||
|
|
||||||
defer self.env.discard(&table_ref);
|
defer self.env.discard(table_ref);
|
||||||
|
|
||||||
{
|
{
|
||||||
var popped = @as(usize, 0);
|
var popped = @as(usize, 0);
|
||||||
|
|
||||||
while (popped < push_table.field_count) : (popped += 1) {
|
while (popped < field_count) : (popped += 1) {
|
||||||
try self.env.set_dynamic(table_ref, try self.env.local_pop(), try self.env.local_pop());
|
try self.env.dynamic_set(table_ref, self.env.local_pop(), self.env.local_pop());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try self.env.local_push_ref(table_ref);
|
try self.env.local_push_ref(table_ref);
|
||||||
},
|
},
|
||||||
|
|
||||||
.local_push_ref => |local| {
|
.push_local => |local| {
|
||||||
var ref = try self.env.local_get(local);
|
const ref = self.env.local_get(local);
|
||||||
|
|
||||||
defer self.env.discard(&ref);
|
defer self.env.discard(ref);
|
||||||
|
|
||||||
try self.env.local_push_ref(ref);
|
try self.env.local_push_ref(ref);
|
||||||
},
|
},
|
||||||
|
|
||||||
.get_global => {
|
.get_global => {
|
||||||
var identifier_ref = try self.env.local_pop();
|
const identifier_ref = self.env.local_pop();
|
||||||
|
|
||||||
defer self.env.discard(&identifier_ref);
|
defer self.env.discard(identifier_ref);
|
||||||
|
|
||||||
var ref = try self.env.get_dynamic(global_ref, identifier_ref);
|
const ref = try self.env.dynamic_get(global_ref, identifier_ref);
|
||||||
|
|
||||||
defer self.env.discard(&ref);
|
defer self.env.discard(ref);
|
||||||
|
|
||||||
try self.env.local_push_ref(ref);
|
try self.env.local_push_ref(ref);
|
||||||
},
|
},
|
||||||
|
|
||||||
.set_local => |local| {
|
.set_local => |local| {
|
||||||
var ref = try self.env.local_pop();
|
const ref = self.env.local_pop();
|
||||||
|
|
||||||
defer self.env.discard(&ref);
|
defer self.env.discard(ref);
|
||||||
|
|
||||||
try self.env.local_set(local, ref);
|
self.env.local_set(local, ref);
|
||||||
},
|
},
|
||||||
|
|
||||||
.global_call => |_| {
|
.call => |arg_count| {
|
||||||
|
const callable_ref = self.env.local_pop();
|
||||||
|
|
||||||
|
defer self.env.discard(callable_ref);
|
||||||
|
|
||||||
|
const args = try self.env.frame_push(arg_count);
|
||||||
|
|
||||||
|
defer self.env.frame_pop();
|
||||||
|
|
||||||
|
const result_ref = try self.env.call(global_ref, callable_ref, args);
|
||||||
|
|
||||||
|
defer self.env.discard(result_ref);
|
||||||
|
|
||||||
|
try self.env.local_push_ref(result_ref);
|
||||||
},
|
},
|
||||||
|
|
||||||
.neg => try self.env.local_push_number(switch (self.env.unbox(try self.env.local_pop())) {
|
.neg => try self.env.local_push_number(switch (self.env.unbox(self.env.local_pop())) {
|
||||||
.number => |number| -number,
|
.number => |number| -number,
|
||||||
else => return self.env.raise(error.TypeMismatch, "object is not scalar negatable"),
|
else => return self.env.raise(error.TypeMismatch, "object is not scalar negatable"),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
.not => try self.env.local_push_boolean(switch (self.env.unbox(try self.env.local_pop())) {
|
.not => try self.env.local_push_boolean(switch (self.env.unbox(self.env.local_pop())) {
|
||||||
.boolean => |boolean| !boolean,
|
.boolean => |boolean| !boolean,
|
||||||
else => return self.env.raise(error.TypeMismatch, "object is not boolean negatable"),
|
else => return self.env.raise(error.TypeMismatch, "object is not boolean negatable"),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
.add => {
|
.add => {
|
||||||
var rhs_ref = try self.env.local_pop();
|
const rhs_ref = self.env.local_pop();
|
||||||
|
|
||||||
defer self.env.discard(&rhs_ref);
|
defer self.env.discard(rhs_ref);
|
||||||
|
|
||||||
var lhs_ref = try self.env.local_pop();
|
const lhs_ref = self.env.local_pop();
|
||||||
|
|
||||||
defer self.env.discard(&lhs_ref);
|
defer self.env.discard(lhs_ref);
|
||||||
|
|
||||||
try self.env.local_push_ref(switch (self.env.unbox(lhs_ref)) {
|
try self.env.local_push_ref(switch (self.env.unbox(lhs_ref)) {
|
||||||
.number => |lhs_number| switch (self.env.unbox(rhs_ref)) {
|
.number => |lhs_number| switch (self.env.unbox(rhs_ref)) {
|
||||||
|
@ -357,13 +354,13 @@ pub fn execute(self: *Self, global_ref: ?*const kym.RuntimeRef, name: []const co
|
||||||
},
|
},
|
||||||
|
|
||||||
.sub => {
|
.sub => {
|
||||||
var rhs_ref = try self.env.local_pop();
|
const rhs_ref = self.env.local_pop();
|
||||||
|
|
||||||
defer self.env.discard(&rhs_ref);
|
defer self.env.discard(rhs_ref);
|
||||||
|
|
||||||
var lhs_ref = try self.env.local_pop();
|
const lhs_ref = self.env.local_pop();
|
||||||
|
|
||||||
defer self.env.discard(&lhs_ref);
|
defer self.env.discard(lhs_ref);
|
||||||
|
|
||||||
try self.env.local_push_ref(switch (self.env.unbox(lhs_ref)) {
|
try self.env.local_push_ref(switch (self.env.unbox(lhs_ref)) {
|
||||||
.number => |lhs_number| switch (self.env.unbox(rhs_ref)) {
|
.number => |lhs_number| switch (self.env.unbox(rhs_ref)) {
|
||||||
|
@ -376,13 +373,13 @@ pub fn execute(self: *Self, global_ref: ?*const kym.RuntimeRef, name: []const co
|
||||||
},
|
},
|
||||||
|
|
||||||
.mul => {
|
.mul => {
|
||||||
var rhs_ref = try self.env.local_pop();
|
const rhs_ref = self.env.local_pop();
|
||||||
|
|
||||||
defer self.env.discard(&rhs_ref);
|
defer self.env.discard(rhs_ref);
|
||||||
|
|
||||||
var lhs_ref = try self.env.local_pop();
|
const lhs_ref = self.env.local_pop();
|
||||||
|
|
||||||
defer self.env.discard(&lhs_ref);
|
defer self.env.discard(lhs_ref);
|
||||||
|
|
||||||
try self.env.local_push_ref(switch (self.env.unbox(lhs_ref)) {
|
try self.env.local_push_ref(switch (self.env.unbox(lhs_ref)) {
|
||||||
.number => |lhs_number| switch (self.env.unbox(rhs_ref)) {
|
.number => |lhs_number| switch (self.env.unbox(rhs_ref)) {
|
||||||
|
@ -395,13 +392,13 @@ pub fn execute(self: *Self, global_ref: ?*const kym.RuntimeRef, name: []const co
|
||||||
},
|
},
|
||||||
|
|
||||||
.div => {
|
.div => {
|
||||||
var rhs_ref = try self.env.local_pop();
|
const rhs_ref = self.env.local_pop();
|
||||||
|
|
||||||
defer self.env.discard(&rhs_ref);
|
defer self.env.discard(rhs_ref);
|
||||||
|
|
||||||
var lhs_ref = try self.env.local_pop();
|
const lhs_ref = self.env.local_pop();
|
||||||
|
|
||||||
defer self.env.discard(&lhs_ref);
|
defer self.env.discard(lhs_ref);
|
||||||
|
|
||||||
try self.env.local_push_ref(switch (self.env.unbox(lhs_ref)) {
|
try self.env.local_push_ref(switch (self.env.unbox(lhs_ref)) {
|
||||||
.number => |lhs_number| switch (self.env.unbox(rhs_ref)) {
|
.number => |lhs_number| switch (self.env.unbox(rhs_ref)) {
|
||||||
|
@ -414,76 +411,72 @@ pub fn execute(self: *Self, global_ref: ?*const kym.RuntimeRef, name: []const co
|
||||||
},
|
},
|
||||||
|
|
||||||
.eql => {
|
.eql => {
|
||||||
var rhs_ref = try self.env.local_pop();
|
const rhs_ref = self.env.local_pop();
|
||||||
|
|
||||||
defer self.env.discard(&rhs_ref);
|
defer self.env.discard(rhs_ref);
|
||||||
|
|
||||||
var lhs_ref = try self.env.local_pop();
|
const lhs_ref = self.env.local_pop();
|
||||||
|
|
||||||
defer self.env.discard(&lhs_ref);
|
defer self.env.discard(lhs_ref);
|
||||||
|
|
||||||
try self.env.local_push_boolean(try kym.test_equality(self.env, lhs_ref, rhs_ref));
|
try self.env.local_push_boolean(try kym.test_equality(self.env, lhs_ref, rhs_ref));
|
||||||
},
|
},
|
||||||
|
|
||||||
.cgt => {
|
.cgt => {
|
||||||
var rhs_ref = try self.env.local_pop();
|
const rhs_ref = self.env.local_pop();
|
||||||
|
|
||||||
defer self.env.discard(&rhs_ref);
|
defer self.env.discard(rhs_ref);
|
||||||
|
|
||||||
var lhs_ref = try self.env.local_pop();
|
const lhs_ref = self.env.local_pop();
|
||||||
|
|
||||||
defer self.env.discard(&lhs_ref);
|
defer self.env.discard(lhs_ref);
|
||||||
|
|
||||||
try self.env.local_push_boolean(try kym.test_difference(self.env, lhs_ref, rhs_ref) > 0);
|
try self.env.local_push_boolean(try kym.test_difference(self.env, lhs_ref, rhs_ref) > 0);
|
||||||
},
|
},
|
||||||
|
|
||||||
.clt => {
|
.clt => {
|
||||||
var rhs_ref = try self.env.local_pop();
|
const rhs_ref = self.env.local_pop();
|
||||||
|
|
||||||
defer self.env.discard(&rhs_ref);
|
defer self.env.discard(rhs_ref);
|
||||||
|
|
||||||
var lhs_ref = try self.env.local_pop();
|
const lhs_ref = self.env.local_pop();
|
||||||
|
|
||||||
defer self.env.discard(&lhs_ref);
|
defer self.env.discard(lhs_ref);
|
||||||
|
|
||||||
try self.env.local_push_boolean(try kym.test_difference(self.env, lhs_ref, rhs_ref) < 0);
|
try self.env.local_push_boolean(try kym.test_difference(self.env, lhs_ref, rhs_ref) < 0);
|
||||||
},
|
},
|
||||||
|
|
||||||
.cge => {
|
.cge => {
|
||||||
var rhs_ref = try self.env.local_pop();
|
const rhs_ref = self.env.local_pop();
|
||||||
|
|
||||||
defer self.env.discard(&rhs_ref);
|
defer self.env.discard(rhs_ref);
|
||||||
|
|
||||||
var lhs_ref = try self.env.local_pop();
|
const lhs_ref = self.env.local_pop();
|
||||||
|
|
||||||
defer self.env.discard(&lhs_ref);
|
defer self.env.discard(lhs_ref);
|
||||||
|
|
||||||
try self.env.local_push_boolean(try kym.test_difference(self.env, lhs_ref, rhs_ref) >= 0);
|
try self.env.local_push_boolean(try kym.test_difference(self.env, lhs_ref, rhs_ref) >= 0);
|
||||||
},
|
},
|
||||||
|
|
||||||
.cle => {
|
.cle => {
|
||||||
var rhs_ref = try self.env.local_pop();
|
const rhs_ref = self.env.local_pop();
|
||||||
|
|
||||||
defer self.env.discard(&rhs_ref);
|
defer self.env.discard(rhs_ref);
|
||||||
|
|
||||||
var lhs_ref = try self.env.local_pop();
|
const lhs_ref = self.env.local_pop();
|
||||||
|
|
||||||
defer self.env.discard(&lhs_ref);
|
defer self.env.discard(lhs_ref);
|
||||||
|
|
||||||
try self.env.local_push_boolean(try kym.test_difference(self.env, lhs_ref, rhs_ref) <= 0);
|
try self.env.local_push_boolean(try kym.test_difference(self.env, lhs_ref, rhs_ref) <= 0);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = try self.env.local_pop();
|
return self.env.local_pop();
|
||||||
|
|
||||||
try self.env.frame_pop();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn free(self: *Self) void {
|
pub fn free(self: *Self) void {
|
||||||
for (self.constant_refs.values) |*constant| {
|
for (self.constant_refs.values) |constant| {
|
||||||
self.env.discard(constant);
|
self.env.discard(constant);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,16 +22,11 @@ pub fn make(env: *kym.RuntimeEnv) Self {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const typeinfo = kym.Typeinfo{
|
pub const typeinfo = kym.Typeinfo{
|
||||||
.call = typeinfo_call,
|
|
||||||
.clean = typeinfo_clean,
|
.clean = typeinfo_clean,
|
||||||
.get = typeinfo_get,
|
.get = typeinfo_get,
|
||||||
.set = typeinfo_set,
|
.set = typeinfo_set,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn typeinfo_call(context: kym.CallContext) kym.RuntimeError!?*kym.RuntimeRef {
|
|
||||||
return context.env.raise(error.TypeMismatch, "cannot call a table");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn typeinfo_clean(userdata: []u8) void {
|
fn typeinfo_clean(userdata: []u8) void {
|
||||||
@as(*Self, @ptrCast(@alignCast(userdata.ptr))).free();
|
@as(*Self, @ptrCast(@alignCast(userdata.ptr))).free();
|
||||||
}
|
}
|
||||||
|
@ -49,15 +44,15 @@ fn typeinfo_get(context: kym.IndexContext) kym.RuntimeError!?*kym.RuntimeRef {
|
||||||
|
|
||||||
fn typeinfo_set(context: kym.IndexContext, value_ref: ?*const kym.RuntimeRef) kym.RuntimeError!void {
|
fn typeinfo_set(context: kym.IndexContext, value_ref: ?*const kym.RuntimeRef) kym.RuntimeError!void {
|
||||||
const table = @as(*Self, @ptrCast(@alignCast(context.userdata.ptr)));
|
const table = @as(*Self, @ptrCast(@alignCast(context.userdata.ptr)));
|
||||||
var acquired_value_ref = context.env.acquire(value_ref);
|
const acquired_value_ref = context.env.acquire(value_ref);
|
||||||
|
|
||||||
errdefer context.env.discard(&acquired_value_ref);
|
errdefer context.env.discard(acquired_value_ref);
|
||||||
|
|
||||||
switch (context.env.unbox(context.index_ref)) {
|
switch (context.env.unbox(context.index_ref)) {
|
||||||
.string => |string| {
|
.string => |string| {
|
||||||
var acquired_index_ref = context.env.acquire(context.index_ref);
|
const acquired_index_ref = context.env.acquire(context.index_ref);
|
||||||
|
|
||||||
errdefer context.env.discard(&acquired_index_ref);
|
errdefer context.env.discard(acquired_index_ref);
|
||||||
|
|
||||||
var displaced_table_entry = if (acquired_value_ref) |ref| try table.fields.replace(string, .{
|
var displaced_table_entry = if (acquired_value_ref) |ref| try table.fields.replace(string, .{
|
||||||
.key_ref = acquired_index_ref,
|
.key_ref = acquired_index_ref,
|
||||||
|
@ -65,8 +60,8 @@ fn typeinfo_set(context: kym.IndexContext, value_ref: ?*const kym.RuntimeRef) ky
|
||||||
}) else table.fields.remove(string);
|
}) else table.fields.remove(string);
|
||||||
|
|
||||||
if (displaced_table_entry) |*entry| {
|
if (displaced_table_entry) |*entry| {
|
||||||
context.env.discard(&entry.value.key_ref);
|
context.env.discard(entry.value.key_ref);
|
||||||
context.env.discard(&entry.value.value_ref);
|
context.env.discard(entry.value.value_ref);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue