implementing superclasses (ch29)
This commit is contained in:
parent
1300c42a09
commit
980312bd62
21
samples/ch29_superclass.lox
Normal file
21
samples/ch29_superclass.lox
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
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();
|
||||||
|
|
||||||
|
|
19
samples/ch29_superclass2.lox
Normal file
19
samples/ch29_superclass2.lox
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
class A {
|
||||||
|
method() {
|
||||||
|
print "A method";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class B < A {
|
||||||
|
method() {
|
||||||
|
print "B method";
|
||||||
|
}
|
||||||
|
|
||||||
|
test() {
|
||||||
|
this.method();
|
||||||
|
super.method();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class C < B {}
|
||||||
|
C().test();
|
2
samples/ch29_superclass_error.lox
Normal file
2
samples/ch29_superclass_error.lox
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
class Cake < Cake {
|
||||||
|
}
|
2
samples/ch29_superclass_error2.lox
Normal file
2
samples/ch29_superclass_error2.lox
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
var NotClass = "So not a class";
|
||||||
|
class OhNo < NotClass {}
|
@ -155,6 +155,9 @@ 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;
|
||||||
|
@ -38,6 +38,7 @@ 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 {
|
||||||
@ -307,7 +308,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 = null, .infix = null, .precedence = Precedence.None },
|
TokenType.SUPER => ParserRule{ .prefix = super_, .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 },
|
||||||
@ -917,9 +918,28 @@ 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.");
|
||||||
@ -929,9 +949,23 @@ 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.?);
|
||||||
@ -970,6 +1004,32 @@ 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 {
|
||||||
|
@ -10,14 +10,14 @@ pub const UINT8_COUNT = UINT8_MAX + 1;
|
|||||||
pub const FRAMES_MAX = 64;
|
pub const FRAMES_MAX = 64;
|
||||||
pub const STACK_MAX = (FRAMES_MAX * UINT8_MAX);
|
pub const STACK_MAX = (FRAMES_MAX * UINT8_MAX);
|
||||||
|
|
||||||
pub const DEBUG_PRINT_CODE = false;
|
pub const DEBUG_PRINT_CODE = true;
|
||||||
pub const DEBUG_TRACE_EXECUTION = false;
|
pub const DEBUG_TRACE_EXECUTION = true;
|
||||||
pub const DEBUG_PRINT_INTERNAL_STRINGS = false;
|
pub const DEBUG_PRINT_INTERNAL_STRINGS = false;
|
||||||
pub const DEBUG_PRINT_GLOBALS = false;
|
pub const DEBUG_PRINT_GLOBALS = false;
|
||||||
|
|
||||||
pub const DEBUG_STRESS_GC = true;
|
pub const DEBUG_STRESS_GC = true;
|
||||||
pub const DEBUG_LOG_GC = false;
|
pub const DEBUG_LOG_GC = false;
|
||||||
|
|
||||||
pub const USE_CUSTON_ALLOCATOR = true;
|
pub const USE_CUSTON_ALLOCATOR = false;
|
||||||
|
|
||||||
pub const GC_HEAP_GROW_FACTOR = 2;
|
pub const GC_HEAP_GROW_FACTOR = 2;
|
||||||
|
@ -35,4 +35,7 @@ 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,
|
||||||
};
|
};
|
||||||
|
@ -132,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, from: Table) void {
|
pub fn add_all(self: *Table, to: *Table) void {
|
||||||
for (from.entries) |entry| {
|
for (self.entries) |entry| {
|
||||||
if (entry.key == null) {
|
if (entry.key == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
_ = self.set(entry.key.?, entry.value);
|
_ = to.set(entry.key.?, entry.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
29
src/vm.zig
29
src/vm.zig
@ -379,6 +379,35 @@ 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;
|
||||||
|
Loading…
Reference in New Issue
Block a user