342 lines
9.2 KiB
Zig
342 lines
9.2 KiB
Zig
const std = @import("std");
|
|
const debug = std.debug;
|
|
const Allocator = std.mem.Allocator;
|
|
|
|
const Chunk = @import("./chunk.zig").Chunk;
|
|
const Table = @import("./table.zig").Table;
|
|
const Value = @import("./values.zig").Value;
|
|
const VM = @import("./vm.zig").VM;
|
|
|
|
const compute_hash = @import("./utils.zig").compute_hash;
|
|
|
|
pub const ObjType = enum {
|
|
String,
|
|
Function,
|
|
Native,
|
|
Closure,
|
|
Upvalue,
|
|
Class,
|
|
Instance,
|
|
BoundMethod,
|
|
};
|
|
|
|
pub const NativeFn = *const fn (vm: *VM, arg_count: usize, args: []Value) Value;
|
|
|
|
pub const Obj = struct {
|
|
kind: ObjType,
|
|
allocator: Allocator,
|
|
next: ?*Obj,
|
|
is_marked: bool,
|
|
|
|
fn new(comptime T: type, vm: *VM, kind: ObjType) *T {
|
|
const created_obj = vm.allocator.create(T) catch unreachable;
|
|
|
|
created_obj.obj = Obj{
|
|
.kind = kind,
|
|
.allocator = vm.allocator,
|
|
.next = vm.objects,
|
|
.is_marked = false,
|
|
};
|
|
|
|
vm.objects = &created_obj.obj;
|
|
|
|
return created_obj;
|
|
}
|
|
|
|
pub fn destroy(self: *Obj) void {
|
|
switch (self.kind) {
|
|
ObjType.String => self.as_string().destroy(),
|
|
ObjType.Function => self.as_function().destroy(),
|
|
ObjType.Native => self.as_native().destroy(),
|
|
ObjType.Closure => self.as_closure().destroy(),
|
|
ObjType.Upvalue => self.as_upvalue().destroy(),
|
|
ObjType.Class => self.as_class().destroy(),
|
|
ObjType.Instance => self.as_instance().destroy(),
|
|
ObjType.BoundMethod => self.as_bound_method().destroy(),
|
|
}
|
|
}
|
|
|
|
pub const String = struct {
|
|
obj: Obj,
|
|
chars: []const u8,
|
|
hash: u32,
|
|
|
|
pub fn new(vm: *VM, chars: []const u8) *String {
|
|
const str_obj = Obj.new(String, vm, ObjType.String);
|
|
|
|
str_obj.chars = chars;
|
|
str_obj.hash = compute_hash(str_obj.chars);
|
|
|
|
return str_obj;
|
|
}
|
|
|
|
pub fn destroy(self: *String) void {
|
|
self.obj.allocator.free(self.chars);
|
|
self.obj.allocator.destroy(self);
|
|
}
|
|
};
|
|
|
|
pub const Function = struct {
|
|
obj: Obj,
|
|
arity: usize,
|
|
upvalue_count: usize,
|
|
chunk: *Chunk,
|
|
name: ?*Obj.String,
|
|
|
|
pub fn new(vm: *VM) *Function {
|
|
const function_obj = Obj.new(Function, vm, ObjType.Function);
|
|
|
|
function_obj.arity = 0;
|
|
function_obj.upvalue_count = 0;
|
|
function_obj.chunk = Chunk.new(vm.allocator, vm);
|
|
function_obj.name = null;
|
|
|
|
return function_obj;
|
|
}
|
|
|
|
pub fn destroy(self: *Function) void {
|
|
self.chunk.destroy();
|
|
self.obj.allocator.destroy(self);
|
|
}
|
|
};
|
|
|
|
pub const Native = struct {
|
|
obj: Obj,
|
|
native: NativeFn,
|
|
|
|
pub fn new(vm: *VM, native: NativeFn) *Native {
|
|
const native_obj = Obj.new(Native, vm, ObjType.Native);
|
|
|
|
native_obj.native = native;
|
|
|
|
return native_obj;
|
|
}
|
|
|
|
pub fn destroy(self: *Native) void {
|
|
self.obj.allocator.destroy(self);
|
|
}
|
|
};
|
|
|
|
pub const Closure = struct {
|
|
obj: Obj,
|
|
function: *Obj.Function,
|
|
upvalues: []?*Obj.Upvalue,
|
|
upvalue_count: usize,
|
|
|
|
pub fn new(vm: *VM, function: *Obj.Function) *Closure {
|
|
const closure_obj = Obj.new(Closure, vm, ObjType.Closure);
|
|
|
|
closure_obj.function = function;
|
|
closure_obj.upvalue_count = function.upvalue_count;
|
|
|
|
closure_obj.upvalues = vm.allocator.alloc(?*Obj.Upvalue, function.upvalue_count) catch unreachable;
|
|
|
|
for (0..function.upvalue_count) |i| {
|
|
closure_obj.upvalues[i] = null;
|
|
}
|
|
|
|
return closure_obj;
|
|
}
|
|
|
|
pub fn destroy(self: *Closure) void {
|
|
self.obj.allocator.free(self.upvalues);
|
|
self.obj.allocator.destroy(self);
|
|
}
|
|
};
|
|
|
|
pub const Upvalue = struct {
|
|
obj: Obj,
|
|
location: *Value,
|
|
next: ?*Obj.Upvalue,
|
|
closed: Value,
|
|
|
|
pub fn new(vm: *VM, slot: *Value) *Upvalue {
|
|
const upvalue_obj = Obj.new(Upvalue, vm, ObjType.Upvalue);
|
|
|
|
upvalue_obj.location = slot;
|
|
upvalue_obj.next = null;
|
|
upvalue_obj.closed = Value.nil_val();
|
|
|
|
return upvalue_obj;
|
|
}
|
|
|
|
pub fn destroy(self: *Upvalue) void {
|
|
self.obj.allocator.destroy(self);
|
|
}
|
|
};
|
|
|
|
pub const Class = struct {
|
|
obj: Obj,
|
|
name: *Obj.String,
|
|
methods: Table,
|
|
|
|
pub fn new(vm: *VM, name: *Obj.String) *Class {
|
|
const class_obj = Obj.new(Class, vm, ObjType.Class);
|
|
|
|
class_obj.name = name;
|
|
class_obj.methods = Table.new(vm.allocator);
|
|
|
|
return class_obj;
|
|
}
|
|
|
|
pub fn destroy(self: *Class) void {
|
|
self.methods.destroy();
|
|
self.obj.allocator.destroy(self);
|
|
}
|
|
};
|
|
|
|
pub const Instance = struct {
|
|
obj: Obj,
|
|
class: *Obj.Class,
|
|
fields: Table,
|
|
|
|
pub fn new(vm: *VM, class: *Obj.Class) *Instance {
|
|
const instance_obj = Obj.new(Instance, vm, ObjType.Instance);
|
|
|
|
instance_obj.class = class;
|
|
instance_obj.fields = Table.new(vm.allocator);
|
|
|
|
return instance_obj;
|
|
}
|
|
|
|
pub fn destroy(self: *Instance) void {
|
|
self.fields.destroy();
|
|
self.obj.allocator.destroy(self);
|
|
}
|
|
};
|
|
|
|
pub const BoundMethod = struct {
|
|
obj: Obj,
|
|
receiver: Value,
|
|
method: *Obj.Closure,
|
|
|
|
pub fn new(vm: *VM, receiver: Value, method: *Obj.Closure) *BoundMethod {
|
|
const bound_method = Obj.new(BoundMethod, vm, ObjType.BoundMethod);
|
|
|
|
bound_method.receiver = receiver;
|
|
bound_method.method = method;
|
|
|
|
return bound_method;
|
|
}
|
|
|
|
pub fn destroy(self: *BoundMethod) void {
|
|
self.obj.allocator.destroy(self);
|
|
}
|
|
};
|
|
|
|
pub fn is_type(self: *Obj, kind: ObjType) bool {
|
|
return self.kind == kind;
|
|
}
|
|
|
|
pub fn is_string(self: *Obj) bool {
|
|
return self.is_type(ObjType.String);
|
|
}
|
|
|
|
pub fn is_function(self: *Obj) bool {
|
|
return self.is_type(ObjType.Function);
|
|
}
|
|
|
|
pub fn is_native(self: *Obj) bool {
|
|
return self.is_type(ObjType.Native);
|
|
}
|
|
|
|
pub fn is_closure(self: *Obj) bool {
|
|
return self.is_type(ObjType.Closure);
|
|
}
|
|
|
|
pub fn is_upvalue(self: *Obj) bool {
|
|
return self.is_type(ObjType.Upvalue);
|
|
}
|
|
|
|
pub fn is_class(self: *Obj) bool {
|
|
return self.is_type(ObjType.Class);
|
|
}
|
|
|
|
pub fn is_instance(self: *Obj) bool {
|
|
return self.is_type(ObjType.Instance);
|
|
}
|
|
|
|
pub fn is_bound_method(self: *Obj) bool {
|
|
return self.is_type(ObjType.BoundMethod);
|
|
}
|
|
|
|
pub fn print(self: *Obj) void {
|
|
switch (self.kind) {
|
|
ObjType.String => {
|
|
const obj = self.as_string();
|
|
debug.print("{s}", .{obj.chars});
|
|
},
|
|
ObjType.Function => {
|
|
const obj = self.as_function();
|
|
if (obj.name == null) {
|
|
debug.print("<script>", .{});
|
|
} else {
|
|
debug.print("<fn {s}>", .{obj.name.?.chars});
|
|
}
|
|
},
|
|
ObjType.Native => {
|
|
debug.print("<native fn>", .{});
|
|
},
|
|
ObjType.Closure => {
|
|
const obj = self.as_closure();
|
|
obj.function.obj.print();
|
|
},
|
|
ObjType.Upvalue => {
|
|
debug.print("upvalue", .{});
|
|
},
|
|
ObjType.Class => {
|
|
const obj = self.as_class();
|
|
debug.print("{s}", .{obj.name.chars});
|
|
},
|
|
ObjType.Instance => {
|
|
const obj = self.as_instance();
|
|
debug.print("{s} instance", .{obj.class.name.chars});
|
|
},
|
|
ObjType.BoundMethod => {
|
|
const obj = self.as_bound_method();
|
|
obj.method.function.obj.print();
|
|
},
|
|
}
|
|
}
|
|
|
|
pub fn as_string(self: *Obj) *String {
|
|
std.debug.assert(self.kind == ObjType.String);
|
|
return @fieldParentPtr("obj", self);
|
|
}
|
|
|
|
pub fn as_function(self: *Obj) *Function {
|
|
std.debug.assert(self.kind == ObjType.Function);
|
|
return @fieldParentPtr("obj", self);
|
|
}
|
|
|
|
pub fn as_native(self: *Obj) *Native {
|
|
std.debug.assert(self.kind == ObjType.Native);
|
|
return @fieldParentPtr("obj", self);
|
|
}
|
|
|
|
pub fn as_closure(self: *Obj) *Closure {
|
|
std.debug.assert(self.kind == ObjType.Closure);
|
|
return @fieldParentPtr("obj", self);
|
|
}
|
|
|
|
pub fn as_upvalue(self: *Obj) *Upvalue {
|
|
std.debug.assert(self.kind == ObjType.Upvalue);
|
|
return @fieldParentPtr("obj", self);
|
|
}
|
|
|
|
pub fn as_class(self: *Obj) *Class {
|
|
std.debug.assert(self.kind == ObjType.Class);
|
|
return @fieldParentPtr("obj", self);
|
|
}
|
|
|
|
pub fn as_instance(self: *Obj) *Instance {
|
|
std.debug.assert(self.kind == ObjType.Instance);
|
|
return @fieldParentPtr("obj", self);
|
|
}
|
|
|
|
pub fn as_bound_method(self: *Obj) *BoundMethod {
|
|
std.debug.assert(self.kind == ObjType.BoundMethod);
|
|
return @fieldParentPtr("obj", self);
|
|
}
|
|
};
|