implementing instance initializers
This commit is contained in:
parent
b522f05c8c
commit
b839d67cea
14
samples/ch28_initializers.lox
Normal file
14
samples/ch28_initializers.lox
Normal 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();
|
12
samples/ch28_methods_again.lox
Normal file
12
samples/ch28_methods_again.lox
Normal file
@ -0,0 +1,12 @@
|
||||
class Brunch {
|
||||
bacon() {
|
||||
print "calling bacon()";
|
||||
}
|
||||
eggs() {
|
||||
print "calling eggs()";
|
||||
}
|
||||
}
|
||||
|
||||
var brunch = Brunch();
|
||||
print brunch;
|
||||
brunch.bacon();
|
@ -1,8 +1,4 @@
|
||||
class Brunch {
|
||||
bacon() {}
|
||||
eggs() {}
|
||||
init(food, drink) {}
|
||||
}
|
||||
|
||||
var brunch = Brunch();
|
||||
print brunch;
|
||||
brunch.bacon();
|
||||
Brunch("eggs", "coffee");
|
||||
|
17
samples/ch8_initalizers.lox
Normal file
17
samples/ch8_initalizers.lox
Normal 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();
|
@ -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 {
|
||||
|
@ -72,7 +72,6 @@ pub fn main() !void {
|
||||
} else {
|
||||
vm.init_vm(allocator);
|
||||
}
|
||||
|
||||
defer vm.destroy();
|
||||
|
||||
if (args.len == 1) {
|
||||
|
@ -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", .{});
|
||||
|
@ -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", .{});
|
||||
}
|
||||
|
30
src/vm.zig
30
src/vm.zig
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user