153 lines
3.4 KiB
Zig
153 lines
3.4 KiB
Zig
const coral = @import("coral");
|
|
|
|
allocator: coral.io.Allocator,
|
|
interned: SymbolTable,
|
|
globals: Object,
|
|
values: DataStack,
|
|
frames: CallStack,
|
|
|
|
pub const Float = f64;
|
|
|
|
const CallStack = coral.list.Stack(struct {
|
|
callable: *Object,
|
|
opcode_index: usize,
|
|
stack_index: usize,
|
|
});
|
|
|
|
const DataStack = coral.list.Stack(Variant);
|
|
|
|
pub const Object = struct {
|
|
ref_count: usize,
|
|
userdata: []coral.io.Byte,
|
|
userinfo: *const anyopaque,
|
|
};
|
|
|
|
pub const PopError = error {
|
|
StackOverflow,
|
|
};
|
|
|
|
const Self = @This();
|
|
|
|
const SymbolTable = coral.map.Table([]const coral.io.Byte, *Object, coral.map.string_table_traits);
|
|
|
|
pub const Variant = union (enum) {
|
|
nil,
|
|
true,
|
|
false,
|
|
number: Float,
|
|
object: *Object,
|
|
|
|
pub fn equals(self: Variant, other: Variant) bool {
|
|
return switch (self) {
|
|
.nil => other == .nil,
|
|
.true => other == .true,
|
|
.false => other == .false,
|
|
|
|
.number => |number| switch (other) {
|
|
.number => |other_number| number == other_number,
|
|
else => false,
|
|
},
|
|
|
|
.object => |object| switch (other) {
|
|
.object => |other_object| object == other_object,
|
|
else => false,
|
|
},
|
|
};
|
|
}
|
|
};
|
|
|
|
pub fn acquire_instance(_: *Self, object: *Object) *Object {
|
|
// TODO: safety-check object belongs to state.
|
|
object.ref_count += 1;
|
|
|
|
return object;
|
|
}
|
|
|
|
pub fn acquire_interned(self: *Self, userdata: []const u8, userinfo: *const anyopaque) coral.io.AllocationError!*Object {
|
|
// TODO: Include userinfo in matching lookup.
|
|
if (self.interned.lookup(userdata)) |object| {
|
|
return self.acquire_instance(object);
|
|
} else {
|
|
const data_object = try self.acquire_new(userdata, userinfo);
|
|
|
|
errdefer self.release(data_object);
|
|
|
|
coral.debug.assert(try self.interned.insert(data_object.userdata, data_object));
|
|
|
|
return data_object;
|
|
}
|
|
}
|
|
|
|
pub fn acquire_new(self: *Self, userdata: []const u8, userinfo: *const anyopaque) coral.io.AllocationError!*Object {
|
|
const allocated_userdata = try coral.io.allocate_copy(self.allocator, userdata);
|
|
|
|
errdefer self.allocator.deallocate(allocated_userdata);
|
|
|
|
const allocated_object = try coral.io.allocate_one(self.allocator, Object{
|
|
.ref_count = 1,
|
|
.userdata = allocated_userdata,
|
|
.userinfo = userinfo,
|
|
});
|
|
|
|
errdefer self.allocator.deallocate(allocated_object);
|
|
|
|
return allocated_object;
|
|
}
|
|
|
|
pub fn free(self: *Self) void {
|
|
self.values.free();
|
|
self.frames.free();
|
|
self.interned.free();
|
|
}
|
|
|
|
pub fn get_value(self: *Self, tail_index: usize) Variant {
|
|
if (tail_index >= self.values.values.len) {
|
|
return .nil;
|
|
}
|
|
|
|
return self.values.values[self.values.values.len - (1 + tail_index)];
|
|
}
|
|
|
|
pub fn make(allocator: coral.io.Allocator) Self {
|
|
return .{
|
|
.values = DataStack.make(allocator),
|
|
.frames = CallStack.make(allocator),
|
|
.interned = SymbolTable.make(allocator),
|
|
.allocator = allocator,
|
|
|
|
.globals = .{
|
|
.ref_count = 0,
|
|
.userdata = &.{},
|
|
.userinfo = &.{},
|
|
},
|
|
};
|
|
}
|
|
|
|
pub fn pop_value(self: *Self) PopError!Variant {
|
|
return self.values.pop() orelse error.StackOverflow;
|
|
}
|
|
|
|
pub fn push_value(self: *Self, value: Variant) coral.io.AllocationError!void {
|
|
return self.values.push_one(value);
|
|
}
|
|
|
|
pub fn release(self: *Self, object: *Object) void {
|
|
coral.debug.assert(object.ref_count != 0);
|
|
|
|
object.ref_count -= 1;
|
|
|
|
if (object.ref_count == 0) {
|
|
self.allocator.deallocate(object);
|
|
}
|
|
}
|
|
|
|
pub fn set_value(self: *Self, tail_index: usize, value: Variant) bool {
|
|
if (tail_index >= self.values.values.len) {
|
|
return false;
|
|
}
|
|
|
|
self.values.values[self.values.values.len - (1 + tail_index)] = value;
|
|
|
|
return true;
|
|
}
|