diff --git a/clox/src/chunk.h b/clox/src/chunk.h index 4810f38..1748adc 100644 --- a/clox/src/chunk.h +++ b/clox/src/chunk.h @@ -19,6 +19,7 @@ typedef enum { OP_SET_UPVALUE, OP_GET_PROPERTY, OP_SET_PROPERTY, + OP_GET_SUPER, OP_EQUAL, OP_GREATER, OP_LESS, @@ -34,6 +35,7 @@ typedef enum { OP_LOOP, OP_CALL, OP_INVOKE, + OP_SUPER_INVOKE, OP_CLOSURE, OP_CLOSE_UPVALUE, OP_RETURN, diff --git a/clox/src/compiler.c b/clox/src/compiler.c index 599ad1b..fd435d5 100644 --- a/clox/src/compiler.c +++ b/clox/src/compiler.c @@ -486,6 +486,29 @@ static Token syntheticToken(const char *text) { return token; } +static void super_(bool canAssign) { + if (currentClass == NULL) { + error("Can't use 'super' outside of a class."); + } else if (!currentClass->hasSuperclass) { + error("Can't use 'super' in a class with no superclass."); + } + + consume(TOKEN_DOT, "Expect '.' after 'super'."); + consume(TOKEN_IDENTIFIER, "Expect superclass method name."); + uint8_t name = identifierConstant(&parser.previous); + + namedVariable(syntheticToken("this"), false); + if (match(TOKEN_LEFT_PAREN)) { + uint8_t argCount = argumentList(); + namedVariable(syntheticToken("super"), false); + emitBytes(OP_SUPER_INVOKE, name); + emitByte(argCount); + } else { + namedVariable(syntheticToken("super"), false); + emitBytes(OP_GET_SUPER, name); + } +} + static void classDeclaration() { consume(TOKEN_IDENTIFIER, "Expect class name"); Token className = parser.previous; @@ -823,7 +846,7 @@ ParseRule rules[] = { [TOKEN_OR] = {NULL, or_, PREC_OR}, [TOKEN_PRINT] = {NULL, NULL, PREC_NONE}, [TOKEN_RETURN] = {NULL, NULL, PREC_NONE}, - [TOKEN_SUPER] = {NULL, NULL, PREC_NONE}, + [TOKEN_SUPER] = {super_, NULL, PREC_NONE}, [TOKEN_THIS] = {this_, NULL, PREC_NONE}, [TOKEN_TRUE] = {literal, NULL, PREC_NONE}, [TOKEN_VAR] = {NULL, NULL, PREC_NONE}, diff --git a/clox/src/debug.c b/clox/src/debug.c index 4a39811..b8e4a48 100644 --- a/clox/src/debug.c +++ b/clox/src/debug.c @@ -86,6 +86,8 @@ int disassembleInstruction(Chunk *chunk, int offset) { return constantInstruction("OP_GET_PROPERTY", chunk, offset); case OP_SET_PROPERTY: return constantInstruction("OP_SET_PROPERTY", chunk, offset); + case OP_GET_SUPER: + return constantInstruction("OP_GET_SUPER", chunk, offset); case OP_EQUAL: return simpleInstruction("OP_EQUAL", offset); case OP_GREATER: @@ -116,6 +118,8 @@ int disassembleInstruction(Chunk *chunk, int offset) { return byteInstruction("OP_CALL", chunk, offset); case OP_INVOKE: return invokeInstruction("OP_INVOKE", chunk, offset); + case OP_SUPER_INVOKE: + return invokeInstruction("OP_SUPER_INVOKE", chunk, offset); case OP_CLOSURE: { offset++; uint8_t constant = chunk->code[offset++]; diff --git a/clox/src/vm.c b/clox/src/vm.c index 8a6847e..47c9f29 100644 --- a/clox/src/vm.c +++ b/clox/src/vm.c @@ -379,6 +379,15 @@ static InterpretResult run() { push(value); break; } + case OP_GET_SUPER: { + ObjString *name = READ_STRING(); + ObjClass *superclass = AS_CLASS(pop()); + + if (!bindMethod(superclass, name)) { + return INTERPRET_RUNTIME_ERROR; + } + break; + } case OP_EQUAL: { Value b = pop(); Value a = pop(); @@ -460,6 +469,16 @@ static InterpretResult run() { frame = &vm.frames[vm.frameCount - 1]; break; } + case OP_SUPER_INVOKE: { + ObjString *method = READ_STRING(); + int argCount = READ_BYTE(); + ObjClass *superclass = AS_CLASS(pop()); + if (!invokeFromClass(superclass, method, argCount)) { + return INTERPRET_RUNTIME_ERROR; + } + frame = &vm.frames[vm.frameCount - 1]; + break; + } case OP_CLOSURE: { ObjFunction *function = AS_FUNCTION(READ_CONSTANT()); ObjClosure *closure = newClosure(function);