Remove the distinction between a class's 'fields' and 'methods' and implement actual method resolution

This commit is contained in:
K. Lange 2021-03-09 22:35:40 +09:00
parent 1530e5338f
commit 84b7a37fab
21 changed files with 241 additions and 163 deletions

View File

@ -8,6 +8,9 @@
static KrkClass * Helper;
static KrkClass * LicenseReader;
FUNC_SIG(list,__init__);
FUNC_SIG(list,sort);
KrkValue krk_dirObject(int argc, KrkValue argv[], int hasKw) {
if (argc != 1) return krk_runtimeError(vm.exceptions->argumentError, "wrong number of arguments or bad type, got %d\n", argc);
@ -18,51 +21,52 @@ KrkValue krk_dirObject(int argc, KrkValue argv[], int hasKw) {
if (IS_INSTANCE(argv[0])) {
/* Obtain self-reference */
KrkInstance * self = AS_INSTANCE(argv[0]);
/* First add each method of the class */
for (size_t i = 0; i < self->_class->methods.capacity; ++i) {
if (self->_class->methods.entries[i].key.type != VAL_KWARGS) {
krk_writeValueArray(AS_LIST(myList),
self->_class->methods.entries[i].key);
}
}
/* Then add each field of the instance */
for (size_t i = 0; i < self->fields.capacity; ++i) {
if (self->fields.entries[i].key.type != VAL_KWARGS) {
krk_writeValueArray(AS_LIST(myList),
self->fields.entries[i].key);
}
}
} else {
if (IS_CLASS(argv[0])) {
KrkClass * _class = AS_CLASS(argv[0]);
} else if (IS_CLASS(argv[0])) {
KrkClass * _class = AS_CLASS(argv[0]);
while (_class) {
for (size_t i = 0; i < _class->methods.capacity; ++i) {
if (_class->methods.entries[i].key.type != VAL_KWARGS) {
krk_writeValueArray(AS_LIST(myList),
_class->methods.entries[i].key);
}
}
for (size_t i = 0; i < _class->fields.capacity; ++i) {
if (_class->fields.entries[i].key.type != VAL_KWARGS) {
krk_writeValueArray(AS_LIST(myList),
_class->fields.entries[i].key);
}
}
_class = _class->base;
}
KrkClass * type = krk_getType(argv[0]);
}
KrkClass * type = krk_getType(argv[0]);
while (type) {
for (size_t i = 0; i < type->methods.capacity; ++i) {
if (type->methods.entries[i].key.type != VAL_KWARGS) {
krk_writeValueArray(AS_LIST(myList),
type->methods.entries[i].key);
}
}
type = type->base;
}
/* Prepare output value */
/* Throw it at a set to get unique, unordered results */
krk_push(krk_set_of(AS_LIST(myList)->count, AS_LIST(myList)->values, 0));
krk_swap(1);
krk_pop();
return myList;
/* Now build a fresh list */
myList = krk_list_of(0,NULL,0);
krk_push(myList);
krk_swap(1);
FUNC_NAME(list,__init__)(2,(KrkValue[]){krk_peek(1), krk_peek(0)},0);
FUNC_NAME(list,sort)(1,(KrkValue[]){krk_peek(1)},0);
krk_pop();
return krk_pop();
}
@ -674,7 +678,21 @@ KRK_FUNC(getattr,{
FUNCTION_TAKES_AT_LEAST(2);
KrkValue object = argv[0];
CHECK_ARG(1,str,KrkString*,property);
return krk_valueGetAttribute(object, property->chars);
KrkValue result = krk_valueGetAttribute(object, property->chars);
if (argc == 3 && krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION &&
krk_isInstanceOf(krk_currentThread.currentException, vm.exceptions->attributeError)) {
krk_currentThread.flags &= ~(KRK_THREAD_HAS_EXCEPTION);
result = argv[2];
}
return result;
})
KRK_FUNC(setattr,{
FUNCTION_TAKES_EXACTLY(3);
KrkValue object = argv[0];
CHECK_ARG(1,str,KrkString*,property);
KrkValue value = argv[2];
return krk_valueSetAttribute(object, property->chars, value);
})
@ -727,6 +745,14 @@ KRK_METHOD(LicenseReader,__call__,{
return krk_runtimeError(vm.exceptions->typeError, "unexpected error");
})
static KrkValue _property_init(int argc, KrkValue argv[], int hasKw) {
if (argc != 2) {
return krk_runtimeError(vm.exceptions->notImplementedError, "Additional arguments to @property() are not supported.");
}
return OBJECT_VAL(krk_newProperty(argv[1]));
}
static KrkValue _property_repr(int argc, KrkValue argv[], int hasKw) {
if (argc != 1 || !IS_PROPERTY(argv[0])) return krk_runtimeError(vm.exceptions->typeError, "?");
struct StringBuilder sb = {0};
@ -828,7 +854,8 @@ void _createAndBind_builtins(void) {
"attaching new properties to the @c \\__builtins__ instance."
);
krk_makeClass(vm.builtins, &vm.baseClasses->propertyClass, "Property", vm.baseClasses->objectClass);
krk_makeClass(vm.builtins, &vm.baseClasses->propertyClass, "property", vm.baseClasses->objectClass);
krk_defineNative(&vm.baseClasses->propertyClass->methods, ".__init__", _property_init);
krk_defineNative(&vm.baseClasses->propertyClass->methods, ".__repr__", _property_repr);
krk_defineNative(&vm.baseClasses->propertyClass->methods, ":__doc__", _property_doc);
krk_defineNative(&vm.baseClasses->propertyClass->methods, ":__name__", _property_name);
@ -887,6 +914,7 @@ void _createAndBind_builtins(void) {
BUILTIN_FUNCTION("any", _any, "Returns True if at least one element in the given iterable is truthy, False otherwise.");
BUILTIN_FUNCTION("all", _all, "Returns True if every element in the given iterable is truthy, False otherwise.");
BUILTIN_FUNCTION("getattr", FUNC_NAME(krk,getattr), "Obtain a property of an object as if it were accessed by the dot operator.");
BUILTIN_FUNCTION("setattr", FUNC_NAME(krk,setattr), "Set a property of an object as if it were accessed by the dot operator.");
BUILTIN_FUNCTION("sum", _sum, "Add the elements of an iterable.");
BUILTIN_FUNCTION("min", _min, "Return the lowest value in an iterable or the passed arguments.");
BUILTIN_FUNCTION("max", _max, "Return the highest value in an iterable or the passed arguments.");

View File

@ -32,7 +32,6 @@ typedef enum {
OP_CALL_STACK,
OP_CLEANUP_WITH,
OP_CLOSE_UPVALUE,
OP_CREATE_PROPERTY,
OP_DIVIDE,
OP_DOCSTRING,
OP_EQUAL,
@ -63,7 +62,6 @@ typedef enum {
OP_SWAP,
OP_TRUE,
OP_FILTER_EXCEPT,
OP_CREATE_CLASSMETHOD,
OP_INVOKE_ITER,
OP_INVOKE_CONTAINS,
OP_BREAKPOINT, /* NEVER output this instruction in the compiler or bad things can happen */
@ -88,7 +86,7 @@ typedef enum {
OP_IMPORT_FROM,
OP_INC,
OP_KWARGS,
OP_METHOD,
OP_CLASS_PROPERTY,
OP_SET_GLOBAL,
OP_SET_LOCAL,
OP_SET_PROPERTY,
@ -121,7 +119,7 @@ typedef enum {
OP_IMPORT_FROM_LONG,
OP_INC_LONG,
OP_KWARGS_LONG,
OP_METHOD_LONG,
OP_CLASS_PROPERTY_LONG,
OP_SET_GLOBAL_LONG,
OP_SET_LOCAL_LONG,
OP_SET_PROPERTY_LONG,

View File

@ -103,6 +103,11 @@ typedef enum {
TYPE_CLASSMETHOD,
} FunctionType;
struct IndexWithNext {
size_t ind;
struct IndexWithNext * next;
};
typedef struct Compiler {
struct Compiler * enclosing;
KrkFunction * function;
@ -123,6 +128,8 @@ typedef struct Compiler {
int * continues;
size_t localNameCapacity;
struct IndexWithNext * properties;
} Compiler;
typedef struct ClassCompiler {
@ -167,6 +174,7 @@ static void initCompiler(Compiler * compiler, FunctionType type) {
compiler->continues = NULL;
compiler->loopLocalCount = 0;
compiler->localNameCapacity = 0;
compiler->properties = NULL;
if (type != TYPE_MODULE) {
current->function->name = krk_copyString(parser.previous.start, parser.previous.length);
@ -181,6 +189,14 @@ static void initCompiler(Compiler * compiler, FunctionType type) {
}
}
static void rememberClassProperty(size_t ind) {
struct IndexWithNext * me = malloc(sizeof(struct IndexWithNext));
me->ind = ind;
me->next = current->properties;
current->properties = me;
}
static void parsePrecedence(Precedence precedence);
static ssize_t parseVariable(const char * errorMessage);
static void variable(int canAssign);
@ -379,6 +395,12 @@ static void freeCompiler(Compiler * compiler) {
FREE_ARRAY(Upvalue,compiler->upvalues, compiler->upvaluesSpace);
FREE_ARRAY(int,compiler->breaks, compiler->breakSpace);
FREE_ARRAY(int,compiler->continues, compiler->continueSpace);
while (compiler->properties) {
void * tmp = compiler->properties;
compiler->properties = compiler->properties->next;
free(tmp);
}
}
static size_t emitConstant(KrkValue value) {
@ -989,12 +1011,11 @@ static void method(size_t blockWidth) {
if (check(TOKEN_AT)) {
decorator(0, TYPE_METHOD);
} else if (match(TOKEN_IDENTIFIER)) {
emitBytes(OP_DUP, 0); /* SET_PROPERTY will pop class */
size_t ind = identifierConstant(&parser.previous);
consume(TOKEN_EQUAL, "Class field must have value.");
expression();
EMIT_CONSTANT_OP(OP_SET_PROPERTY, ind);
emitByte(OP_POP); /* Value of expression replaces dup of class*/
rememberClassProperty(ind);
EMIT_CONSTANT_OP(OP_CLASS_PROPERTY, ind);
if (!match(TOKEN_EOL) && !match(TOKEN_EOF)) {
errorAtCurrent("Expected end of line after class attribut declaration");
}
@ -1012,7 +1033,8 @@ static void method(size_t blockWidth) {
}
function(type, blockWidth);
EMIT_CONSTANT_OP(OP_METHOD, ind);
rememberClassProperty(ind);
EMIT_CONSTANT_OP(OP_CLASS_PROPERTY, ind);
}
}
@ -1199,40 +1221,14 @@ static KrkToken decorator(size_t level, FunctionType type) {
advance(); /* Collect the `@` */
KrkToken funcName = {0};
int haveCallable = 0;
/* hol'up, let's special case some stuff */
KrkToken at_staticmethod = syntheticToken("staticmethod");
KrkToken at_property = syntheticToken("property");
KrkToken at_classmethod = syntheticToken("classmethod");
if (identifiersEqual(&at_staticmethod, &parser.current)) {
if (level != 0 || type != TYPE_METHOD) {
error("Invalid use of @staticmethod, which must be the top decorator of a class method.");
return funcName;
}
advance();
type = TYPE_STATIC;
emitBytes(OP_DUP, 0); /* SET_PROPERTY will pop class */
} else if (identifiersEqual(&at_property, &parser.current)) {
if (level != 0 || type != TYPE_METHOD) {
error("Invalid use of @property, which must be the top decorator of a class method.");
return funcName;
}
advance();
type = TYPE_PROPERTY;
emitBytes(OP_DUP, 0);
} else if (identifiersEqual(&at_classmethod, &parser.current)) {
if (level != 0 || type != TYPE_METHOD) {
error("Invalid use of @classmethod, which must be the top decorator of a class method.");
return funcName;
}
advance();
type = TYPE_CLASSMETHOD;
} else {
/* Collect an identifier */
expression();
haveCallable = 1;
}
KrkToken at_classmethod = syntheticToken("classmethod");
if (identifiersEqual(&at_staticmethod, &parser.current)) type = TYPE_STATIC;
if (identifiersEqual(&at_classmethod, &parser.current)) type = TYPE_CLASSMETHOD;
expression();
consume(TOKEN_EOL, "Expected line feed after decorator.");
if (blockWidth) {
@ -1262,8 +1258,7 @@ static KrkToken decorator(size_t level, FunctionType type) {
return funcName;
}
if (haveCallable)
emitBytes(OP_CALL, 1);
emitBytes(OP_CALL, 1);
if (level == 0) {
if (type == TYPE_FUNCTION) {
@ -1271,22 +1266,10 @@ static KrkToken decorator(size_t level, FunctionType type) {
declareVariable();
size_t ind = (current->scopeDepth > 0) ? 0 : identifierConstant(&funcName);
defineVariable(ind);
} else if (type == TYPE_STATIC) {
size_t ind = identifierConstant(&funcName);
EMIT_CONSTANT_OP(OP_SET_PROPERTY, ind);
emitByte(OP_POP);
} else if (type == TYPE_CLASSMETHOD) {
emitByte(OP_CREATE_CLASSMETHOD);
size_t ind = identifierConstant(&funcName);
EMIT_CONSTANT_OP(OP_METHOD, ind);
} else if (type == TYPE_PROPERTY) {
emitByte(OP_CREATE_PROPERTY);
size_t ind = identifierConstant(&funcName);
EMIT_CONSTANT_OP(OP_SET_PROPERTY, ind);
emitByte(OP_POP);
} else {
size_t ind = identifierConstant(&funcName);
EMIT_CONSTANT_OP(OP_METHOD, ind);
rememberClassProperty(ind);
EMIT_CONSTANT_OP(OP_CLASS_PROPERTY, ind);
}
}
@ -2076,6 +2059,20 @@ static ssize_t resolveUpvalue(Compiler * compiler, KrkToken * name) {
} } while (0)
static void namedVariable(KrkToken name, int canAssign) {
if (current->type == TYPE_CLASS) {
/* Only at the class body level, see if this is a class property. */
struct IndexWithNext * properties = current->properties;
while (properties) {
KrkString * constant = AS_STRING(currentChunk()->constants.values[properties->ind]);
if (constant->length == name.length && !memcmp(constant->chars, name.start, name.length)) {
ssize_t arg = properties->ind;
EMIT_CONSTANT_OP(OP_GET_LOCAL, 0);
DO_VARIABLE(OP_SET_PROPERTY, OP_GET_PROPERTY, OP_NONE);
return;
}
properties = properties->next;
}
}
ssize_t arg = resolveLocal(current, &name);
if (arg != -1) {
DO_VARIABLE(OP_SET_LOCAL, OP_GET_LOCAL, OP_NONE);

View File

@ -576,7 +576,7 @@ KRK_FUNC(dis,{
}
} else if (IS_CLASS(argv[0])) {
KrkValue code;
if (krk_tableGet(&AS_CLASS(argv[0])->fields, OBJECT_VAL(S("__func__")), &code) && IS_CLOSURE(code)) {
if (krk_tableGet(&AS_CLASS(argv[0])->methods, OBJECT_VAL(S("__func__")), &code) && IS_CLOSURE(code)) {
KrkFunction * func = AS_CLOSURE(code)->function;
krk_disassembleCodeObject(stdout, func, AS_CLASS(argv[0])->name->chars);
}

View File

@ -63,7 +63,6 @@ static void freeObject(KrkObj * object) {
case OBJ_CLASS: {
KrkClass * _class = (KrkClass*)object;
krk_freeTable(&_class->methods);
krk_freeTable(&_class->fields);
FREE(KrkClass, object);
break;
}
@ -163,7 +162,6 @@ static void blackenObject(KrkObj * object) {
krk_markObject((KrkObj*)_class->docstring);
krk_markObject((KrkObj*)_class->base);
krk_markTable(&_class->methods);
krk_markTable(&_class->fields);
break;
}
case OBJ_INSTANCE: {

View File

@ -39,10 +39,10 @@ static KrkValue _class_to_str(int argc, KrkValue argv[], int hasKw) {
/* Determine if this class has a module */
KrkValue module = NONE_VAL();
krk_tableGet(&AS_CLASS(argv[0])->fields, OBJECT_VAL(S("__module__")), &module);
krk_tableGet(&AS_CLASS(argv[0])->methods, OBJECT_VAL(S("__module__")), &module);
KrkValue qualname = NONE_VAL();
krk_tableGet(&AS_CLASS(argv[0])->fields, OBJECT_VAL(S("__qualname__")), &qualname);
krk_tableGet(&AS_CLASS(argv[0])->methods, 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__")));

View File

@ -98,13 +98,14 @@ static KrkValue _closure_str(int argc, KrkValue argv[], int hasKw) {
/* 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);
const char * typeName = krk_typeName(AS_BOUND_METHOD(argv[0])->receiver);
size_t len = AS_STRING(s)->length + sizeof("<method >") + strlen(typeName) + 1;
size_t len = AS_STRING(s)->length + sizeof("<bound method >") + strlen(typeName) + 1;
char * tmp = malloc(len);
snprintf(tmp, len, "<method %s.%s>", typeName, AS_CSTRING(s));
snprintf(tmp, len, "<bound method %s.%s>", typeName, AS_CSTRING(s));
s = OBJECT_VAL(krk_copyString(tmp,len-1));
free(tmp);
krk_pop();
@ -167,6 +168,20 @@ static KrkValue _bound_get_argnames(int argc, KrkValue argv[], int hasKw) {
}
KRK_FUNC(staticmethod,{
FUNCTION_TAKES_EXACTLY(1);
CHECK_ARG(0,CLOSURE,KrkClosure*,method);
method->function->isStaticMethod = 1;
return argv[0];
})
KRK_FUNC(classmethod,{
FUNCTION_TAKES_EXACTLY(1);
CHECK_ARG(0,CLOSURE,KrkClosure*,method);
method->function->isClassMethod = 1;
return argv[0];
})
_noexport
void _createAndBind_functionClass(void) {
ADD_BASE_CLASS(vm.baseClasses->codeobjectClass, "codeobject", vm.baseClasses->objectClass);
@ -195,4 +210,7 @@ void _createAndBind_functionClass(void) {
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);
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.");
}

View File

@ -9,6 +9,9 @@
if (index < 0 || index >= (krk_integer_type)self->values.count) return krk_runtimeError(vm.exceptions->indexError, "tuple index out of range: " PRIkrk_int, index)
static KrkValue _tuple_init(int argc, KrkValue argv[], int hasKw) {
if (argc == 1) {
return OBJECT_VAL(krk_newTuple(0));
}
return krk_runtimeError(vm.exceptions->typeError,"tuple() initializier unsupported");
}

View File

@ -289,19 +289,12 @@ KrkClass * krk_newClass(KrkString * name, KrkClass * baseClass) {
_class->name = name;
_class->allocSize = sizeof(KrkInstance);
krk_initTable(&_class->methods);
krk_initTable(&_class->fields);
if (baseClass) {
krk_push(OBJECT_VAL(_class));
_class->base = baseClass;
_class->allocSize = baseClass->allocSize;
_class->_ongcscan = baseClass->_ongcscan;
_class->_ongcsweep = baseClass->_ongcsweep;
krk_tableAddAll(&baseClass->methods, &_class->methods);
krk_tableAddAll(&baseClass->fields, &_class->fields);
krk_pop();
}
return _class;
@ -311,11 +304,6 @@ KrkInstance * krk_newInstance(KrkClass * _class) {
KrkInstance * instance = (KrkInstance*)allocateObject(_class->allocSize, OBJ_INSTANCE);
instance->_class = _class;
krk_initTable(&instance->fields);
if (_class) {
krk_push(OBJECT_VAL(instance));
krk_tableAddAll(&_class->fields, &instance->fields);
krk_pop();
}
return instance;
}

View File

@ -126,6 +126,7 @@ typedef struct {
unsigned char collectsKeywords:1;
unsigned char isClassMethod:1;
unsigned char isGenerator:1;
unsigned char isStaticMethod:1;
struct KrkInstance * globalsContext;
} KrkFunction;
@ -158,7 +159,6 @@ typedef struct KrkClass {
KrkString * docstring;
struct KrkClass * base;
KrkTable methods;
KrkTable fields;
size_t allocSize;
KrkCleanupCallback _ongcscan;
KrkCleanupCallback _ongcsweep;

View File

@ -36,10 +36,8 @@ SIMPLE(OP_SWAP)
SIMPLE(OP_FINALIZE)
SIMPLE(OP_IS)
SIMPLE(OP_POW)
SIMPLE(OP_CREATE_PROPERTY)
SIMPLE(OP_CLEANUP_WITH)
SIMPLE(OP_FILTER_EXCEPT)
SIMPLE(OP_CREATE_CLASSMETHOD)
SIMPLE(OP_BREAKPOINT)
SIMPLE(OP_YIELD)
CONSTANT(OP_DEFINE_GLOBAL,(void)0)
@ -51,7 +49,7 @@ CONSTANT(OP_CLASS,(void)0)
CONSTANT(OP_GET_PROPERTY, (void)0)
CONSTANT(OP_SET_PROPERTY, (void)0)
CONSTANT(OP_DEL_PROPERTY,(void)0)
CONSTANT(OP_METHOD, (void)0)
CONSTANT(OP_CLASS_PROPERTY, (void)0)
CONSTANT(OP_CLOSURE, CLOSURE_MORE)
CONSTANT(OP_IMPORT, (void)0)
CONSTANT(OP_IMPORT_FROM, (void)0)

View File

@ -565,7 +565,7 @@ char * syn_krk_types[] = {
"object","exception","isinstance","type","tuple","reversed",
"print","set","any","all","bool","ord","chr","hex","oct","filter",
"sorted","bytes","getattr","sum","min","max","id","hash","map","bin",
"enumerate","zip",
"enumerate","zip","setattr","property","staticmethod","classmethod",
NULL
};

View File

@ -88,7 +88,7 @@ static inline const char * _method_name(const char * func) {
#define MAKE_CLASS(klass) do { krk_makeClass(module,&klass,#klass,vm.baseClasses->objectClass); klass ->allocSize = sizeof(struct klass); } while (0)
#define BIND_METHOD(klass,method) krk_defineNative(&klass->methods, "." #method, _ ## klass ## _ ## method)
#define BIND_FIELD(klass,method) krk_defineNative(&klass->methods, ":" #method, _ ## klass ## _ ## method)
#define BIND_PROP(klass,method) krk_defineNativeProperty(&klass->fields, #method, _ ## klass ## _ ## method)
#define BIND_PROP(klass,method) krk_defineNativeProperty(&klass->methods, #method, _ ## klass ## _ ## method)
#define BIND_FUNC(module,func) krk_defineNative(&module->fields, #func, _krk_ ## func)
/**

124
src/vm.c
View File

@ -371,7 +371,7 @@ KrkClass * krk_makeClass(KrkInstance * module, KrkClass ** _class, const char *
/* Now give it a __module__ */
KrkValue moduleName = NONE_VAL();
krk_tableGet(&module->fields, OBJECT_VAL(S("__name__")), &moduleName);
krk_attachNamedValue(&(*_class)->fields,"__module__",moduleName);
krk_attachNamedValue(&(*_class)->methods,"__module__",moduleName);
krk_pop();
}
krk_pop();
@ -414,7 +414,12 @@ void krk_finalizeClass(KrkClass * _class) {
};
for (struct TypeMap * entry = specials; entry->method; ++entry) {
if (krk_tableGet(&_class->methods, vm.specialMethodNames[entry->index], &tmp)) {
KrkClass * _base = _class;
while (_base) {
if (krk_tableGet(&_base->methods, vm.specialMethodNames[entry->index], &tmp)) break;
_base = _base->base;
}
if (_base && (IS_CLOSURE(tmp) || IS_NATIVE(tmp))) {
*entry->method = AS_OBJECT(tmp);
}
}
@ -894,13 +899,25 @@ KrkValue krk_callSimple(KrkValue value, int argCount, int isMethod) {
*/
int krk_bindMethod(KrkClass * _class, KrkString * name) {
KrkValue method, out;
if (!krk_tableGet(&_class->methods, OBJECT_VAL(name), &method)) return 0;
while (_class) {
if (krk_tableGet(&_class->methods, OBJECT_VAL(name), &method)) break;
_class = _class->base;
}
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_PROPERTY(method)) {
/* Need to push for property object */
krk_push(krk_peek(0));
out = krk_callSimple(AS_PROPERTY(method)->method, 1, 0);
} else if (IS_CLOSURE(method) && (AS_CLOSURE(method)->function->isClassMethod)) {
out = OBJECT_VAL(krk_newBoundMethod(OBJECT_VAL(_class), AS_OBJECT(method)));
} else {
} else if (IS_CLOSURE(method) && (AS_CLOSURE(method)->function->isStaticMethod)) {
out = method;
} else if (IS_CLOSURE(method) || IS_NATIVE(method)) {
out = OBJECT_VAL(krk_newBoundMethod(krk_peek(0), AS_OBJECT(method)));
} else {
out = method;
}
krk_pop();
krk_push(out);
@ -1039,8 +1056,7 @@ static KrkValue krk_getsize(int argc, KrkValue argv[], int hasKw) {
}
case OBJ_CLASS: {
KrkClass * self = AS_CLASS(argv[0]);
mySize += sizeof(KrkClass) + sizeof(KrkTableEntry) * self->fields.capacity
+ sizeof(KrkTableEntry) * self->methods.capacity;
mySize += sizeof(KrkClass) + sizeof(KrkTableEntry) * self->methods.capacity;
break;
}
case OBJ_NATIVE: {
@ -1699,11 +1715,6 @@ static int valueGetProperty(KrkString * name) {
if (IS_INSTANCE(krk_peek(0))) {
KrkInstance * instance = AS_INSTANCE(krk_peek(0));
if (krk_tableGet(&instance->fields, OBJECT_VAL(name), &value)) {
if (IS_PROPERTY(value)) {
/* Properties retreived from instances are magic. */
krk_push(krk_callSimple(AS_PROPERTY(value)->method, 1, 0));
return 1;
}
krk_pop();
krk_push(value);
return 1;
@ -1711,10 +1722,13 @@ static int valueGetProperty(KrkString * name) {
objectClass = instance->_class;
} else if (IS_CLASS(krk_peek(0))) {
KrkClass * _class = AS_CLASS(krk_peek(0));
if (krk_tableGet(&_class->fields, OBJECT_VAL(name), &value) ||
krk_tableGet(&_class->methods, OBJECT_VAL(name), &value)) {
while (_class) {
if (krk_tableGet(&_class->methods, OBJECT_VAL(name), &value)) break;
_class = _class->base;
}
if (_class) {
if (IS_CLOSURE(value) && AS_CLOSURE(value)->function->isClassMethod) {
value = OBJECT_VAL(krk_newBoundMethod(OBJECT_VAL(_class), AS_OBJECT(value)));
value = OBJECT_VAL(krk_newBoundMethod(krk_peek(0), AS_OBJECT(value)));
}
krk_pop();
krk_push(value);
@ -1760,7 +1774,7 @@ static int valueDelProperty(KrkString * name) {
return 1;
} else if (IS_CLASS(krk_peek(0))) {
KrkClass * _class = AS_CLASS(krk_peek(0));
if (!krk_tableDelete(&_class->fields, OBJECT_VAL(name))) {
if (!krk_tableDelete(&_class->methods, OBJECT_VAL(name))) {
return 0;
}
krk_pop(); /* the original value */
@ -1770,6 +1784,46 @@ static int valueDelProperty(KrkString * name) {
return 0;
}
static int valueSetProperty(KrkString * name) {
KrkValue owner = krk_peek(1);
KrkValue value = krk_peek(0);
if (IS_INSTANCE(owner)) {
if (krk_tableSet(&AS_INSTANCE(owner)->fields, OBJECT_VAL(name), value)) {
KrkClass * _class = AS_INSTANCE(owner)->_class;
KrkValue method;
while (_class) {
if (krk_tableGet(&_class->methods, OBJECT_VAL(name), &method)) break;
_class = _class->base;
}
if (_class && IS_PROPERTY(method)) {
krk_tableDelete(&AS_INSTANCE(owner)->fields, OBJECT_VAL(name));
krk_push(krk_callSimple(AS_PROPERTY(method)->method, 2, 0));
return 1;
}
}
} else if (IS_CLASS(owner)) {
krk_tableSet(&AS_CLASS(owner)->methods, OBJECT_VAL(name), value);
} else {
/* TODO: Setters for other things */
return 0;
}
krk_swap(1);
krk_pop();
return 1;
}
KrkValue krk_valueSetAttribute(KrkValue owner, char * name, KrkValue to) {
krk_push(OBJECT_VAL(krk_copyString(name,strlen(name))));
krk_push(owner);
krk_push(to);
if (!valueSetProperty(AS_STRING(krk_peek(2)))) {
return krk_runtimeError(vm.exceptions->attributeError, "'%s' object has no attribute '%s'", krk_typeName(krk_peek(1)), name);
}
krk_swap(1);
krk_pop(); /* String */
return krk_pop();
}
#define READ_BYTE() (*frame->ip++)
#define BINARY_OP(op) { KrkValue b = krk_pop(); KrkValue a = krk_pop(); krk_push(krk_operator_ ## op (a,b)); break; }
#define BINARY_OP_CHECK_ZERO(op) { KrkValue b = krk_pop(); KrkValue a = krk_pop(); \
@ -2043,8 +2097,6 @@ _resumeHook: (void)0;
}
KrkClass * subclass = AS_CLASS(krk_peek(0));
subclass->base = AS_CLASS(superclass);
krk_tableAddAll(&AS_CLASS(superclass)->methods, &subclass->methods);
krk_tableAddAll(&AS_CLASS(superclass)->fields, &subclass->fields);
subclass->allocSize = AS_CLASS(superclass)->allocSize;
subclass->_ongcsweep = AS_CLASS(superclass)->_ongcsweep;
subclass->_ongcscan = AS_CLASS(superclass)->_ongcscan;
@ -2059,21 +2111,6 @@ _resumeHook: (void)0;
case OP_SWAP:
krk_swap(1);
break;
case OP_CREATE_PROPERTY: {
KrkProperty * newProperty = krk_newProperty(krk_peek(0));
krk_pop();
krk_push(OBJECT_VAL(newProperty));
break;
}
case OP_CREATE_CLASSMETHOD: {
if (!IS_CLOSURE(krk_peek(0))) {
krk_runtimeError(vm.exceptions->typeError, "Classmethod must be a method, not '%s'",
krk_typeName(krk_peek(0)));
goto _finishException;
}
AS_CLOSURE(krk_peek(0))->function->isClassMethod = 1;
break;
}
case OP_FILTER_EXCEPT: {
int isMatch = 0;
if (IS_CLASS(krk_peek(0)) && krk_isInstanceOf(krk_peek(1), AS_CLASS(krk_peek(0)))) {
@ -2275,7 +2312,7 @@ _resumeHook: (void)0;
KrkClass * _class = krk_newClass(name, vm.baseClasses->objectClass);
krk_push(OBJECT_VAL(_class));
_class->filename = frame->closure->function->chunk.filename;
krk_attachNamedObject(&_class->fields, "__func__", (KrkObj*)frame->closure);
krk_attachNamedObject(&_class->methods, "__func__", (KrkObj*)frame->closure);
break;
}
case OP_IMPORT_FROM_LONG:
@ -2322,27 +2359,14 @@ _resumeHook: (void)0;
case OP_SET_PROPERTY_LONG:
case OP_SET_PROPERTY: {
KrkString * name = READ_STRING(OPERAND);
KrkTable * table = NULL;
if (IS_INSTANCE(krk_peek(1))) table = &AS_INSTANCE(krk_peek(1))->fields;
else if (IS_CLASS(krk_peek(1))) table = &AS_CLASS(krk_peek(1))->fields;
if (table) {
KrkValue previous;
if (krk_tableGet(table, OBJECT_VAL(name), &previous) && IS_PROPERTY(previous)) {
krk_push(krk_callSimple(AS_PROPERTY(previous)->method, 2, 0));
break;
} else {
krk_tableSet(table, OBJECT_VAL(name), krk_peek(0));
}
} else {
krk_runtimeError(vm.exceptions->attributeError, "'%s' object has no attribute '%s'", krk_typeName(krk_peek(0)), name->chars);
if (unlikely(!valueSetProperty(name))) {
krk_runtimeError(vm.exceptions->attributeError, "'%s' object has no attribute '%s'", krk_typeName(krk_peek(1)), name->chars);
goto _finishException;
}
krk_swap(1);
krk_pop();
break;
}
case OP_METHOD_LONG:
case OP_METHOD: {
case OP_CLASS_PROPERTY_LONG:
case OP_CLASS_PROPERTY: {
KrkValue method = krk_peek(0);
KrkClass * _class = AS_CLASS(krk_peek(1));
KrkValue name = OBJECT_VAL(READ_STRING(OPERAND));

View File

@ -593,6 +593,14 @@ extern KrkValue krk_dict_of(int argc, KrkValue argv[], int hasKw);
*/
extern KrkValue krk_tuple_of(int argc, KrkValue argv[], int hasKw);
/**
* @brief Create a set object.
* @memberof Set
*
* This is the native function bound to @c setOf
*/
extern KrkValue krk_set_of(int argc, KrkValue argv[], int hasKw);
/**
* @brief Call a callable and manage VM state to obtain the return value.
* @memberof KrkValue
@ -735,6 +743,22 @@ extern int krk_isFalsey(KrkValue value);
*/
extern KrkValue krk_valueGetAttribute(KrkValue value, char * name);
/**
* @brief Set a property of an object by name.
* @memberof KrkValue
*
* This is a convenience function that works in essentially the
* same way as the OP_SET_PROPERTY instruction.
*
* @param owner The owner of the property to modify.
* @param name C-string of the property name to modify.
* @param to The value to assign.
* @return The set value, or None with an @ref AttributeError
* exception set in the current thread if the object can
* not have a property set.
*/
extern KrkValue krk_valueSetAttribute(KrkValue owner, char * name, KrkValue to);
/**
* @brief Concatenate two strings.
*

View File

@ -123,6 +123,8 @@ class SuperClass():
print("This is a great " + self.a + "!")
def __str__(self):
return "(I am a " + self.a + ")"
def __repr__(self):
return "(I am a " + self.a + ")"
def __get__(self, ind):
return "(get[" + str(ind) + "])"
def __set__(self, ind, val):

View File

@ -29,7 +29,7 @@ Let's do some classes.
<class '__main__.Test'>
yay: bax
bar
<method Test.doAThing>
<bound method Test.doAThing>
yay: bar
This is a great teapot!
Subclass says: (I am a teapot)

View File

@ -1,4 +1,4 @@
['__class__', '__str__', '__dir__', '__repr__', '__hash__', 'thatWeWantToSet', 'longList', 'onThatObject', 'ofAttributes']
['__class__', '__dir__', '__hash__', '__repr__', '__str__', 'longList', 'ofAttributes', 'onThatObject', 'thatWeWantToSet']
1
2
3

View File

@ -7,10 +7,10 @@ False
[1, 3, 4, 5]
[1, 3, 4]
list index out of range: 3
['__class__', '__str__', '__dir__', '__repr__', '__hash__', 'baz', 'qux']
['__class__', '__dir__', '__hash__', '__repr__', '__str__', 'baz', 'qux']
42
['__class__', '__str__', '__dir__', '__repr__', '__hash__', 'qux']
['__class__', '__dir__', '__hash__', '__repr__', '__str__', 'qux']
hi
'object' object has no attribute 'baz'
['__class__', '__str__', '__dir__', '__repr__', '__hash__']
['__class__', '__dir__', '__hash__', '__repr__', '__str__']
'object' object has no attribute 'bar'

View File

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

View File

@ -205,7 +205,7 @@ def processModules(modules):
if isinstance(obj, function) and member not in seen:
seen.append(member)
methods.append(Pair(member,obj))
else if isinstance(obj, Property) and member not in seen:
else if isinstance(obj, property) and member not in seen:
seen.append(member)
properties.append(Pair(member,obj.__method__))
if methods: