implementing instance initializers

This commit is contained in:
Patrick MARIE 2024-08-31 11:32:05 +02:00
parent b522f05c8c
commit b839d67cea
10 changed files with 95 additions and 17 deletions

0
empty.lox Normal file
View File

View File

@ -0,0 +1,14 @@
class CoffeeMaker {
init(coffee) {
this.coffee = coffee;
}
brew() {
print "Enjoy your cup of " + this.coffee;
// No reusing the grounds!
this.coffee = nil;
}
}
var maker = CoffeeMaker("coffee and chicory");
print maker;
maker.brew();

View File

@ -0,0 +1,12 @@
class Brunch {
bacon() {
print "calling bacon()";
}
eggs() {
print "calling eggs()";
}
}
var brunch = Brunch();
print brunch;
brunch.bacon();

View File

@ -1,8 +1,4 @@
class Brunch {
bacon() {}
eggs() {}
init(food, drink) {}
}
var brunch = Brunch();
print brunch;
brunch.bacon();
Brunch("eggs", "coffee");

View File

@ -0,0 +1,17 @@
class Brunch {
var food;
var drink;
init(food, drink) {
this.food = food;
this.drink = drink;
}
fun get_recipe() {
return this.food + " " + this.drink;
}
}
var instance = Brunch("eggs", "coffee");
print instance;
print instance.get_recipe();

View File

