diff --git a/src/compiler.c b/src/compiler.c index 719d926..e35baf2 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -964,6 +964,11 @@ static void endScope() { } } +static void markInitialized() { + if (current->scopeDepth == 0) return; + current->locals[current->localCount - 1].depth = current->scopeDepth; +} + static void block(size_t indentation, const char * blockName) { if (match(TOKEN_EOL)) { if (check(TOKEN_INDENTATION)) { @@ -1259,43 +1264,28 @@ static KrkToken classDeclaration() { beginScope(); - KrkToken className = parser.previous; size_t constInd = identifierConstant(&parser.previous); declareVariable(); - EMIT_CONSTANT_OP(OP_CLASS, constInd); - defineVariable(constInd); + markInitialized(); ClassCompiler classCompiler; classCompiler.name = parser.previous; classCompiler.enclosing = currentClass; currentClass = &classCompiler; - int hasSuperclass = 0; classCompiler.hasAnnotations = 0; if (match(TOKEN_LEFT_PAREN)) { startEatingWhitespace(); if (!check(TOKEN_RIGHT_PAREN)) { expression(); - hasSuperclass = 1; + emitByte(OP_INHERIT); } stopEatingWhitespace(); consume(TOKEN_RIGHT_PAREN, "Expected ) after superclass."); } - if (!hasSuperclass) { - KrkToken Object = syntheticToken("object"); - size_t ind = identifierConstant(&Object); - EMIT_CONSTANT_OP(OP_GET_GLOBAL, ind); - } - beginScope(); - addLocal(syntheticToken("super")); - defineVariable(0); - - namedVariable(className, 0); - - emitByte(OP_INHERIT); consume(TOKEN_COLON, "Expected colon after class"); @@ -1342,12 +1332,7 @@ _pop_class: freeCompiler(&subcompiler); emitBytes(OP_CALL, 0); - return className; -} - -static void markInitialized() { - if (current->scopeDepth == 0) return; - current->locals[current->localCount - 1].depth = current->scopeDepth; + return classCompiler.name; } static void lambda(int canAssign) { @@ -2366,16 +2351,28 @@ static void variable(int canAssign) { } static void super_(int canAssign) { - if (currentClass == NULL) { - error("Invalid reference to `super` outside of a class."); - } consume(TOKEN_LEFT_PAREN, "Expected `super` to be called."); - consume(TOKEN_RIGHT_PAREN, "`super` can not take arguments."); + + /* Argument time */ + if (match(TOKEN_RIGHT_PAREN)) { + if (!isMethod(current->type)) { + error("super() outside of a method body requires arguments"); + return; + } + namedVariable(currentClass->name, 0); + EMIT_CONSTANT_OP(OP_GET_LOCAL, 0); + } else { + expression(); + if (match(TOKEN_COMMA)) { + expression(); + } else { + emitConstant(KWARGS_VAL(0)); + } + consume(TOKEN_RIGHT_PAREN, "Expected ')' after argument list"); + } consume(TOKEN_DOT, "Expected a field of `super()` to be referenced."); consume(TOKEN_IDENTIFIER, "Expected a field name."); size_t ind = identifierConstant(&parser.previous); - EMIT_CONSTANT_OP(OP_GET_LOCAL, 0); - namedVariable(syntheticToken("super"), 0); EMIT_CONSTANT_OP(OP_GET_SUPER, ind); } diff --git a/src/vm.c b/src/vm.c index 6d06d25..7d9226c 100644 --- a/src/vm.c +++ b/src/vm.c @@ -2261,17 +2261,18 @@ _finishReturn: (void)0; break; } case OP_INHERIT: { - KrkValue superclass = krk_peek(1); + KrkValue superclass = krk_peek(0); if (unlikely(!IS_CLASS(superclass))) { krk_runtimeError(vm.exceptions->typeError, "Superclass must be a class, not '%s'", krk_typeName(superclass)); goto _finishException; } - KrkClass * subclass = AS_CLASS(krk_peek(0)); + KrkClass * subclass = AS_CLASS(krk_peek(1)); subclass->base = AS_CLASS(superclass); subclass->allocSize = AS_CLASS(superclass)->allocSize; subclass->_ongcsweep = AS_CLASS(superclass)->_ongcsweep; subclass->_ongcscan = AS_CLASS(superclass)->_ongcscan; + krk_pop(); /* Super class */ break; } case OP_DOCSTRING: { @@ -2627,12 +2628,33 @@ _finishReturn: (void)0; case OP_GET_SUPER_LONG: case OP_GET_SUPER: { KrkString * name = READ_STRING(OPERAND); - KrkClass * superclass = AS_CLASS(krk_pop()); + KrkValue baseClass = krk_peek(1); + if (!IS_CLASS(baseClass)) { + krk_runtimeError(vm.exceptions->typeError, + "super() argument 1 must be class, not %s", krk_typeName(baseClass)); + goto _finishException; + } + if (IS_KWARGS(krk_peek(0))) { + krk_runtimeError(vm.exceptions->notImplementedError, + "Unbound super() reference not supported"); + goto _finishException; + } + if (!krk_isInstanceOf(krk_peek(0), AS_CLASS(baseClass))) { + krk_runtimeError(vm.exceptions->typeError, + "'%s' object is not an instance of '%s'", + krk_typeName(krk_peek(0)), AS_CLASS(baseClass)->name->chars); + goto _finishException; + } + KrkClass * superclass = AS_CLASS(baseClass)->base ? AS_CLASS(baseClass)->base : vm.baseClasses->objectClass; if (!krk_bindMethod(superclass, name)) { krk_runtimeError(vm.exceptions->attributeError, "'%s' object has no attribute '%s'", superclass->name->chars, name->chars); goto _finishException; } + /* Swap bind and superclass */ + krk_swap(1); + /* Pop super class */ + krk_pop(); break; } case OP_DUP_LONG: diff --git a/test/testSuperArguments.krk b/test/testSuperArguments.krk new file mode 100644 index 0000000..3960eaf --- /dev/null +++ b/test/testSuperArguments.krk @@ -0,0 +1,22 @@ +class Foo: + def method(): + print("hi") + +class Bar: + pass + +try: + super(Foo,Bar()).test +except TypeError as e: + print(str(e)) + +class Baz(Foo): + def method(): + print("hello") + +class Qux(Baz): + def method(): + super().method() + super(Baz,self).method() + +Qux().method() diff --git a/test/testSuperArguments.krk.expect b/test/testSuperArguments.krk.expect new file mode 100644 index 0000000..05ae62d --- /dev/null +++ b/test/testSuperArguments.krk.expect @@ -0,0 +1,3 @@ +'Bar' object is not an instance of 'Foo' +hello +hi