Compare commits

..

No commits in common. "7e216dd38294c7521d0aea452c7ae8c1a35fc27c" and "1300c42a09cc2bbdb8aaddc485e3683cdce42579" have entirely different histories.

10 changed files with 15 additions and 164 deletions

View File

@ -19,8 +19,8 @@ While reading [Crafting Interpreters](https://craftinginterpreters.com/), after
- [x] 26 - Garbage Collection - [x] 26 - Garbage Collection
- [x] 27 - Classes and Instances - [x] 27 - Classes and Instances
- [x] 28 - Method and Initializers - [x] 28 - Method and Initializers
- [x] 29 - Superclasses - [ ] 29 - Superclasses
- [x] 30 - Optimization (without NaN boxing/NaN tagging) - [ ] 30 - Optimization
## Build & run ## Build & run

View File

@ -1,21 +0,0 @@
class Doughnut {
cook() {
print "Dunk in the fryer.";
}
zzz() {
print "Bla";
}
}
class Cruller < Doughnut {
finish() {
print "Glaze with icing.";
}
}
var o2 = Cruller();
o2.cook();
o2.finish();

View File

@ -1,19 +0,0 @@
class A {
method() {
print "A method";
}
}
class B < A {
method() {
print "B method";
}
test() {
this.method();
super.method();
}
}
class C < B {}
C().test();

View File

@ -1,2 +0,0 @@
class Cake < Cake {
}

View File

@ -1,2 +0,0 @@
var NotClass = "So not a class";
class OhNo < NotClass {}

View File

@ -73,18 +73,18 @@ pub const Chunk = struct {
debug.print("== end of chunk dump \n\n", .{}); debug.print("== end of chunk dump \n\n", .{});
} }
pub fn disassemble(self: Chunk, name: []const u8) void { pub fn dissassemble(self: Chunk, name: []const u8) void {
debug.print("== {s} ==\n", .{name}); debug.print("== {s} ==\n", .{name});
var offset: usize = 0; var offset: usize = 0;
while (offset < self.count) { while (offset < self.count) {
offset = self.disassemble_instruction(offset); offset = self.dissassemble_instruction(offset);
} }
debug.print("== end of {s} ==\n\n", .{name}); debug.print("== end of {s} ==\n\n", .{name});
} }
pub fn disassemble_instruction(self: Chunk, offset: usize) usize { pub fn dissassemble_instruction(self: Chunk, offset: usize) usize {
var current_offset = offset; var current_offset = offset;
debug.print("{d:0>4} ", .{offset}); debug.print("{d:0>4} ", .{offset});
@ -155,9 +155,6 @@ pub const Chunk = struct {
@intFromEnum(OpCode.OP_INDEX_GET) => return utils.simple_instruction("OP_INDEX_GET", offset), @intFromEnum(OpCode.OP_INDEX_GET) => return utils.simple_instruction("OP_INDEX_GET", offset),
@intFromEnum(OpCode.OP_INDEX_SET) => return utils.simple_instruction("OP_INDEX_SET", offset), @intFromEnum(OpCode.OP_INDEX_SET) => return utils.simple_instruction("OP_INDEX_SET", offset),
@intFromEnum(OpCode.OP_INVOKE) => return utils.invoke_instruction("OP_INVOKE", self, offset), @intFromEnum(OpCode.OP_INVOKE) => return utils.invoke_instruction("OP_INVOKE", self, offset),
@intFromEnum(OpCode.OP_INHERIT) => return utils.simple_instruction("OP_INHERIT", offset),
@intFromEnum(OpCode.OP_GET_SUPER) => return utils.constant_instruction("OP_GET_SUPER", self, offset),
@intFromEnum(OpCode.OP_SUPER_INVOKE) => return utils.invoke_instruction("OP_SUPER_INVOKE", self, offset),
else => { else => {
debug.print("unknown opcode {d}\n", .{instruction}); debug.print("unknown opcode {d}\n", .{instruction});
return offset + 1; return offset + 1;

View File

@ -38,7 +38,6 @@ const ParserFn = *const fn (*Parser, bool) ParsingError!void;
const ClassCompiler = struct { const ClassCompiler = struct {
enclosing: ?*ClassCompiler, enclosing: ?*ClassCompiler,
has_super_class: bool,
}; };
const ParserRule = struct { const ParserRule = struct {
@ -158,14 +157,8 @@ pub const Parser = struct {
fn end_parser(self: *Parser) !*Obj.Function { fn end_parser(self: *Parser) !*Obj.Function {
try self.emit_return(); try self.emit_return();
const compiler_function = self.compiler.function;
if (!self.had_error and constants.DEBUG_PRINT_CODE) { if (!self.had_error and constants.DEBUG_PRINT_CODE) {
var label: []const u8 = "<script>"; self.current_chunk().dissassemble("code");
if (compiler_function.name != null) {
label = compiler_function.name.?.chars;
}
self.current_chunk().disassemble(label);
} }
const function_obj = self.compiler.function; const function_obj = self.compiler.function;
@ -314,7 +307,7 @@ pub const Parser = struct {
TokenType.OR => ParserRule{ .prefix = null, .infix = or_, .precedence = Precedence.Or }, TokenType.OR => ParserRule{ .prefix = null, .infix = or_, .precedence = Precedence.Or },
TokenType.PRINT => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None }, TokenType.PRINT => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
TokenType.RETURN => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None }, TokenType.RETURN => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
TokenType.SUPER => ParserRule{ .prefix = super_, .infix = null, .precedence = Precedence.None }, TokenType.SUPER => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
TokenType.THIS => ParserRule{ .prefix = this_, .infix = null, .precedence = Precedence.None }, TokenType.THIS => ParserRule{ .prefix = this_, .infix = null, .precedence = Precedence.None },
TokenType.TRUE => ParserRule{ .prefix = literal, .infix = null, .precedence = Precedence.None }, TokenType.TRUE => ParserRule{ .prefix = literal, .infix = null, .precedence = Precedence.None },
TokenType.VAR => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None }, TokenType.VAR => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
@ -924,28 +917,9 @@ pub const Parser = struct {
var class_compiler = ClassCompiler{ var class_compiler = ClassCompiler{
.enclosing = self.current_class_compiler(), .enclosing = self.current_class_compiler(),
.has_super_class = false,
}; };
self.class_compiler = &class_compiler; self.class_compiler = &class_compiler;
if (self.match(TokenType.LESS)) {
self.consume(TokenType.IDENTIFIER, "Expect superclass name.");
try self.variable(false);
if (identifiers_equals(class_name, self.previous.?)) {
self.error_msg("A class can't inherit from itself.");
}
self.begin_scope();
self.add_local(self.synthetic_token("super"));
try self.define_variable(0);
try self.named_variable(class_name, false);
try self.emit_byte(@intFromEnum(OpCode.OP_INHERIT));
self.current_class_compiler().?.has_super_class = true;
}
try self.named_variable(class_name, false); try self.named_variable(class_name, false);
self.consume(TokenType.LEFT_BRACE, "Expect '{' before class body."); self.consume(TokenType.LEFT_BRACE, "Expect '{' before class body.");
@ -955,23 +929,9 @@ pub const Parser = struct {
self.consume(TokenType.RIGHT_BRACE, "Expect '}' after class body."); self.consume(TokenType.RIGHT_BRACE, "Expect '}' after class body.");
try self.emit_byte(@intFromEnum(OpCode.OP_POP)); try self.emit_byte(@intFromEnum(OpCode.OP_POP));
if (self.current_class_compiler().?.has_super_class) {
try self.end_scope();
}
self.class_compiler = self.current_class_compiler().?.enclosing; self.class_compiler = self.current_class_compiler().?.enclosing;
} }
fn synthetic_token(self: *Parser, text: []const u8) Token {
_ = self;
return Token{
.token_type = TokenType.EOF,
.start = text,
.length = text.len,
.line = 0,
};
}
fn dot(self: *Parser, can_assign: bool) ParsingError!void { fn dot(self: *Parser, can_assign: bool) ParsingError!void {
self.consume(TokenType.IDENTIFIER, "Expect property name after '.'."); self.consume(TokenType.IDENTIFIER, "Expect property name after '.'.");
const name = try self.identifier_constant(self.previous.?); const name = try self.identifier_constant(self.previous.?);
@ -1010,32 +970,6 @@ pub const Parser = struct {
_ = can_assign; _ = can_assign;
try self.variable(false); try self.variable(false);
} }
fn super_(self: *Parser, can_assign: bool) ParsingError!void {
_ = can_assign;
if (self.current_class_compiler() == null) {
self.error_msg("Can't use 'super' outside of a class.");
} else if (!self.current_class_compiler().?.has_super_class) {
self.error_msg("Can't use 'super' in a class with no superclass.");
}
self.consume(TokenType.DOT, "Expect '.' after 'super'.");
self.consume(TokenType.IDENTIFIER, "Expect superclass method name.");
const name = try self.identifier_constant(self.previous.?);
try self.named_variable(self.synthetic_token("this"), false);
if (self.match(TokenType.LEFT_PAREN)) {
const arg_count = try self.argument_list();
try self.named_variable(self.synthetic_token("super"), false);
try self.emit_bytes(@intFromEnum(OpCode.OP_SUPER_INVOKE), name);
try self.emit_byte(@intCast(arg_count));
} else {
try self.named_variable(self.synthetic_token("super"), false);
try self.emit_bytes(@intFromEnum(OpCode.OP_GET_SUPER), name);
}
}
}; };
const FunctionType = enum { const FunctionType = enum {

View File

@ -35,7 +35,4 @@ pub const OpCode = enum(u8) {
OP_INDEX_SET, OP_INDEX_SET,
OP_INDEX_GET, OP_INDEX_GET,
OP_INVOKE, OP_INVOKE,
OP_INHERIT,
OP_GET_SUPER,
OP_SUPER_INVOKE,
}; };

View File

@ -61,8 +61,7 @@ pub const Table = struct {
pub fn find_entry(entries: []Entry, key: *Obj.String) ?*Entry { pub fn find_entry(entries: []Entry, key: *Obj.String) ?*Entry {
var tombstone: ?*Entry = null; var tombstone: ?*Entry = null;
// var index = key.hash % entries.len; var index = key.hash % entries.len;
var index = key.hash & (entries.len - 1);
while (true) { while (true) {
const entry = &entries[index]; const entry = &entries[index];
@ -85,8 +84,7 @@ pub const Table = struct {
return entry; return entry;
} }
// index = (index + 1) % entries.len; index = (index + 1) % entries.len;
index = (index + 1) & (entries.len - 1);
} }
} }
@ -134,12 +132,12 @@ pub const Table = struct {
std.debug.print("== End of hash table ==\n\n", .{}); std.debug.print("== End of hash table ==\n\n", .{});
} }
pub fn add_all(self: *Table, to: *Table) void { pub fn add_all(self: *Table, from: Table) void {
for (self.entries) |entry| { for (from.entries) |entry| {
if (entry.key == null) { if (entry.key == null) {
continue; continue;
} }
_ = to.set(entry.key.?, entry.value); _ = self.set(entry.key.?, entry.value);
} }
} }
@ -181,8 +179,7 @@ pub const Table = struct {
return null; return null;
} }
// var index = hash % self.capacity; var index = hash % self.capacity;
var index = hash & (self.capacity - 1);
while (true) { while (true) {
const entry = &self.entries[index]; const entry = &self.entries[index];
if (entry.key == null) { if (entry.key == null) {
@ -194,8 +191,7 @@ pub const Table = struct {
return entry.key; return entry.key;
} }
// index = (index + 1) % self.capacity; index = (index + 1) % self.capacity;
index = (index + 1) & (self.capacity - 1);
} }
} }
}; };

View File

@ -156,7 +156,7 @@ pub const VM = struct {
} }
debug.print("\n", .{}); debug.print("\n", .{});
} }
_ = self.current_chunk().disassemble_instruction(self.current_frame().ip); _ = self.current_chunk().dissassemble_instruction(self.current_frame().ip);
} }
const instruction = self.read_byte(); const instruction = self.read_byte();
@ -379,35 +379,6 @@ pub const VM = struct {
return InterpretResult.RUNTIME_ERROR; return InterpretResult.RUNTIME_ERROR;
} }
}, },
@intFromEnum(OpCode.OP_INHERIT) => {
const superclass = self.peek(1);
const subclass = self.peek(0).as_obj().as_class();
if (!superclass.is_obj() or !superclass.as_obj().is_class()) {
self.runtime_error("Superclass must be a class.");
return InterpretResult.RUNTIME_ERROR;
}
superclass.as_obj().as_class().methods.add_all(&subclass.methods);
_ = self.pop(); // subclass
},
@intFromEnum(OpCode.OP_GET_SUPER) => {
const name: *Obj.String = self.read_constant().as_string();
const superclass = self.pop().as_obj().as_class();
if (!self.bind_method(superclass, name)) {
return InterpretResult.RUNTIME_ERROR;
}
},
@intFromEnum(OpCode.OP_SUPER_INVOKE) => {
const method = self.read_constant().as_string();
const arg_count = self.read_byte();
const superclass = self.pop().as_obj().as_class();
if (!self.invoke_from_class(superclass, method, arg_count)) {
return InterpretResult.RUNTIME_ERROR;
}
},
else => { else => {
debug.print("Invalid instruction: {d}\n", .{instruction}); debug.print("Invalid instruction: {d}\n", .{instruction});
return InterpretResult.RUNTIME_ERROR; return InterpretResult.RUNTIME_ERROR;