@ -144,7 +144,12 @@ pub const Parser = struct {
}
fn emit_return(self: *Parser) ParsingError!void {
try self.emit_byte(@intFromEnum(OpCode.OP_NIL));
if (self.compiler.function_type == FunctionType.Initializer) {
try self.emit_bytes(@intFromEnum(OpCode.OP_GET_LOCAL), 0);
} else {
try self.emit_byte(@intFromEnum(OpCode.OP_NIL));
}
try self.emit_byte(@intFromEnum(OpCode.OP_RETURN));
}
@ -865,6 +870,9 @@ pub const Parser = struct {
if (self.match(TokenType.SEMICOLON)) {
try self.emit_return();
} else {
if (self.compiler.function_type == FunctionType.Initializer) {
self.error_msg("Can't return a value from an initialiaer");
}
try self.expression();
self.consume(TokenType.SEMICOLON, "Expect ';' after return value.");
try self.emit_byte(@intFromEnum(OpCode.OP_RETURN));
@ -914,7 +922,12 @@ pub const Parser = struct {
self.consume(TokenType.IDENTIFIER, "Expect method name.");
const constant = try self.identifier_constant(self.previous.?);
try self.function(FunctionType.Method);
var function_type: FunctionType = FunctionType.Method;
// std.debug.print("len: {d} {s}\n", .{self.previous.?.length, });
if (self.previous.?.length == 4 and std.mem.eql(u8, self.previous.?.start[0..4], "init")) {
function_type = FunctionType.Initializer;
}
try self.function(function_type);
try self.emit_bytes(@intFromEnum(OpCode.OP_METHOD), constant);
}
@ -933,6 +946,7 @@ const FunctionType = enum {
Function,
Script,
Method,
Initializer,
};
pub const Compiler = struct {

View File

@ -72,7 +72,6 @@ pub fn main() !void {
} else {
vm.init_vm(allocator);
}
defer vm.destroy();
if (args.len == 1) {

View File

@ -25,7 +25,7 @@ pub const ZloxAllocator = struct {
.parent_allocator = parent_allocator,
.vm = vm,
.bytes_allocated = 0,
.next_gc = 1024,
.next_gc = 4096,
.current_gc = false,
};
}
@ -145,6 +145,8 @@ pub const ZloxAllocator = struct {
self.mark_table(&self.vm.globals);
self.mark_compiler_roots();
self.mark_object(&self.vm.init_string.?.obj);
}
pub fn mark_value(self: *Self, value: *Value) void {
@ -264,6 +266,9 @@ pub const ZloxAllocator = struct {
for (0..table.capacity) |idx| {
const entry: *Entry = &table.entries[idx];
if (entry.key != null and !entry.key.?.obj.is_marked) {
if (comptime constants.DEBUG_LOG_GC) {
std.debug.print("GC: table_remove_white: deleting {s}\n", .{entry.key.?.chars});
}
_ = table.del(entry.key.?);
}
}
@ -288,7 +293,7 @@ pub const ZloxAllocator = struct {
self.vm.objects = object;
}
if (comptime constants.DEBUG_LOG_GC == true) {
if (comptime constants.DEBUG_LOG_GC) {
std.debug.print("GC: sweeping {*}: ", .{unreached});
unreached.print();
std.debug.print("\n", .{});

View File

@ -65,7 +65,6 @@ pub const Table = struct {
while (true) {
const entry = &entries[index];
if (entry.key == null) {
if (entry.value.is_nil()) {
// Empty entry.
@ -121,7 +120,7 @@ pub const Table = struct {
std.debug.print("== Hash table count:{} capacity:{} ==\n", .{ self.count, self.capacity });
for (self.entries, 0..) |entry, idx| {
if (entry.key != null) {
std.debug.print("{d} ({d}) - {s}: ", .{ idx, entry.key.?.hash, entry.key.?.chars });
std.debug.print("{d} {*} (size: {d} hash:{d}) - {s}: ", .{ idx, entry.key, entry.key.?.chars.len, entry.key.?.hash, entry.key.?.chars });
entry.value.type_print();
std.debug.print("\n", .{});
}

View File

@ -49,6 +49,7 @@ pub const VM = struct {
gray_count: usize,
gray_capacity: usize,
gray_stack: ?[]*Obj,
init_string: ?*Obj.String,
pub fn new() VM {
const vm = VM{
@ -65,6 +66,7 @@ pub const VM = struct {
.gray_capacity = 0,
.gray_count = 0,
.gray_stack = &.{},
.init_string = null,
};
return vm;
@ -75,6 +77,9 @@ pub const VM = struct {
self.globals = Table.new(self.allocator);
self.strings = Table.new(self.allocator);
_ = try self.push(Value.obj_val(&self.copy_string("init").obj));
self.init_string = self.pop().as_string();
self.define_native("clock", natives.clock);
self.define_native("power", natives.power);
self.define_native("str2num", natives.str2num);
@ -88,6 +93,7 @@ pub const VM = struct {
self.strings.destroy();
self.globals.destroy();
self.init_string = null;
self.destroy_objects();
if (self.gray_stack != null) {
@ -108,7 +114,9 @@ pub const VM = struct {
var obj = self.objects;
while (obj != null) {
const obj_next = obj.?.next;
std.debug.print("OBJ: {*}\n", .{obj.?});
std.debug.print("OBJ: {*} {any}", .{ obj.?, obj.?.kind });
// obj.?.print();
std.debug.print("\n", .{});
obj = obj_next;
}
}
@ -500,6 +508,14 @@ pub const VM = struct {
const class = callee.as_obj().as_class();
self.stack[self.stack_top - arg_count - 1] = Value.obj_val(&Obj.Instance.new(self, class).obj);
var initializer = Value.nil_val();
if (class.methods.get(self.init_string.?, &initializer) == true) {
return self.call(initializer.as_obj().as_closure(), arg_count);
} else if (arg_count != 0) {
self.runtime_error("Expected 0 arguments."); // XXX show number of arguments.
}
return true;
},
ObjType.BoundMethod => {
@ -518,7 +534,10 @@ pub const VM = struct {
var method: Value = Value.nil_val();
if (!class.methods.get(name, &method)) {
self.runtime_error("Undefined property."); // XXX add name.chars as '%s'.
const err_msg = std.fmt.allocPrint(self.allocator, "Undefined property '{s}'.", .{name.chars}) catch unreachable;
defer self.allocator.free(err_msg);
self.runtime_error(err_msg);
return false;
}
@ -531,8 +550,11 @@ pub const VM = struct {
pub fn call(self: *VM, closure: *Obj.Closure, arg_count: usize) bool {
if (arg_count != closure.function.arity) {
self.runtime_error("Invalid argument count.");
// runtimeError("Expected %d arguments but got %d.", function->arity, argCount);
const err_msg = std.fmt.allocPrint(self.allocator, "Expected {d} arguments but got {d}.", .{ closure.function.arity, arg_count }) catch unreachable;
defer self.allocator.free(err_msg);
self.runtime_error(err_msg);
return false;
}