Add __qualname__ to classes

This commit is contained in:
K. Lange 2021-02-25 10:03:52 +09:00
parent 1bf238ebc1
commit 00fce076fe
5 changed files with 82 additions and 16 deletions

View File

@ -95,6 +95,7 @@ typedef enum {
TYPE_STATIC,
TYPE_PROPERTY,
TYPE_CLASS,
TYPE_CLASSMETHOD,
} FunctionType;
typedef struct Compiler {
@ -307,7 +308,7 @@ static void emitReturn() {
} else if (current->type == TYPE_MODULE) {
/* Un-pop the last stack value */
emitBytes(OP_GET_LOCAL, 0);
} else if (current->type != TYPE_LAMBDA) {
} else if (current->type != TYPE_LAMBDA && current->type != TYPE_CLASS) {
emitByte(OP_NONE);
}
emitByte(OP_RETURN);
@ -1010,13 +1011,52 @@ static void method(size_t blockWidth) {
}
}
#define ATTACH_PROPERTY(propName,how,propValue) do { \
emitBytes(OP_DUP, 0); \
KrkToken val_tok = syntheticToken(propValue); \
size_t val_ind = identifierConstant(&val_tok); \
EMIT_CONSTANT_OP(how, val_ind); \
KrkToken name_tok = syntheticToken(propName); \
size_t name_ind = identifierConstant(&name_tok); \
EMIT_CONSTANT_OP(OP_SET_PROPERTY, name_ind); \
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("<locals>.");
}
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` */
consume(TOKEN_IDENTIFIER, "Expected class name.");
Compiler subcompiler;
initCompiler(&subcompiler, TYPE_LAMBDA);
initCompiler(&subcompiler, TYPE_CLASS);
subcompiler.function->chunk.filename = subcompiler.enclosing->function->chunk.filename;
beginScope();
@ -1063,14 +1103,10 @@ static KrkToken classDeclaration() {
consume(TOKEN_COLON, "Expected colon after class");
emitBytes(OP_DUP, 0);
KrkToken name_tok = syntheticToken("__name__");
size_t name_ind = identifierConstant(&name_tok);
EMIT_CONSTANT_OP(OP_GET_GLOBAL, name_ind);
KrkToken module_tok = syntheticToken("__module__");
size_t module_ind = identifierConstant(&module_tok);
EMIT_CONSTANT_OP(OP_SET_PROPERTY, module_ind);
emitByte(OP_POP);
/* Set Class.__module__ to the value of __name__, which is the string
* name of the current module. */
ATTACH_PROPERTY("__module__", OP_GET_GLOBAL, "__name__");
ATTACH_PROPERTY("__qualname__", OP_CONSTANT, calculateQualName());
if (match(TOKEN_EOL)) {
if (check(TOKEN_INDENTATION)) {
@ -1186,7 +1222,7 @@ static KrkToken decorator(size_t level, FunctionType type) {
return funcName;
}
advance();
type = TYPE_CLASS;
type = TYPE_CLASSMETHOD;
} else {
/* Collect an identifier */
expression();
@ -1234,7 +1270,7 @@ static KrkToken decorator(size_t level, FunctionType type) {
size_t ind = identifierConstant(&funcName);
EMIT_CONSTANT_OP(OP_SET_PROPERTY, ind);
emitByte(OP_POP);
} else if (type == TYPE_CLASS) {
} else if (type == TYPE_CLASSMETHOD) {
emitByte(OP_CREATE_CLASSMETHOD);
size_t ind = identifierConstant(&funcName);
EMIT_CONSTANT_OP(OP_METHOD, ind);

View File

@ -41,15 +41,19 @@ static KrkValue _class_to_str(int argc, KrkValue argv[], int hasKw) {
KrkValue module = NONE_VAL();
krk_tableGet(&AS_CLASS(argv[0])->fields, OBJECT_VAL(S("__module__")), &module);
KrkValue qualname = NONE_VAL();
krk_tableGet(&AS_CLASS(argv[0])->fields, OBJECT_VAL(S("__qualname__")), &qualname);
KrkString * name = IS_STRING(qualname) ? AS_STRING(qualname) : AS_CLASS(argv[0])->name;
int includeModule = !(IS_NONE(module) || (IS_STRING(module) && AS_STRING(module) == S("__builtins__")));
size_t allocSize = sizeof("<class ''>") + AS_CLASS(argv[0])->name->length;
size_t allocSize = sizeof("<class ''>") + name->length;
if (IS_STRING(module)) allocSize += AS_STRING(module)->length + 1;
char * tmp = malloc(allocSize);
size_t l = snprintf(tmp, allocSize, "<class '%s%s%s'>",
includeModule ? AS_CSTRING(module) : "",
includeModule ? "." : "",
AS_CLASS(argv[0])->name->chars);
name->chars);
KrkString * out = krk_copyString(tmp,l);
free(tmp);
return OBJECT_VAL(out);

View File

@ -1,3 +1,3 @@
['__init__', '__str__', '__repr__', '__getattr__', '__class__', '__dir__', '__hash__', '__func__', '__module__', '_dict']
['__init__', '__str__', '__repr__', '__getattr__', '__class__', '__dir__', '__hash__', '__qualname__', '__func__', '__module__', '_dict']
1
['__class__', '__str__', '__dir__', '__repr__', '__hash__', '__func__', '__module__', 'butts']
['__class__', '__str__', '__dir__', '__repr__', '__hash__', '__qualname__', '__func__', '__module__', 'butts']

20
test/testQualname.krk Normal file
View File

@ -0,0 +1,20 @@
class TopLevel():
pass
class TopOfNested():
def func(self):
class Inner():
pass
return Inner
def topLevelFunc():
class OtherInner():
pass
return OtherInner
print(TopLevel.__qualname__)
print(TopOfNested.__qualname__)
print(TopOfNested().func().__qualname__)
print(TopOfNested().func())
print(topLevelFunc().__qualname__)
print(topLevelFunc())

View File

@ -0,0 +1,6 @@
TopLevel
TopOfNested
TopOfNested.func.<locals>.Inner
<class '__main__.TopOfNested.func.<locals>.Inner'>
topLevelFunc.<locals>.OtherInner
<class '__main__.topLevelFunc.<locals>.OtherInner'>