Compare commits
	
		
			4 Commits
		
	
	
		
			1300c42a09
			...
			7e216dd382
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 7e216dd382 | |||
| 031ca59a07 | |||
| 3ecbe5ca9d | |||
| 980312bd62 | 
@@ -19,8 +19,8 @@ While reading [Crafting Interpreters](https://craftinginterpreters.com/), after
 | 
			
		||||
- [x] 26 - Garbage Collection
 | 
			
		||||
- [x] 27 - Classes and Instances
 | 
			
		||||
- [x] 28 - Method and Initializers
 | 
			
		||||
- [ ] 29 - Superclasses
 | 
			
		||||
- [ ] 30 - Optimization
 | 
			
		||||
- [x] 29 - Superclasses
 | 
			
		||||
- [x] 30 - Optimization (without NaN boxing/NaN tagging)
 | 
			
		||||
 | 
			
		||||
## Build & run
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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 {}
 | 
			
		||||
@@ -73,18 +73,18 @@ pub const Chunk = struct {
 | 
			
		||||
        debug.print("== end of chunk dump \n\n", .{});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn dissassemble(self: Chunk, name: []const u8) void {
 | 
			
		||||
    pub fn disassemble(self: Chunk, name: []const u8) void {
 | 
			
		||||
        debug.print("== {s} ==\n", .{name});
 | 
			
		||||
 | 
			
		||||
        var offset: usize = 0;
 | 
			
		||||
 | 
			
		||||
        while (offset < self.count) {
 | 
			
		||||
            offset = self.dissassemble_instruction(offset);
 | 
			
		||||
            offset = self.disassemble_instruction(offset);
 | 
			
		||||
        }
 | 
			
		||||
        debug.print("== end of {s} ==\n\n", .{name});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn dissassemble_instruction(self: Chunk, offset: usize) usize {
 | 
			
		||||
    pub fn disassemble_instruction(self: Chunk, offset: usize) usize {
 | 
			
		||||
        var current_offset = offset;
 | 
			
		||||
        debug.print("{d:0>4} ", .{offset});
 | 
			
		||||
 | 
			
		||||
@@ -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_SET) => return utils.simple_instruction("OP_INDEX_SET", 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 => {
 | 
			
		||||
                debug.print("unknown opcode {d}\n", .{instruction});
 | 
			
		||||
                return offset + 1;
 | 
			
		||||
 
 | 
			
		||||
@@ -38,6 +38,7 @@ const ParserFn = *const fn (*Parser, bool) ParsingError!void;
 | 
			
		||||
 | 
			
		||||
const ClassCompiler = struct {
 | 
			
		||||
    enclosing: ?*ClassCompiler,
 | 
			
		||||
    has_super_class: bool,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const ParserRule = struct {
 | 
			
		||||
@@ -157,8 +158,14 @@ pub const Parser = struct {
 | 
			
		||||
    fn end_parser(self: *Parser) !*Obj.Function {
 | 
			
		||||
        try self.emit_return();
 | 
			
		||||
 | 
			
		||||
        const compiler_function = self.compiler.function;
 | 
			
		||||
 | 
			
		||||
        if (!self.had_error and constants.DEBUG_PRINT_CODE) {
 | 
			
		||||
            self.current_chunk().dissassemble("code");
 | 
			
		||||
            var label: []const u8 = "<script>";
 | 
			
		||||
            if (compiler_function.name != null) {
 | 
			
		||||
                label = compiler_function.name.?.chars;
 | 
			
		||||
            }
 | 
			
		||||
            self.current_chunk().disassemble(label);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const function_obj = self.compiler.function;
 | 
			
		||||
@@ -307,7 +314,7 @@ pub const Parser = struct {
 | 
			
		||||
            TokenType.OR => ParserRule{ .prefix = null, .infix = or_, .precedence = Precedence.Or },
 | 
			
		||||
            TokenType.PRINT => 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.TRUE => ParserRule{ .prefix = literal, .infix = null, .precedence = Precedence.None },
 | 
			
		||||
            TokenType.VAR => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
 | 
			
		||||
@@ -917,9 +924,28 @@ pub const Parser = struct {
 | 
			
		||||
 | 
			
		||||
        var class_compiler = ClassCompiler{
 | 
			
		||||
            .enclosing = self.current_class_compiler(),
 | 
			
		||||
            .has_super_class = false,
 | 
			
		||||
        };
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
        self.consume(TokenType.LEFT_BRACE, "Expect '{' before class body.");
 | 
			
		||||
@@ -929,9 +955,23 @@ pub const Parser = struct {
 | 
			
		||||
        self.consume(TokenType.RIGHT_BRACE, "Expect '}' after class body.");
 | 
			
		||||
        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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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 {
 | 
			
		||||
        self.consume(TokenType.IDENTIFIER, "Expect property name after '.'.");
 | 
			
		||||
        const name = try self.identifier_constant(self.previous.?);
 | 
			
		||||
@@ -970,6 +1010,32 @@ pub const Parser = struct {
 | 
			
		||||
        _ = can_assign;
 | 
			
		||||
        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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -35,4 +35,7 @@ pub const OpCode = enum(u8) {
 | 
			
		||||
    OP_INDEX_SET,
 | 
			
		||||
    OP_INDEX_GET,
 | 
			
		||||
    OP_INVOKE,
 | 
			
		||||
    OP_INHERIT,
 | 
			
		||||
    OP_GET_SUPER,
 | 
			
		||||
    OP_SUPER_INVOKE,
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -61,7 +61,8 @@ pub const Table = struct {
 | 
			
		||||
 | 
			
		||||
    pub fn find_entry(entries: []Entry, key: *Obj.String) ?*Entry {
 | 
			
		||||
        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) {
 | 
			
		||||
            const entry = &entries[index];
 | 
			
		||||
@@ -84,7 +85,8 @@ pub const Table = struct {
 | 
			
		||||
                return entry;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            index = (index + 1) % entries.len;
 | 
			
		||||
            // index = (index + 1) % entries.len;
 | 
			
		||||
            index = (index + 1) & (entries.len - 1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -132,12 +134,12 @@ pub const Table = struct {
 | 
			
		||||
        std.debug.print("== End of hash table ==\n\n", .{});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn add_all(self: *Table, from: Table) void {
 | 
			
		||||
        for (from.entries) |entry| {
 | 
			
		||||
    pub fn add_all(self: *Table, to: *Table) void {
 | 
			
		||||
        for (self.entries) |entry| {
 | 
			
		||||
            if (entry.key == null) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            _ = self.set(entry.key.?, entry.value);
 | 
			
		||||
            _ = to.set(entry.key.?, entry.value);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -179,7 +181,8 @@ pub const Table = struct {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var index = hash % self.capacity;
 | 
			
		||||
        // var index = hash % self.capacity;
 | 
			
		||||
        var index = hash & (self.capacity - 1);
 | 
			
		||||
        while (true) {
 | 
			
		||||
            const entry = &self.entries[index];
 | 
			
		||||
            if (entry.key == null) {
 | 
			
		||||
@@ -191,7 +194,8 @@ pub const Table = struct {
 | 
			
		||||
                return entry.key;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            index = (index + 1) % self.capacity;
 | 
			
		||||
            // index = (index + 1) % self.capacity;
 | 
			
		||||
            index = (index + 1) & (self.capacity - 1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										31
									
								
								src/vm.zig
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								src/vm.zig
									
									
									
									
									
								
							@@ -156,7 +156,7 @@ pub const VM = struct {
 | 
			
		||||
                    }
 | 
			
		||||
                    debug.print("\n", .{});
 | 
			
		||||
                }
 | 
			
		||||
                _ = self.current_chunk().dissassemble_instruction(self.current_frame().ip);
 | 
			
		||||
                _ = self.current_chunk().disassemble_instruction(self.current_frame().ip);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const instruction = self.read_byte();
 | 
			
		||||
@@ -379,6 +379,35 @@ pub const VM = struct {
 | 
			
		||||
                        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 => {
 | 
			
		||||
                    debug.print("Invalid instruction: {d}\n", .{instruction});
 | 
			
		||||
                    return InterpretResult.RUNTIME_ERROR;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user