From f655ca7f61076b281f95607daff445335bc82d70 Mon Sep 17 00:00:00 2001 From: K Lange Date: Thu, 11 Mar 2021 19:05:21 +0900 Subject: [PATCH] Fix up function, object repring and add __qualname__ to functions --- src/builtins.c | 19 +- src/compiler.c | 58 ++-- src/obj_function.c | 377 +++++++++++++--------- src/object.h | 5 +- src/vm.c | 6 +- test/test.krk | 2 +- test/test.krk.expect | 2 +- test/testClassMethodAssignment.krk | 4 +- test/testClassMethodAssignment.krk.expect | 4 +- test/testComplexArgsNative.krk | 2 +- test/testComplexArgsNative.krk.expect | 2 +- test/testDecorators.krk | 4 +- test/testDecorators.krk.expect | 4 +- test/testDefaultArgs.krk | 2 +- test/testDefaultArgs.krk.expect | 2 +- test/testList.krk | 5 - test/testList.krk.expect | 1 - test/testStrReprOrdering.krk.expect | 4 +- test/testUnicodeIdentifiers.krk | 2 +- test/testUnicodeIdentifiers.krk.expect | 2 +- test/testUpvalue.krk.expect | 2 +- 21 files changed, 294 insertions(+), 215 deletions(-) diff --git a/src/builtins.c b/src/builtins.c index 7c9310b..e4322a0 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -658,13 +658,26 @@ static KrkValue obj_hash(int argc, KrkValue argv[], int hasKw) { */ static KrkValue _strBase(int argc, KrkValue argv[], int hasKw) { KrkClass * type = krk_getType(argv[0]); - size_t allocSize = sizeof("") + type->name->length; + + KrkValue module = NONE_VAL(); + krk_tableGet(&type->methods, OBJECT_VAL(S("__module__")), &module); + KrkValue qualname = NONE_VAL(); + krk_tableGet(&type->methods, OBJECT_VAL(S("__qualname__")), &qualname); + KrkString * name = IS_STRING(qualname) ? AS_STRING(qualname) : type->name; + int includeModule = !(IS_NONE(module) || (IS_STRING(module) && AS_STRING(module) == S("__builtins__"))); + + size_t allocSize = sizeof("<. object at 0x1234567812345678>") + name->length; + if (includeModule) allocSize += AS_STRING(module)->length + 1; char * tmp = malloc(allocSize); size_t len; if (IS_OBJECT(argv[0])) { - len = snprintf(tmp, allocSize, "", type->name->chars, (void*)AS_OBJECT(argv[0])); + len = snprintf(tmp, allocSize, "<%s%s%s object at %p>", + includeModule ? AS_CSTRING(module) : "", + includeModule ? "." : "", + name->chars, + (void*)AS_OBJECT(argv[0])); } else { - len = snprintf(tmp, allocSize, "", type->name->chars); + len = snprintf(tmp, allocSize, "<%s object>", name->chars); } KrkValue out = OBJECT_VAL(krk_copyString(tmp, len)); free(tmp); diff --git a/src/compiler.c b/src/compiler.c index 2f2529b..63eb10d 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -153,6 +153,34 @@ static int isMethod(int type) { return type == TYPE_METHOD || type == TYPE_INIT || type == TYPE_PROPERTY; } +static char * calculateQualName(void) { + static char space[1024]; /* We'll just truncate if we need to */ + space[1023] = '\0'; + char * writer = &space[1023]; + +#define WRITE(s) do { \ + size_t len = strlen(s); \ + if (writer - len < space) break; \ + writer -= len; \ + memcpy(writer, s, len); \ +} while (0) + + WRITE(current->function->name->chars); + /* Go up by _compiler_, ignore class compilers as we don't need them. */ + Compiler * ptr = current->enclosing; + while (ptr->enclosing) { /* Ignores the top level module */ + if (ptr->type != TYPE_CLASS) { + /* We must be the locals of a function. */ + WRITE("."); + } + WRITE("."); + WRITE(ptr->function->name->chars); + ptr = ptr->enclosing; + } + + return writer; +} + static void initCompiler(Compiler * compiler, FunctionType type) { compiler->enclosing = current; current = compiler; @@ -178,6 +206,8 @@ static void initCompiler(Compiler * compiler, FunctionType type) { if (type != TYPE_MODULE) { current->function->name = krk_copyString(parser.previous.start, parser.previous.length); + char * qualname = calculateQualName(); + current->function->qualname = krk_copyString(qualname, strlen(qualname)); } if (isMethod(type)) { @@ -1049,34 +1079,6 @@ static void method(size_t blockWidth) { emitByte(OP_POP); \ } while (0) -static char * calculateQualName(void) { - static char space[1024]; /* We'll just truncate if we need to */ - space[1023] = '\0'; - char * writer = &space[1023]; - -#define WRITE(s) do { \ - size_t len = strlen(s); \ - if (writer - len < space) break; \ - writer -= len; \ - memcpy(writer, s, len); \ -} while (0) - - WRITE(current->function->name->chars); - /* Go up by _compiler_, ignore class compilers as we don't need them. */ - Compiler * ptr = current->enclosing; - while (ptr->enclosing) { /* Ignores the top level module */ - if (ptr->type != TYPE_CLASS) { - /* We must be the locals of a function. */ - WRITE("."); - } - WRITE("."); - WRITE(ptr->function->name->chars); - ptr = ptr->enclosing; - } - - return writer; -} - static KrkToken classDeclaration() { size_t blockWidth = (parser.previous.type == TOKEN_INDENTATION) ? parser.previous.length : 0; advance(); /* Collect the `class` */ diff --git a/src/obj_function.c b/src/obj_function.c index ea5b93b..6fb0854 100644 --- a/src/obj_function.c +++ b/src/obj_function.c @@ -5,23 +5,6 @@ #include "util.h" #include "debug.h" -/* function.__doc__ */ -static KrkValue _closure_get_doc(int argc, KrkValue argv[], int hasKw) { - if (IS_NATIVE(argv[0]) && AS_NATIVE(argv[0])->doc) { - return OBJECT_VAL(krk_copyString(AS_NATIVE(argv[0])->doc, strlen(AS_NATIVE(argv[0])->doc))); - } else if (IS_CLOSURE(argv[0]) && AS_CLOSURE(argv[0])->function->docstring) { - return OBJECT_VAL(AS_CLOSURE(argv[0])->function->docstring); - } else { - return NONE_VAL(); - } -} - -/* method.__doc__ */ -static KrkValue _bound_get_doc(int argc, KrkValue argv[], int hasKw) { - KrkBoundMethod * boundMethod = AS_BOUND_METHOD(argv[0]); - return _closure_get_doc(1, (KrkValue[]){OBJECT_VAL(boundMethod->method)}, 0); -} - /* Check for and return the name of a native function as a string object */ static KrkValue nativeFunctionName(KrkValue func) { const char * string = ((KrkNative*)AS_OBJECT(func))->name; @@ -30,44 +13,143 @@ static KrkValue nativeFunctionName(KrkValue func) { return OBJECT_VAL(krk_copyString(string,len)); } -static KrkValue _function_get_name(int argc, KrkValue argv[], int hasKw) { - return AS_FUNCTION(argv[0])->name ? OBJECT_VAL(AS_FUNCTION(argv[0])->name) : OBJECT_VAL(S("")); -} +#define IS_codeobject(o) IS_FUNCTION(o) +#define IS_method(o) IS_BOUND_METHOD(o) +#define IS_function(o) (IS_CLOSURE(o)|IS_NATIVE(o)) -/* function.__name__ */ -static KrkValue _closure_get_name(int argc, KrkValue argv[], int hasKw) { - if (IS_NATIVE(argv[0])) return nativeFunctionName(argv[0]); - else if (!IS_CLOSURE(argv[0])) return krk_runtimeError(vm.exceptions->typeError, "'%s' is neither a closure nor a native", krk_typeName(argv[0])); - return AS_CLOSURE(argv[0])->function->name ? OBJECT_VAL(AS_CLOSURE(argv[0])->function->name) : OBJECT_VAL(S("")); -} +#define AS_codeobject(o) AS_FUNCTION(o) +#define AS_method(o) AS_BOUND_METHOD(o) +#define AS_function(o) (o) -/* method.__name__ */ -static KrkValue _bound_get_name(int argc, KrkValue argv[], int hasKw) { - KrkBoundMethod * boundMethod = AS_BOUND_METHOD(argv[0]); - return _closure_get_name(1, (KrkValue[]){OBJECT_VAL(boundMethod->method)}, hasKw); -} +#define CURRENT_NAME self +#define CURRENT_CTYPE KrkValue -static KrkValue _function_ip_to_line(int argc, KrkValue argv[], int hasKw) { - if (argc < 2) return krk_runtimeError(vm.exceptions->argumentError, "%s() expects exactly 2 arguments", "ip_to_line"); - if (!IS_FUNCTION(argv[0])) return NONE_VAL(); - if (!IS_INTEGER(argv[1])) return TYPE_ERROR(int,argv[1]); - return INTEGER_VAL(krk_lineNumber(&AS_FUNCTION(argv[0])->chunk, AS_INTEGER(argv[1]))); -} +KRK_METHOD(function,__doc__,{ + METHOD_TAKES_NONE(); -static KrkValue _closure_ip_to_line(int argc, KrkValue argv[], int hasKw) { - if (argc < 2) return krk_runtimeError(vm.exceptions->argumentError, "%s() expects exactly 2 arguments", "ip_to_line"); - if (!IS_CLOSURE(argv[0])) return NONE_VAL(); - if (!IS_INTEGER(argv[1])) return TYPE_ERROR(int,argv[1]); - return INTEGER_VAL(krk_lineNumber(&AS_CLOSURE(argv[0])->function->chunk, AS_INTEGER(argv[1]))); -} + if (IS_NATIVE(self) && AS_NATIVE(self)->doc) { + return OBJECT_VAL(krk_copyString(AS_NATIVE(self)->doc, strlen(AS_NATIVE(self)->doc))); + } else if (IS_CLOSURE(self) && AS_CLOSURE(self)->function->docstring) { + return OBJECT_VAL(AS_CLOSURE(self)->function->docstring); + } +}) -static KrkValue _bound_ip_to_line(int argc, KrkValue argv[], int hasKw) { - KrkBoundMethod * boundMethod = AS_BOUND_METHOD(argv[0]); - return _closure_ip_to_line(1, (KrkValue[]){OBJECT_VAL(boundMethod->method)}, hasKw); -} +KRK_METHOD(function,__name__,{ + METHOD_TAKES_NONE(); -static KrkValue _function_str(int argc, KrkValue argv[], int hasKw) { - KrkValue s = _function_get_name(argc, argv, hasKw); + if (IS_NATIVE(self)) { + return nativeFunctionName(self); + } else if (IS_CLOSURE(self) && AS_CLOSURE(self)->function->name) { + return OBJECT_VAL(AS_CLOSURE(self)->function->name); + } + + return OBJECT_VAL(S("")); +}) + +KRK_METHOD(function,__qualname__,{ + METHOD_TAKES_NONE(); + + if (IS_CLOSURE(self) && AS_CLOSURE(self)->function->qualname) { + return OBJECT_VAL(AS_CLOSURE(self)->function->qualname); + } +}) + +KRK_METHOD(function,_ip_to_line,{ + METHOD_TAKES_EXACTLY(1); + CHECK_ARG(1,int,krk_integer_type,ip); + + if (!IS_CLOSURE(self)) return NONE_VAL(); + + size_t line = krk_lineNumber(&AS_CLOSURE(self)->function->chunk, ip); + + return INTEGER_VAL(line); +}) + +KRK_METHOD(function,__str__,{ + METHOD_TAKES_NONE(); + + struct StringBuilder sb = {0}; + pushStringBuilderStr(&sb, "")); + + pushStringBuilderStr(&sb, AS_CSTRING(name), AS_STRING(name)->length); + + pushStringBuilderStr(&sb," at ", 4); + + char address[100]; + size_t len = snprintf(address, 100, "%p", (void*)AS_OBJECT(self)); + pushStringBuilderStr(&sb, address, len); + + pushStringBuilder(&sb,'>'); + + return finishStringBuilder(&sb); +}) + +KRK_METHOD(function,__file__,{ + METHOD_TAKES_NONE(); + + if (IS_NATIVE(self)) return OBJECT_VAL(S("")); + + return AS_CLOSURE(self)->function->chunk.filename ? + OBJECT_VAL(AS_CLOSURE(self)->function->chunk.filename) : + OBJECT_VAL(S("")); +}) + +KRK_METHOD(function,__args__,{ + if (!IS_CLOSURE(self)) return OBJECT_VAL(krk_newTuple(0)); + KrkFunction * _self = AS_CLOSURE(self)->function; + KrkTuple * tuple = krk_newTuple(_self->requiredArgs + _self->keywordArgs + _self->collectsArguments + _self->collectsKeywords); + krk_push(OBJECT_VAL(tuple)); + + for (short i = 0; i < _self->requiredArgs; ++i) { + tuple->values.values[tuple->values.count++] = _self->requiredArgNames.values[i]; + } + + for (short i = 0; i < _self->keywordArgs; ++i) { + struct StringBuilder sb = {0}; + pushStringBuilderStr(&sb, AS_CSTRING(_self->keywordArgNames.values[i]), AS_STRING(_self->keywordArgNames.values[i])->length); + pushStringBuilder(&sb,'='); + tuple->values.values[tuple->values.count++] = finishStringBuilder(&sb); + } + + if (_self->collectsArguments) { + struct StringBuilder sb = {0}; + pushStringBuilder(&sb, '*'); + pushStringBuilderStr(&sb, AS_CSTRING(_self->requiredArgNames.values[_self->requiredArgs]), AS_STRING(_self->requiredArgNames.values[_self->requiredArgs])->length); + tuple->values.values[tuple->values.count++] = finishStringBuilder(&sb); + } + + if (_self->collectsKeywords) { + struct StringBuilder sb = {0}; + pushStringBuilder(&sb, '*'); + pushStringBuilder(&sb, '*'); + pushStringBuilderStr(&sb, AS_CSTRING(_self->keywordArgNames.values[_self->keywordArgs]), AS_STRING(_self->keywordArgNames.values[_self->keywordArgs])->length); + tuple->values.values[tuple->values.count++] = finishStringBuilder(&sb); + } + + krk_tupleUpdateHash(tuple); + krk_pop(); + return OBJECT_VAL(tuple); +}) + +#undef CURRENT_CTYPE +#define CURRENT_CTYPE KrkFunction* + +KRK_METHOD(codeobject,__name__,{ + METHOD_TAKES_NONE(); + return self->name ? OBJECT_VAL(self->name) : OBJECT_VAL(S("")); +}) + +KRK_METHOD(codeobject,__str__,{ + METHOD_TAKES_NONE(); + KrkValue s = FUNC_NAME(codeobject,__name__)(1,argv,0); if (!IS_STRING(s)) return NONE_VAL(); krk_push(s); @@ -76,140 +158,127 @@ static KrkValue _function_str(int argc, KrkValue argv[], int hasKw) { snprintf(tmp, len, "", AS_CSTRING(s)); s = OBJECT_VAL(krk_copyString(tmp,len-1)); free(tmp); + krk_pop(); return s; -} +}) -/* function.__str__ / function.__repr__ */ -static KrkValue _closure_str(int argc, KrkValue argv[], int hasKw) { - KrkValue s = _closure_get_name(argc, argv, hasKw); +KRK_METHOD(codeobject,_ip_to_line,{ + METHOD_TAKES_EXACTLY(1); + CHECK_ARG(1,int,krk_integer_type,ip); + size_t line = krk_lineNumber(&self->chunk, ip); + return INTEGER_VAL(line); +}) + +#undef CURRENT_CTYPE +#define CURRENT_CTYPE KrkBoundMethod* + +KRK_METHOD(method,__name__,{ + METHOD_TAKES_NONE(); + return FUNC_NAME(function,__name__)(1,(KrkValue[]){OBJECT_VAL(self->method)},0); +}) + +KRK_METHOD(method,__qualname__,{ + METHOD_TAKES_NONE(); + return FUNC_NAME(function,__qualname__)(1,(KrkValue[]){OBJECT_VAL(self->method)},0); +}) + +KRK_METHOD(method,_ip_to_line,{ + METHOD_TAKES_EXACTLY(1); + return FUNC_NAME(function,_ip_to_line)(2,(KrkValue[]){OBJECT_VAL(self->method),argv[1]},0); +}) + +KRK_METHOD(method,__str__,{ + METHOD_TAKES_NONE(); + KrkValue s = FUNC_NAME(method,__qualname__)(1,argv,0); + if (!IS_STRING(s)) s = FUNC_NAME(method,__name__)(1,argv,0); if (!IS_STRING(s)) return NONE_VAL(); krk_push(s); - size_t len = AS_STRING(s)->length + sizeof(""); + KrkClass * type = krk_getType(self->receiver); + krk_push(self->receiver); + KrkValue reprVal = krk_callSimple(OBJECT_VAL(type->_reprer), 1, 0); + + size_t len = AS_STRING(s)->length + AS_STRING(reprVal)->length + sizeof("") + 1; char * tmp = malloc(len); - snprintf(tmp, len, "", AS_CSTRING(s)); + snprintf(tmp, len, "", AS_CSTRING(s), AS_CSTRING(reprVal)); s = OBJECT_VAL(krk_copyString(tmp,len-1)); free(tmp); krk_pop(); return s; -} +}) -/* method.__str__ / method.__repr__ */ -static KrkValue _bound_str(int argc, KrkValue argv[], int hasKw) { - KrkValue s = _bound_get_name(argc, argv, hasKw); - if (!IS_STRING(s)) return NONE_VAL(); - krk_push(s); +KRK_METHOD(method,__file__,{ + METHOD_TAKES_NONE(); + return FUNC_NAME(function,__file__)(1,(KrkValue[]){OBJECT_VAL(self->method)},0); +}) - const char * typeName = krk_typeName(AS_BOUND_METHOD(argv[0])->receiver); - - size_t len = AS_STRING(s)->length + sizeof("") + strlen(typeName) + 1; - char * tmp = malloc(len); - snprintf(tmp, len, "", typeName, AS_CSTRING(s)); - s = OBJECT_VAL(krk_copyString(tmp,len-1)); - free(tmp); - krk_pop(); - return s; -} - -/* function.__file__ */ -static KrkValue _closure_get_file(int argc, KrkValue argv[], int hasKw) { - if (!IS_CLOSURE(argv[0])) return OBJECT_VAL(S("")); - return AS_CLOSURE(argv[0])->function->chunk.filename ? OBJECT_VAL(AS_CLOSURE(argv[0])->function->chunk.filename) : OBJECT_VAL(S("")); -} - -/* method.__file__ */ -static KrkValue _bound_get_file(int argc, KrkValue argv[], int hasKw) { - KrkBoundMethod * boundMethod = AS_BOUND_METHOD(argv[0]); - return _closure_get_file(1, (KrkValue[]){OBJECT_VAL(boundMethod->method)}, 0); -} - -/* function.__args__ */ -static KrkValue _closure_get_argnames(int argc, KrkValue argv[], int hasKw) { - if (!IS_CLOSURE(argv[0])) return OBJECT_VAL(krk_newTuple(0)); - KrkFunction * self = AS_CLOSURE(argv[0])->function; - KrkTuple * tuple = krk_newTuple(self->requiredArgs + self->keywordArgs + self->collectsArguments + self->collectsKeywords); - krk_push(OBJECT_VAL(tuple)); - - for (short i = 0; i < self->requiredArgs; ++i) { - tuple->values.values[tuple->values.count++] = self->requiredArgNames.values[i]; - } - - for (short i = 0; i < self->keywordArgs; ++i) { - struct StringBuilder sb = {0}; - pushStringBuilderStr(&sb, AS_CSTRING(self->keywordArgNames.values[i]), AS_STRING(self->keywordArgNames.values[i])->length); - pushStringBuilder(&sb,'='); - tuple->values.values[tuple->values.count++] = finishStringBuilder(&sb); - } - - if (self->collectsArguments) { - struct StringBuilder sb = {0}; - pushStringBuilder(&sb, '*'); - pushStringBuilderStr(&sb, AS_CSTRING(self->requiredArgNames.values[self->requiredArgs]), AS_STRING(self->requiredArgNames.values[self->requiredArgs])->length); - tuple->values.values[tuple->values.count++] = finishStringBuilder(&sb); - } - - if (self->collectsKeywords) { - struct StringBuilder sb = {0}; - pushStringBuilder(&sb, '*'); - pushStringBuilder(&sb, '*'); - pushStringBuilderStr(&sb, AS_CSTRING(self->keywordArgNames.values[self->keywordArgs]), AS_STRING(self->keywordArgNames.values[self->keywordArgs])->length); - tuple->values.values[tuple->values.count++] = finishStringBuilder(&sb); - } - - krk_tupleUpdateHash(tuple); - krk_pop(); - return OBJECT_VAL(tuple); -} - -static KrkValue _bound_get_argnames(int argc, KrkValue argv[], int hasKw) { - KrkBoundMethod * boundMethod = AS_BOUND_METHOD(argv[0]); - return _closure_get_argnames(1, (KrkValue[]){OBJECT_VAL(boundMethod->method)}, 0); -} +KRK_METHOD(method,__args__,{ + METHOD_TAKES_NONE(); + return FUNC_NAME(function,__args__)(1,(KrkValue[]){OBJECT_VAL(self->method)},0); +}) +KRK_METHOD(method,__doc__,{ + METHOD_TAKES_NONE(); + return FUNC_NAME(function,__doc__)(1,(KrkValue[]){OBJECT_VAL(self->method)},0); +}) KRK_FUNC(staticmethod,{ FUNCTION_TAKES_EXACTLY(1); CHECK_ARG(0,CLOSURE,KrkClosure*,method); - method->function->isStaticMethod = 1; - return argv[0]; + /* Make a copy */ + krk_push(OBJECT_VAL(krk_newClosure(method->function))); + /* Copy upvalues */ + for (size_t i = 0; i < method->upvalueCount; ++i) { + AS_CLOSURE(krk_peek(0))->upvalues[i] = method->upvalues[i]; + } + AS_CLOSURE(krk_peek(0))->isStaticMethod = 1; + return krk_pop(); }) KRK_FUNC(classmethod,{ FUNCTION_TAKES_EXACTLY(1); CHECK_ARG(0,CLOSURE,KrkClosure*,method); - method->function->isClassMethod = 1; - return argv[0]; + /* Make a copy */ + krk_push(OBJECT_VAL(krk_newClosure(method->function))); + /* Copy upvalues */ + for (size_t i = 0; i < method->upvalueCount; ++i) { + AS_CLOSURE(krk_peek(0))->upvalues[i] = method->upvalues[i]; + } + AS_CLOSURE(krk_peek(0))->isClassMethod = 1; + return krk_pop(); }) _noexport void _createAndBind_functionClass(void) { - ADD_BASE_CLASS(vm.baseClasses->codeobjectClass, "codeobject", vm.baseClasses->objectClass); - krk_defineNative(&vm.baseClasses->codeobjectClass->methods, ".__str__", _function_str); - krk_defineNative(&vm.baseClasses->codeobjectClass->methods, ".__repr__", _function_str); - krk_defineNative(&vm.baseClasses->codeobjectClass->methods, ":__name__", _function_get_name); - krk_defineNative(&vm.baseClasses->codeobjectClass->methods, "_ip_to_line", _function_ip_to_line); - krk_finalizeClass(vm.baseClasses->codeobjectClass); + KrkClass * codeobject = ADD_BASE_CLASS(vm.baseClasses->codeobjectClass, "codeobject", vm.baseClasses->objectClass); + BIND_METHOD(codeobject,__str__); + BIND_METHOD(codeobject,_ip_to_line); + BIND_PROP(codeobject,__name__); + krk_defineNative(&codeobject->methods, ".__repr__", FUNC_NAME(codeobject,__str__)); + krk_finalizeClass(codeobject); - ADD_BASE_CLASS(vm.baseClasses->functionClass, "function", vm.baseClasses->objectClass); - krk_defineNative(&vm.baseClasses->functionClass->methods, ".__str__", _closure_str); - krk_defineNative(&vm.baseClasses->functionClass->methods, ".__repr__", _closure_str); - krk_defineNative(&vm.baseClasses->functionClass->methods, ":__doc__", _closure_get_doc); - krk_defineNative(&vm.baseClasses->functionClass->methods, ":__name__", _closure_get_name); - krk_defineNative(&vm.baseClasses->functionClass->methods, ":__file__", _closure_get_file); - krk_defineNative(&vm.baseClasses->functionClass->methods, ":__args__", _closure_get_argnames); - krk_defineNative(&vm.baseClasses->functionClass->methods, "_ip_to_line", _closure_ip_to_line); - krk_finalizeClass(vm.baseClasses->functionClass); + KrkClass * function = ADD_BASE_CLASS(vm.baseClasses->functionClass, "function", vm.baseClasses->objectClass); + BIND_METHOD(function,__str__); + BIND_METHOD(function,_ip_to_line); + BIND_PROP(function,__doc__); + BIND_PROP(function,__name__); + BIND_PROP(function,__qualname__); + BIND_PROP(function,__file__); + BIND_PROP(function,__args__); + krk_defineNative(&function->methods, ".__repr__", FUNC_NAME(function,__str__)); + krk_finalizeClass(function); - ADD_BASE_CLASS(vm.baseClasses->methodClass, "method", vm.baseClasses->objectClass); - krk_defineNative(&vm.baseClasses->methodClass->methods, ".__str__", _bound_str); - krk_defineNative(&vm.baseClasses->methodClass->methods, ".__repr__", _bound_str); - krk_defineNative(&vm.baseClasses->methodClass->methods, ".__doc__", _bound_get_doc); - krk_defineNative(&vm.baseClasses->methodClass->methods, ":__name__", _bound_get_name); - krk_defineNative(&vm.baseClasses->methodClass->methods, ":__file__", _bound_get_file); - krk_defineNative(&vm.baseClasses->methodClass->methods, ":__args__", _bound_get_argnames); - krk_defineNative(&vm.baseClasses->methodClass->methods, "_ip_to_line", _bound_ip_to_line); - krk_finalizeClass(vm.baseClasses->methodClass); + KrkClass * method = ADD_BASE_CLASS(vm.baseClasses->methodClass, "method", vm.baseClasses->objectClass); + BIND_METHOD(method,__str__); + BIND_METHOD(method,_ip_to_line); + BIND_PROP(method,__doc__); + BIND_PROP(method,__name__); + BIND_PROP(method,__qualname__); + BIND_PROP(method,__file__); + BIND_PROP(method,__args__); + krk_defineNative(&method->methods, ".__repr__", FUNC_NAME(method,__str__)); + krk_finalizeClass(method); BUILTIN_FUNCTION("staticmethod", FUNC_NAME(krk,staticmethod), "A static method does not take an implicit self or cls argument."); BUILTIN_FUNCTION("classmethod", FUNC_NAME(krk,classmethod), "A class method takes an implicit cls argument, instead of self."); diff --git a/src/object.h b/src/object.h index 835adaf..e456df4 100644 --- a/src/object.h +++ b/src/object.h @@ -123,10 +123,9 @@ typedef struct { KrkLocalEntry * localNames; unsigned char collectsArguments:1; unsigned char collectsKeywords:1; - unsigned char isClassMethod:1; unsigned char isGenerator:1; - unsigned char isStaticMethod:1; struct KrkInstance * globalsContext; + KrkString * qualname; } KrkFunction; /** @@ -140,6 +139,8 @@ typedef struct { KrkFunction * function; KrkUpvalue ** upvalues; size_t upvalueCount; + unsigned char isClassMethod:1; + unsigned char isStaticMethod:1; } KrkClosure; typedef void (*KrkCleanupCallback)(struct KrkInstance *); diff --git a/src/vm.c b/src/vm.c index ad7e100..a4c590d 100644 --- a/src/vm.c +++ b/src/vm.c @@ -907,9 +907,9 @@ int krk_bindMethod(KrkClass * _class, KrkString * name) { if (!_class) return 0; if (IS_NATIVE(method) && ((KrkNative*)AS_OBJECT(method))->isMethod == 2) { out = AS_NATIVE(method)->function(1, (KrkValue[]){krk_peek(0)}, 0); - } else if (IS_CLOSURE(method) && (AS_CLOSURE(method)->function->isClassMethod)) { + } else if (IS_CLOSURE(method) && (AS_CLOSURE(method)->isClassMethod)) { out = OBJECT_VAL(krk_newBoundMethod(OBJECT_VAL(_class), AS_OBJECT(method))); - } else if (IS_CLOSURE(method) && (AS_CLOSURE(method)->function->isStaticMethod)) { + } else if (IS_CLOSURE(method) && (AS_CLOSURE(method)->isStaticMethod)) { out = method; } else if (IS_CLOSURE(method) || IS_NATIVE(method)) { out = OBJECT_VAL(krk_newBoundMethod(krk_peek(0), AS_OBJECT(method))); @@ -1744,7 +1744,7 @@ static int valueGetProperty(KrkString * name) { _class = _class->base; } if (_class) { - if (IS_CLOSURE(value) && AS_CLOSURE(value)->function->isClassMethod) { + if (IS_CLOSURE(value) && AS_CLOSURE(value)->isClassMethod) { value = OBJECT_VAL(krk_newBoundMethod(krk_peek(0), AS_OBJECT(value))); } krk_pop(); diff --git a/test/test.krk b/test/test.krk index 57ea027..b1c405f 100755 --- a/test/test.krk +++ b/test/test.krk @@ -112,7 +112,7 @@ let test = Test() test.doAThing() test.foo = "bar" print(test.foo) -print(test.doAThing) +print(' yay: bax bar - +True yay: bar This is a great teapot! Subclass says: (I am a teapot) diff --git a/test/testClassMethodAssignment.krk b/test/testClassMethodAssignment.krk index 74e6541..21784eb 100644 --- a/test/testClassMethodAssignment.krk +++ b/test/testClassMethodAssignment.krk @@ -14,10 +14,10 @@ def noargs(): Foo.other = other Foo.noargs = noargs -print(f.other) +print(' +True Called other - +True noargs() takes exactly 0 arguments (1 given) I can be called diff --git a/test/testComplexArgsNative.krk b/test/testComplexArgsNative.krk index e23bd49..dc65a21 100644 --- a/test/testComplexArgsNative.krk +++ b/test/testComplexArgsNative.krk @@ -1,4 +1,4 @@ -print(*[1,2,3],*['test',object,lambda x: x * 5],7,9,10,end=';\n',**{'sep':', '}) +print(*[1,2,3],*['test',object],7,9,10,end=';\n',**{'sep':', '}) let l = [1,2,3] let d = {'sep': ', '} diff --git a/test/testComplexArgsNative.krk.expect b/test/testComplexArgsNative.krk.expect index dee5166..cabe342 100644 --- a/test/testComplexArgsNative.krk.expect +++ b/test/testComplexArgsNative.krk.expect @@ -1,4 +1,4 @@ -1, 2, 3, test, , >, 7, 9, 10; +1, 2, 3, test, , 7, 9, 10; a, b, 1, 2, 3, 7, 1, 2, 3test 1 apples got multiple values for argument 'a' diff --git a/test/testDecorators.krk b/test/testDecorators.krk index ec7f5be..3e1cb68 100644 --- a/test/testDecorators.krk +++ b/test/testDecorators.krk @@ -73,7 +73,7 @@ print("Returned from f.decorated()") def decoratorWithArgument(decoratorArg): def theActualDecorator(func): - print("I am decorating",func,"with this argument:",decoratorArg) + print("I am decorating",func.__qualname__,"with this argument:",decoratorArg) def wrapped(): print("This wrapper captured this decorator arg:", decoratorArg) func() @@ -88,7 +88,7 @@ decoratedWithArgument() def methodDecoratorWithArguments(decoratorArgA, decoratorArgB): def theActualDecorator(method): - print("I am decorating", method, "with these args:", decoratorArgA, decoratorArgB) + print("I am decorating", method.__qualname__, "with these args:", decoratorArgA, decoratorArgB) def wrapped(instance, wrapperArg): print("I captured these from the decorator:", decoratorArgA, decoratorArgB) print("I also take my own argument:", wrapperArg) diff --git a/test/testDecorators.krk.expect b/test/testDecorators.krk.expect index 9ccb246..7f14007 100644 --- a/test/testDecorators.krk.expect +++ b/test/testDecorators.krk.expect @@ -22,10 +22,10 @@ I need an extra arg: butts (decorated) self.foo = bar Done. Returned from f.decorated() -I am decorating with this argument: foo +I am decorating decoratedWithArgument with this argument: foo This wrapper captured this decorator arg: foo I don't take any args of mine own, and nor does my wrapper. -I am decorating with these args: foo bar +I am decorating Bar.superDecoratedMethod with these args: foo bar I captured these from the decorator: foo bar I also take my own argument: this arg goes to the wrapper I am method, so I can access myself weee and also my own argument which is now and modify this one diff --git a/test/testDefaultArgs.krk b/test/testDefaultArgs.krk index 25a86dc..a633034 100644 --- a/test/testDefaultArgs.krk +++ b/test/testDefaultArgs.krk @@ -4,7 +4,7 @@ def foo(default="bacon"): print("hello") return test -print(foo()) +print('.test' in str(foo())) foo("sports") def fillValues(a=1,b=2,c="c",d=None,e=2.71828): diff --git a/test/testDefaultArgs.krk.expect b/test/testDefaultArgs.krk.expect index a46aba3..38540b7 100644 --- a/test/testDefaultArgs.krk.expect +++ b/test/testDefaultArgs.krk.expect @@ -1,5 +1,5 @@ You like bacon right? - +True You like sports right? 1 True c None 2.71828 one 2 test None diff --git a/test/testList.krk b/test/testList.krk index fe60e75..034dc0a 100644 --- a/test/testList.krk +++ b/test/testList.krk @@ -4,11 +4,6 @@ l.append(1) l.append(2) l.append("string") l.append(3.14159) - -def foo(): - print("bar") - -l.append(foo) l.append("world") for v in l: diff --git a/test/testList.krk.expect b/test/testList.krk.expect index 4d22484..ecc69e6 100644 --- a/test/testList.krk.expect +++ b/test/testList.krk.expect @@ -2,5 +2,4 @@ 2 string 3.14159 - world diff --git a/test/testStrReprOrdering.krk.expect b/test/testStrReprOrdering.krk.expect index 25cca39..5fc3d5d 100644 --- a/test/testStrReprOrdering.krk.expect +++ b/test/testStrReprOrdering.krk.expect @@ -1,5 +1,5 @@ Foo: - - +> +> diff --git a/test/testUnicodeIdentifiers.krk b/test/testUnicodeIdentifiers.krk index 035b124..0cedb88 100644 --- a/test/testUnicodeIdentifiers.krk +++ b/test/testUnicodeIdentifiers.krk @@ -7,4 +7,4 @@ def テスト(引数="こんにちは"): let おはよう = "おはようございます!" テスト(おはよう) -print(テスト) +print(' +True diff --git a/test/testUpvalue.krk.expect b/test/testUpvalue.krk.expect index 070d7c3..a67a1a5 100644 --- a/test/testUpvalue.krk.expect +++ b/test/testUpvalue.krk.expect @@ -1 +1 @@ -I have a module: