From ae2e2be15dc1467234287541b9db768e7d1871bd Mon Sep 17 00:00:00 2001 From: "K. Lange" Date: Fri, 1 Jan 2021 16:01:58 +0900 Subject: [PATCH] do something more straightforward and useful for methods on non-objects --- builtins.krk | 12 +- debug.c | 5 +- object.c | 51 +- object.h | 4 +- rline.c | 4 +- src/fileio.c | 27 +- test/testPrintBenchmark.krk | 4 + value.c | 56 +- value.h | 1 + vm.c | 983 +++++++++++++++++++----------------- vm.h | 5 + 11 files changed, 612 insertions(+), 540 deletions(-) create mode 100644 test/testPrintBenchmark.krk diff --git a/builtins.krk b/builtins.krk index a07cb6f..f734b5b 100644 --- a/builtins.krk +++ b/builtins.krk @@ -16,13 +16,14 @@ class list(): for v in i: self.append(v) return self.__len__() + def __repr__(self): return self.__str__() def __str__(self): let b="[" let l=self.__len__() for i=0,i0: b+=", " - b=b+__builtins__.list_get(self._list,i) + b+=repr(__builtins__.list_get(self._list,i)) return b+"]" def __iter__(self): let m = self @@ -47,6 +48,7 @@ class dict(): return __builtins__.hash_get(self._map, ind) def __set__(self, ind, val): return __builtins__.hash_set(self._map, ind, val) + def __repr__(self): return self.__str__() def __str__(self): let out = "{" let first = True @@ -54,7 +56,7 @@ class dict(): if not first: out += ", " first = False - out = out + v + ": " + self[v] + out += repr(v) + ": " + repr(self[v]) out += "}" return out def __len__(self): @@ -105,9 +107,11 @@ class range: return makeIter(self.min) def len(obj=None): return (obj and obj.__len__()) or 0 -def str(obj=None): return (obj and ("" + obj)) or "" +def str(obj=None): return (obj and obj.__str__()) or "" +def repr(obj): return obj.__repr__() def int(obj=None): return (obj and obj.__int__()) or 0 def float(obj=None): return (obj and obj.__float__()) or 0.0 +def type(obj): return obj.__class__ def dir(obj): return obj.__dir__() def help(obj=None): @@ -123,7 +127,7 @@ def help(obj=None): except: print "No docstring available for", obj -export list,dict,range,len,str,int,float,dir,help +export list,dict,range,len,str,repr,int,float,dir,help __builtins__.module_paths = ["./","./modules/","/home/klange/Projects/kuroko/modules/","/usr/share/kuroko/"] diff --git a/debug.c b/debug.c index f63e291..7f38c4e 100644 --- a/debug.c +++ b/debug.c @@ -13,14 +13,14 @@ void krk_disassembleChunk(KrkChunk * chunk, const char * name) { #define SIMPLE(opc) case opc: fprintf(stderr, "%s\n", #opc); return offset + 1; #define CONSTANT(opc,more) case opc: { size_t constant = chunk->code[offset + 1]; \ fprintf(stderr, "%-16s %4d '", #opc, (int)constant); \ - krk_printValue(stderr, chunk->constants.values[constant]); \ + krk_printValueSafe(stderr, chunk->constants.values[constant]); \ fprintf(stderr,"' (type=%s)\n", krk_typeName(chunk->constants.values[constant])); \ more; \ return offset + 2; } \ case opc ## _LONG: { size_t constant = (chunk->code[offset + 1] << 16) | \ (chunk->code[offset + 2] << 8) | (chunk->code[offset + 3]); \ fprintf(stderr, "%-16s %4d '", #opc "_LONG", (int)constant); \ - krk_printValue(stderr, chunk->constants.values[constant]); \ + krk_printValueSafe(stderr, chunk->constants.values[constant]); \ fprintf(stderr,"' (type=%s)\n", krk_typeName(chunk->constants.values[constant])); \ more; \ return offset + 4; } @@ -81,6 +81,7 @@ size_t krk_disassembleInstruction(KrkChunk * chunk, size_t offset) { SIMPLE(OP_BITAND) SIMPLE(OP_SHIFTLEFT) SIMPLE(OP_SHIFTRIGHT) + SIMPLE(OP_BITNEGATE) OPERANDB(OP_DUP) OPERANDB(OP_SWAP) CONSTANT(OP_DEFINE_GLOBAL,(void)0) diff --git a/object.c b/object.c index 0afb42b..25f40cf 100644 --- a/object.c +++ b/object.c @@ -61,52 +61,6 @@ KrkString * krk_copyString(const char * chars, size_t length) { return allocateString(heapChars, length, hash); } -#define NAME(obj) ((obj)->name ? obj->name->chars : "(unnamed)") -void krk_printObject(FILE * f, KrkValue value) { - switch (OBJECT_TYPE(value)) { - case OBJ_STRING: - fprintf(f,"\""); - for (char * c = AS_CSTRING(value); *c; ++c) { - switch (*c) { - /* XXX: Other non-printables should probably be escaped as well. */ - case '\n': fprintf(f,"\\n"); break; - case '\r': fprintf(f,"\\r"); break; - case '\t': fprintf(f,"\\t"); break; - case '"': fprintf(f,"\\\""); break; - case '\033': fprintf(f,"\\["); break; - default: fprintf(f,"%c",*c); break; - } - } - fprintf(f,"\""); - break; - case OBJ_FUNCTION: - if (AS_FUNCTION(value)->name == NULL) fprintf(f, ""); - else fprintf(f, "", NAME(AS_FUNCTION(value))); - break; - case OBJ_NATIVE: - fprintf(f, ""); - break; - case OBJ_CLOSURE: - fprintf(f, ">", NAME(AS_CLOSURE(value)->function)); - break; - case OBJ_UPVALUE: - fprintf(f, ""); - break; - case OBJ_CLASS: - fprintf(f, "", NAME(AS_CLASS(value))); - break; - case OBJ_INSTANCE: - fprintf(f, "", NAME(AS_INSTANCE(value)->_class)); - break; - case OBJ_BOUND_METHOD: - fprintf(f, ">", (AS_BOUND_METHOD(value)->method->type == OBJ_CLOSURE) ? - NAME(((KrkClosure*)AS_BOUND_METHOD(value)->method)->function) : ( - (AS_BOUND_METHOD(value)->method->type == OBJ_NATIVE) ? "" : "" - )); - break; - } -} - KrkFunction * krk_newFunction() { KrkFunction * function = ALLOCATE_OBJECT(KrkFunction, OBJ_FUNCTION); function->requiredArgs = 0; @@ -118,10 +72,11 @@ KrkFunction * krk_newFunction() { return function; } -KrkNative * krk_newNative(NativeFn function) { +KrkNative * krk_newNative(NativeFn function, const char * name, int type) { KrkNative * native = ALLOCATE_OBJECT(KrkNative, OBJ_NATIVE); native->function = function; - native->isMethod = 0; + native->isMethod = type; + native->name = name; return native; } diff --git a/object.h b/object.h index b3bf16c..c7396b1 100644 --- a/object.h +++ b/object.h @@ -97,6 +97,7 @@ typedef KrkValue (*NativeFn)(int argCount, KrkValue* args); typedef struct { KrkObj obj; NativeFn function; + const char * name; int isMethod; } KrkNative; @@ -106,9 +107,8 @@ static inline int isObjType(KrkValue value, ObjType type) { extern KrkString * krk_takeString(char * chars, size_t length); extern KrkString * krk_copyString(const char * chars, size_t length); -extern void krk_printObject(FILE * f, KrkValue value); extern KrkFunction * krk_newFunction(); -extern KrkNative * krk_newNative(NativeFn function); +extern KrkNative * krk_newNative(NativeFn function, const char * name, int type); extern KrkClosure * krk_newClosure(KrkFunction * function); extern KrkUpvalue * krk_newUpvalue(int slot); extern KrkClass * krk_newClass(KrkString * name); diff --git a/rline.c b/rline.c index 1afd598..d223d10 100644 --- a/rline.c +++ b/rline.c @@ -540,9 +540,9 @@ char * syn_krk_keywords[] = { char * syn_krk_types[] = { /* built-in functions */ "self", "super", /* implicit in a class method */ - "len", "str", "int", "float", "dir", /* global functions from __builtins__ */ + "len", "str", "int", "float", "dir", "repr", /* global functions from __builtins__ */ "list","dict","range", /* builtin classes */ - "object","exception","isinstance", + "object","exception","isinstance","type", NULL }; diff --git a/src/fileio.c b/src/fileio.c index e26e61d..1dd459a 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -41,7 +41,17 @@ KrkValue krk_open(int argc, KrkValue argv[]) { return NONE_VAL(); } - FILE * file = fopen(AS_CSTRING(argv[0]), AS_CSTRING(argv[1])); + KrkValue arg; + + if (argc == 1) { + arg = OBJECT_VAL(S("r")); + krk_push(arg); + } else { + arg = argv[1]; + krk_push(argv[1]); + } + + FILE * file = fopen(AS_CSTRING(argv[0]), AS_CSTRING(arg)); if (!file) { krk_runtimeError(vm.exceptions.ioError, "open: failed to open file; system returned: %s", strerror(errno)); return NONE_VAL(); @@ -53,14 +63,28 @@ KrkValue krk_open(int argc, KrkValue argv[]) { /* Let's put the filename in there somewhere... */ krk_attachNamedValue(&fileObject->fields, "filename", argv[0]); + krk_attachNamedValue(&fileObject->fields, "modestr", arg); krk_attachNamedValue(&fileObject->fields, "_fileptr", INTEGER_VAL((long)(file))); /* Need a KrkNativePrivate or something... */ + krk_pop(); krk_pop(); return OBJECT_VAL(fileObject); } #define BLOCK_SIZE 1024 +static KrkValue krk_file_str(int argc, KrkValue argv[]) { + KrkInstance * fileObj = AS_INSTANCE(argv[0]); + KrkValue filename, modestr; + krk_tableGet(&fileObj->fields, OBJECT_VAL(S("filename")), &filename); + krk_tableGet(&fileObj->fields, OBJECT_VAL(S("modestr")), &modestr); + char * tmp = malloc(AS_STRING(filename)->length + AS_STRING(modestr)->length + 100); /* safety */ + sprintf(tmp, "", AS_CSTRING(filename), AS_CSTRING(modestr), (void*)fileObj); + KrkString * out = krk_copyString(tmp, strlen(tmp)); + free(tmp); + return OBJECT_VAL(out); +} + static FILE * getFilePtr(KrkValue obj) { KrkValue strFilePtr = OBJECT_VAL(S("_fileptr")); krk_push(strFilePtr); @@ -243,6 +267,7 @@ KrkValue krk_module_onload_fileio(void) { krk_defineNative(&FileClass->methods, ".close", krk_file_close); krk_defineNative(&FileClass->methods, ".write", krk_file_write); krk_defineNative(&FileClass->methods, ".flush", krk_file_flush); + krk_defineNative(&FileClass->methods, ".__str__", krk_file_str); krk_defineNative(&FileClass->methods, ".__init__", krk_file_reject_init); /* Make an instance for stdout, stderr, and stdin */ diff --git a/test/testPrintBenchmark.krk b/test/testPrintBenchmark.krk new file mode 100644 index 0000000..a30cc41 --- /dev/null +++ b/test/testPrintBenchmark.krk @@ -0,0 +1,4 @@ +let x = 1 +while x < 1000000: + print x + x++ diff --git a/value.c b/value.c index 38a330f..6745bcc 100644 --- a/value.c +++ b/value.c @@ -2,6 +2,7 @@ #include "memory.h" #include "value.h" #include "object.h" +#include "vm.h" void krk_initValueArray(KrkValueArray * array) { array->values = NULL; @@ -25,19 +26,48 @@ void krk_freeValueArray(KrkValueArray * array) { krk_initValueArray(array); } -void krk_printValue(FILE * f, KrkValue value) { - if (IS_FLOATING(value)) { - fprintf(f, "%g", AS_FLOATING(value)); - } else if (IS_INTEGER(value)) { - fprintf(f, "%ld", (long)AS_INTEGER(value)); - } else if (IS_BOOLEAN(value)) { - fprintf(f, "%s", AS_BOOLEAN(value) ? "True" : "False"); - } else if (IS_NONE(value)) { - fprintf(f, "None"); - } else if (IS_HANDLER(value)) { - fprintf(f, "{try->%ld}", AS_HANDLER(value)); - } else if (IS_OBJECT(value)) { - krk_printObject(f, value); +void krk_printValue(FILE * f, KrkValue printable) { + if (!IS_OBJECT(printable)) { + switch (printable.type) { + case VAL_INTEGER: fprintf(f, "%ld", AS_INTEGER(printable)); break; + case VAL_BOOLEAN: fprintf(f, "%s", AS_BOOLEAN(printable) ? "True" : "False"); break; + case VAL_FLOATING: fprintf(f, "%g", AS_FLOATING(printable)); break; + case VAL_NONE: fprintf(f, "None"); break; + case VAL_HANDLER: fprintf(f, "{try->%ld}", AS_HANDLER(printable)); break; + default: break; + } + return; + } + krk_push(printable); + if (krk_bindMethod(AS_CLASS(krk_typeOf(1,(KrkValue[]){printable})), AS_STRING(vm.specialMethodNames[METHOD_REPR]))) { + switch (krk_callValue(krk_peek(0), 0)) { + case 2: printable = krk_pop(); break; + case 1: printable = krk_runNext(); break; + default: fprintf(f, "[unable to print object at address %p]", (void*)AS_OBJECT(printable)); return; + } + fprintf(f, "%s", AS_CSTRING(printable)); + } else { + krk_pop(); + fprintf(f, "%s", krk_typeName(printable)); + } +} + +void krk_printValueSafe(FILE * f, KrkValue printable) { + if (!IS_OBJECT(printable)) { + krk_printValue(f,printable); + } else if (IS_STRING(printable)) { + fprintf(f, "\"%s\"", AS_CSTRING(printable)); + } else { + switch (AS_OBJECT(printable)->type) { + case OBJ_CLASS: fprintf(f, "", AS_CLASS(printable)->name->chars); break; + case OBJ_INSTANCE: fprintf(f, "", AS_INSTANCE(printable)->_class->name->chars); break; + case OBJ_NATIVE: fprintf(f, "", ((KrkNative*)AS_OBJECT(printable))->name); break; + case OBJ_CLOSURE: fprintf(f, "", AS_CLOSURE(printable)->function->name->chars); break; + case OBJ_BOUND_METHOD: fprintf(f, "", + AS_BOUND_METHOD(printable)->method->type == OBJ_CLOSURE ? ((KrkClosure*)AS_BOUND_METHOD(printable)->method)->function->name->chars : + ((KrkNative*)AS_BOUND_METHOD(printable)->method)->name); break; + default: fprintf(f, "<%s>", krk_typeName(printable)); break; + } } } diff --git a/value.h b/value.h index 0a7f9d5..b48b1b2 100644 --- a/value.h +++ b/value.h @@ -57,5 +57,6 @@ extern void krk_initValueArray(KrkValueArray * array); extern void krk_writeValueArray(KrkValueArray * array, KrkValue value); extern void krk_freeValueArray(KrkValueArray * array); extern void krk_printValue(FILE * f, KrkValue value); +extern void krk_printValueSafe(FILE * f, KrkValue value); extern int krk_valuesEqual(KrkValue a, KrkValue b); diff --git a/vm.c b/vm.c index 413912d..ac78c43 100644 --- a/vm.c +++ b/vm.c @@ -17,10 +17,9 @@ KrkVM vm; static KrkValue run(); -static int callValue(KrkValue callee, int argCount); -static int bindMethod(KrkClass * _class, KrkString * name); static int call(KrkClosure * closure, int argCount); static KrkValue krk_isinstance(int argc, KrkValue argv[]); +static void dumpStack(CallFrame * frame); extern const char _builtins_src[]; @@ -56,11 +55,11 @@ static void dumpTraceback() { /* Make sure strings are printed without quotes */ fprintf(stderr, "%s", AS_CSTRING(exceptionArg)); } else { - krk_printObject(stderr, exceptionArg); + krk_printValueSafe(stderr, exceptionArg); } } else { /* Whatever, just print it. */ - krk_printObject(stderr, vm.currentException); + krk_printValueSafe(stderr, vm.currentException); } fprintf(stderr, "\n"); @@ -89,7 +88,7 @@ void krk_runtimeError(KrkClass * type, const char * fmt, ...) { vm.currentException = OBJECT_VAL(exceptionObject); } -void krk_push(KrkValue value) { +inline void krk_push(KrkValue value) { if ((size_t)(vm.stackTop - vm.stack) + 1 > vm.stackSize) { size_t old = vm.stackSize; size_t old_offset = vm.stackTop - vm.stack; @@ -122,11 +121,16 @@ void krk_swap(int distance) { } void krk_defineNative(KrkTable * table, const char * name, NativeFn function) { - KrkNative * func = krk_newNative(function); + int functionType = 0; if (*name == '.') { name++; - func->isMethod = 1; + functionType = 1; } + if (*name == ':') { + name++; + functionType = 2; + } + KrkNative * func = krk_newNative(function, name, functionType); krk_push(OBJECT_VAL(func)); krk_push(OBJECT_VAL(krk_copyString(name, (int)strlen(name)))); krk_tableSet(table, krk_peek(0), krk_peek(1)); @@ -388,14 +392,11 @@ static KrkValue krk_set_tracing(int argc, KrkValue argv[]) { } static KrkValue krk_dirObject(int argc, KrkValue argv[]) { - if (argc != 1 || !IS_INSTANCE(argv[0])) { + if (argc != 1) { krk_runtimeError(vm.exceptions.argumentError, "wrong number of arguments or bad type, got %d\n", argc); return NONE_VAL(); } - /* Obtain self-reference */ - KrkInstance * self = AS_INSTANCE(argv[0]); - /* Create a new list instance */ KrkValue Class; krk_tableGet(&vm.globals,OBJECT_VAL(S("list")), &Class); @@ -405,19 +406,34 @@ static KrkValue krk_dirObject(int argc, KrkValue argv[]) { krk_push(OBJECT_VAL(listContents)); krk_tableSet(&outList->fields, OBJECT_VAL(S("_list")), OBJECT_VAL(listContents)); - /* 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_NONE) { - krk_writeValueArray(&listContents->chunk.constants, - 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_NONE) { - krk_writeValueArray(&listContents->chunk.constants, - self->fields.entries[i].key); + 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_NONE) { + krk_writeValueArray(&listContents->chunk.constants, + 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_NONE) { + krk_writeValueArray(&listContents->chunk.constants, + self->fields.entries[i].key); + } + } + } else { + KrkClass * type = AS_CLASS(krk_typeOf(1, (KrkValue[]){argv[0]})); + + for (size_t i = 0; i < type->methods.capacity; ++i) { + if (type->methods.entries[i].key.type != VAL_NONE) { + krk_writeValueArray(&listContents->chunk.constants, + type->methods.entries[i].key); + } } } @@ -428,7 +444,7 @@ static KrkValue krk_dirObject(int argc, KrkValue argv[]) { return out; } -static KrkValue krk_typeOf(int argc, KrkValue argv[]) { +KrkValue krk_typeOf(int argc, KrkValue argv[]) { switch (argv[0].type) { case VAL_INTEGER: return OBJECT_VAL(vm.baseClasses.intClass); @@ -460,6 +476,30 @@ static KrkValue krk_typeOf(int argc, KrkValue argv[]) { } } +static KrkValue krk_baseOfClass(int argc, KrkValue argv[]) { + return AS_CLASS(argv[0])->base ? OBJECT_VAL(AS_CLASS(argv[0])->base) : NONE_VAL(); +} + +static KrkValue krk_nameOfClass(int argc, KrkValue argv[]) { + return AS_CLASS(argv[0])->name ? OBJECT_VAL(AS_CLASS(argv[0])->name) : NONE_VAL(); +} + +static KrkValue krk_fileOfClass(int argc, KrkValue argv[]) { + return AS_CLASS(argv[0])->filename ? OBJECT_VAL(AS_CLASS(argv[0])->filename) : NONE_VAL(); +} + +static KrkValue krk_docOfClass(int argc, KrkValue argv[]) { + return AS_CLASS(argv[0])->docstring ? OBJECT_VAL(AS_CLASS(argv[0])->docstring) : NONE_VAL(); +} + +static KrkValue _class_to_str(int argc, KrkValue argv[]) { + char * tmp = malloc(sizeof("") + AS_CLASS(argv[0])->name->length); + size_t l = sprintf(tmp, "", AS_CLASS(argv[0])->name->chars); + KrkString * out = krk_copyString(tmp,l); + free(tmp); + return OBJECT_VAL(out); +} + static KrkValue krk_isinstance(int argc, KrkValue argv[]) { if (argc != 2) { krk_runtimeError(vm.exceptions.argumentError, "isinstance expects 2 arguments, got %d", argc); @@ -512,14 +552,14 @@ static int call(KrkClosure * closure, int argCount) { return 1; } -static int callValue(KrkValue callee, int argCount) { +int krk_callValue(KrkValue callee, int argCount) { if (IS_OBJECT(callee)) { switch (OBJECT_TYPE(callee)) { case OBJ_CLOSURE: return call(AS_CLOSURE(callee), argCount); case OBJ_NATIVE: { NativeFn native = AS_NATIVE(callee); - int extraArgs = !!((KrkNative*)AS_OBJECT(callee))->isMethod; + int extraArgs = (((KrkNative*)AS_OBJECT(callee))->isMethod == 1); KrkValue * stackCopy = malloc((argCount + extraArgs) * sizeof(KrkValue)); memcpy(stackCopy, vm.stackTop - argCount - extraArgs, (argCount + extraArgs) * sizeof(KrkValue)); KrkValue result = native(argCount + extraArgs, stackCopy); @@ -537,7 +577,7 @@ static int callValue(KrkValue callee, int argCount) { vm.stackTop[-argCount - 1] = OBJECT_VAL(krk_newInstance(_class)); KrkValue initializer; if (krk_tableGet(&_class->methods, vm.specialMethodNames[METHOD_INIT], &initializer)) { - return callValue(initializer, argCount); + return krk_callValue(initializer, argCount); } else if (argCount != 0) { krk_runtimeError(vm.exceptions.attributeError, "Class does not have an __init__ but arguments were passed to initializer: %d\n", argCount); return 0; @@ -547,7 +587,7 @@ static int callValue(KrkValue callee, int argCount) { case OBJ_BOUND_METHOD: { KrkBoundMethod * bound = AS_BOUND_METHOD(callee); vm.stackTop[-argCount - 1] = bound->receiver; - return callValue(OBJECT_VAL(bound->method), argCount); + return krk_callValue(OBJECT_VAL(bound->method), argCount); } default: break; @@ -557,12 +597,16 @@ static int callValue(KrkValue callee, int argCount) { return 0; } -static int bindMethod(KrkClass * _class, KrkString * name) { - KrkValue method; +int krk_bindMethod(KrkClass * _class, KrkString * name) { + KrkValue method, out; if (!krk_tableGet(&_class->methods, OBJECT_VAL(name), &method)) return 0; - KrkBoundMethod * bound = krk_newBoundMethod(krk_peek(0), AS_OBJECT(method)); + if (IS_NATIVE(method) && ((KrkNative*)AS_OBJECT(method))->isMethod == 2) { + out = AS_NATIVE(method)(1, (KrkValue[]){krk_peek(0)}); + } else { + out = OBJECT_VAL(krk_newBoundMethod(krk_peek(0), AS_OBJECT(method))); + } krk_pop(); - krk_push(OBJECT_VAL(bound)); + krk_push(out); return 1; } @@ -646,292 +690,21 @@ static KrkValue krk_initException(int argc, KrkValue argv[]) { krk_tableAddAll(&baseClass->methods, &obj->methods); \ } while (0) -void krk_initVM(int flags) { - vm.flags = flags; - KRK_PAUSE_GC(); - - resetStack(); - vm.objects = NULL; - vm.bytesAllocated = 0; - vm.nextGC = 1024 * 1024; - vm.grayCount = 0; - vm.grayCapacity = 0; - vm.grayStack = NULL; - krk_initTable(&vm.globals); - krk_initTable(&vm.strings); - memset(vm.specialMethodNames,0,sizeof(vm.specialMethodNames)); - - vm.specialMethodNames[METHOD_INIT] = OBJECT_VAL(S("__init__")); - vm.specialMethodNames[METHOD_STR] = OBJECT_VAL(S("__str__")); - vm.specialMethodNames[METHOD_GET] = OBJECT_VAL(S("__get__")); - vm.specialMethodNames[METHOD_SET] = OBJECT_VAL(S("__set__")); - vm.specialMethodNames[METHOD_CLASS]= OBJECT_VAL(S("__class__")); - vm.specialMethodNames[METHOD_NAME] = OBJECT_VAL(S("__name__")); - vm.specialMethodNames[METHOD_FILE] = OBJECT_VAL(S("__file__")); - vm.specialMethodNames[METHOD_INT] = OBJECT_VAL(S("__int__")); - vm.specialMethodNames[METHOD_CHR] = OBJECT_VAL(S("__chr__")); - vm.specialMethodNames[METHOD_FLOAT]= OBJECT_VAL(S("__float__")); - vm.specialMethodNames[METHOD_LEN] = OBJECT_VAL(S("__len__")); - vm.specialMethodNames[METHOD_DOC] = OBJECT_VAL(S("__doc__")); - vm.specialMethodNames[METHOD_BASE] = OBJECT_VAL(S("__base__")); - vm.specialMethodNames[METHOD_GETSLICE] = OBJECT_VAL(S("__getslice__")); - - /* Create built-in class `object` */ - vm.objectClass = krk_newClass(S("object")); - krk_attachNamedObject(&vm.globals, "object", (KrkObj*)vm.objectClass); - krk_defineNative(&vm.objectClass->methods, ".__dir__", krk_dirObject); - - vm.builtins = krk_newInstance(vm.objectClass); - krk_attachNamedObject(&vm.globals, "__builtins__", (KrkObj*)vm.builtins); - - /* Add exception classes */ - ADD_EXCEPTION_CLASS(vm.exceptions.baseException, "Exception", vm.objectClass); - /* base exception class gets an init that takes an optional string */ - krk_defineNative(&vm.exceptions.baseException->methods, ".__init__", krk_initException); - ADD_EXCEPTION_CLASS(vm.exceptions.typeError, "TypeError", vm.exceptions.baseException); - ADD_EXCEPTION_CLASS(vm.exceptions.argumentError, "ArgumentError", vm.exceptions.baseException); - ADD_EXCEPTION_CLASS(vm.exceptions.indexError, "IndexError", vm.exceptions.baseException); - ADD_EXCEPTION_CLASS(vm.exceptions.keyError, "KeyError", vm.exceptions.baseException); - ADD_EXCEPTION_CLASS(vm.exceptions.attributeError, "AttributeError", vm.exceptions.baseException); - ADD_EXCEPTION_CLASS(vm.exceptions.nameError, "NameError", vm.exceptions.baseException); - ADD_EXCEPTION_CLASS(vm.exceptions.importError, "ImportError", vm.exceptions.baseException); - ADD_EXCEPTION_CLASS(vm.exceptions.ioError, "IOError", vm.exceptions.baseException); - - /* Build classes for basic types */ - ADD_BASE_CLASS(vm.baseClasses.typeClass, "type", vm.objectClass); - ADD_BASE_CLASS(vm.baseClasses.intClass, "int", vm.objectClass); - ADD_BASE_CLASS(vm.baseClasses.floatClass, "float", vm.objectClass); - ADD_BASE_CLASS(vm.baseClasses.boolClass, "bool", vm.objectClass); - ADD_BASE_CLASS(vm.baseClasses.noneTypeClass, "NoneType", vm.objectClass); - ADD_BASE_CLASS(vm.baseClasses.strClass, "str", vm.objectClass); - ADD_BASE_CLASS(vm.baseClasses.functionClass, "function", vm.objectClass); - ADD_BASE_CLASS(vm.baseClasses.methodClass, "method", vm.objectClass); - - krk_defineNative(&vm.builtins->fields, "hash_new", krk_expose_hash_new); - krk_defineNative(&vm.builtins->fields, "hash_set", krk_expose_hash_set); - krk_defineNative(&vm.builtins->fields, "hash_get", krk_expose_hash_get); - krk_defineNative(&vm.builtins->fields, "hash_key_at_index", krk_expose_hash_key_at_index); - krk_defineNative(&vm.builtins->fields, "hash_capacity", krk_expose_hash_capacity); - krk_defineNative(&vm.builtins->fields, "hash_count", krk_expose_hash_count); - - krk_defineNative(&vm.builtins->fields, "list_new", krk_expose_list_new); - krk_defineNative(&vm.builtins->fields, "list_get", krk_expose_list_get); - krk_defineNative(&vm.builtins->fields, "list_set", krk_expose_list_set); - krk_defineNative(&vm.builtins->fields, "list_append", krk_expose_list_append); - krk_defineNative(&vm.builtins->fields, "list_length", krk_expose_list_length); - - krk_defineNative(&vm.builtins->fields, "set_tracing", krk_set_tracing); - -#ifndef NO_SYSTEM_BINDS - /* Set some other built-ins for the system module */ - krk_defineNative(&vm.builtins->fields, "sleep", krk_sleep); - krk_defineNative(&vm.builtins->fields, "uname", krk_uname); -#endif - - krk_defineNative(&vm.globals, "listOf", krk_list_of); - krk_defineNative(&vm.globals, "dictOf", krk_dict_of); - krk_defineNative(&vm.globals, "isinstance", krk_isinstance); - krk_defineNative(&vm.globals, "type", krk_typeOf); - - /* Now read the builtins module */ - KrkValue builtinsModule = krk_interpret(_builtins_src,1,"__builtins__","__builtins__"); - if (!IS_OBJECT(builtinsModule)) { - fprintf(stderr, "VM startup failure: Failed to load __builtins__ module.\n"); - } - - resetStack(); - KRK_RESUME_GC(); +static KrkValue _noop(int argc, KrkValue argv[]) { + return argv[0]; } -void krk_freeVM() { - krk_freeTable(&vm.globals); - krk_freeTable(&vm.strings); - krk_freeTable(&vm.modules); - memset(vm.specialMethodNames,0,sizeof(vm.specialMethodNames)); - krk_freeObjects(); - FREE_ARRAY(size_t, vm.stack, vm.stackSize); +static KrkValue _floating_to_int(int argc, KrkValue argv[]) { + return INTEGER_VAL((long)AS_FLOATING(argv[0])); } -static int isFalsey(KrkValue value) { - return IS_NONE(value) || (IS_BOOLEAN(value) && !AS_BOOLEAN(value)) || - (IS_INTEGER(value) && !AS_INTEGER(value)); - /* Objects in the future: */ - /* IS_STRING && length == 0; IS_ARRAY && length == 0; IS_INSTANCE && __bool__ returns 0... */ +static KrkValue _int_to_floating(int argc, KrkValue argv[]) { + return FLOATING_VAL((double)AS_INTEGER(argv[0])); } -const char * krk_typeName(KrkValue value) { - if (value.type == VAL_BOOLEAN) return "Boolean"; - if (value.type == VAL_NONE) return "None"; - if (value.type == VAL_INTEGER) return "Integer"; - if (value.type == VAL_FLOATING) return "Floating"; - if (value.type == VAL_OBJECT) { - if (IS_STRING(value)) return "String"; - if (IS_FUNCTION(value)) return "Function"; - if (IS_NATIVE(value)) return "Native"; - if (IS_CLOSURE(value)) return "Closure"; - if (IS_CLASS(value)) return "Class"; - if (IS_INSTANCE(value)) return "Instance"; - if (IS_BOUND_METHOD(value)) return "BoundMethod"; - return "(Unspecified Object)"; - } - return "???"; -} - -#define MAKE_BIN_OP(name,operator) \ - static KrkValue name (KrkValue a, KrkValue b) { \ - if (IS_INTEGER(a) && IS_INTEGER(b)) return INTEGER_VAL(AS_INTEGER(a) operator AS_INTEGER(b)); \ - if (IS_FLOATING(a)) { \ - if (IS_INTEGER(b)) return FLOATING_VAL(AS_FLOATING(a) operator (double)AS_INTEGER(b)); \ - else if (IS_FLOATING(b)) return FLOATING_VAL(AS_FLOATING(a) operator AS_FLOATING(b)); \ - } else if (IS_FLOATING(b)) { \ - if (IS_INTEGER(a)) return FLOATING_VAL((double)AS_INTEGER(a) operator AS_FLOATING(b)); \ - } \ - krk_runtimeError(vm.exceptions.typeError, "Incompatible types for binary operand %s: %s and %s", #operator, krk_typeName(a), krk_typeName(b)); \ - return NONE_VAL(); \ - } - -MAKE_BIN_OP(add,+) -MAKE_BIN_OP(subtract,-) -MAKE_BIN_OP(multiply,*) -MAKE_BIN_OP(divide,/) - -#define MAKE_BIT_OP(name,operator) \ - static KrkValue name (KrkValue a, KrkValue b) { \ - if (IS_INTEGER(a) && IS_INTEGER(b)) return INTEGER_VAL(AS_INTEGER(a) operator AS_INTEGER(b)); \ - krk_runtimeError(vm.exceptions.typeError, "Incompatible types for binary operand %s: %s and %s", #operator, krk_typeName(a), krk_typeName(b)); \ - return NONE_VAL(); \ - } - -MAKE_BIT_OP(bitor,|) -MAKE_BIT_OP(bitxor,^) -MAKE_BIT_OP(bitand,&) -MAKE_BIT_OP(shiftleft,<<) -MAKE_BIT_OP(shiftright,>>) -MAKE_BIT_OP(modulo,%) /* not a bit op, but doesn't work on floating point */ - -#define MAKE_COMPARATOR(name, operator) \ - static KrkValue name (KrkValue a, KrkValue b) { \ - if (IS_INTEGER(a) && IS_INTEGER(b)) return BOOLEAN_VAL(AS_INTEGER(a) operator AS_INTEGER(b)); \ - if (IS_FLOATING(a)) { \ - if (IS_INTEGER(b)) return BOOLEAN_VAL(AS_FLOATING(a) operator AS_INTEGER(b)); \ - else if (IS_FLOATING(b)) return BOOLEAN_VAL(AS_FLOATING(a) operator AS_FLOATING(b)); \ - } else if (IS_FLOATING(b)) { \ - if (IS_INTEGER(a)) return BOOLEAN_VAL(AS_INTEGER(a) operator AS_INTEGER(b)); \ - } \ - krk_runtimeError(vm.exceptions.typeError, "Can not compare types %s and %s", krk_typeName(a), krk_typeName(b)); \ - return NONE_VAL(); \ - } - -MAKE_COMPARATOR(less, <) -MAKE_COMPARATOR(greater, >) - -static void concatenate(const char * a, const char * b, size_t al, size_t bl) { - size_t length = al + bl; - char * chars = ALLOCATE(char, length + 1); - memcpy(chars, a, al); - memcpy(chars + al, b, bl); - chars[length] = '\0'; - - KrkString * result = krk_takeString(chars, length); - krk_pop(); - krk_pop(); - krk_push(OBJECT_VAL(result)); -} - -static void addObjects() { - KrkValue _b = krk_peek(0); - KrkValue _a = krk_peek(1); - - if (IS_STRING(_a)) { - KrkString * a = AS_STRING(_a); - if (IS_STRING(_b)) { - KrkString * b = AS_STRING(_b); - concatenate(a->chars,b->chars,a->length,b->length); - return; - } - char tmp[256] = {0}; - if (IS_INTEGER(_b)) { - sprintf(tmp, "%ld", (long)AS_INTEGER(_b)); - } else if (IS_FLOATING(_b)) { - sprintf(tmp, "%g", AS_FLOATING(_b)); - } else if (IS_BOOLEAN(_b)) { - sprintf(tmp, "%s", AS_BOOLEAN(_b) ? "True" : "False"); - } else if (IS_NONE(_b)) { - sprintf(tmp, "None"); - } else if (IS_INSTANCE(_b)) { - KrkValue method; - if (!krk_tableGet(&AS_INSTANCE(_b)->_class->methods, vm.specialMethodNames[METHOD_STR], &method)) { - sprintf(tmp, ""); - } else { - /* Push the object for self reference */ - krk_push(_b); - KrkValue result; - switch (callValue(method, 0)) { - case 0: result = NONE_VAL(); break; - case 1: result = krk_runNext(); break; - case 2: result = krk_pop(); break; - } - if (!IS_STRING(result)) { - krk_runtimeError(vm.exceptions.typeError, "__str__ produced something that wasn't a string: %s", krk_typeName(result)); - return; - } - sprintf(tmp, "%s", AS_CSTRING(result)); - } - } else { - sprintf(tmp, ""); - } - concatenate(a->chars,tmp,a->length,strlen(tmp)); - } else { - krk_runtimeError(vm.exceptions.typeError, "Can not concatenate types %s and %s", krk_typeName(_a), krk_typeName(_b)); \ - } -} - -#define READ_BYTE() (*frame->ip++) -#define BINARY_OP(op) { KrkValue b = krk_pop(); KrkValue a = krk_pop(); krk_push(op(a,b)); break; } -#define READ_CONSTANT(s) (frame->closure->function->chunk.constants.values[readBytes(frame,s)]) -#define READ_STRING(s) AS_STRING(READ_CONSTANT(s)) - -static inline size_t readBytes(CallFrame * frame, int num) { - size_t out = READ_BYTE(); - while (--num) { - out <<= 8; - out |= (READ_BYTE() & 0xFF); - } - return out; -} - -static KrkClosure * boundNative(NativeFn method, int arity) { - /* Build an object */ - KrkValue nativeFunction = OBJECT_VAL(krk_newNative(method)); - - /* Build a function that calls it */ - KrkFunction * methodWrapper = krk_newFunction(); - methodWrapper->requiredArgs = arity; /* This is WITHOUT the self reference */ - krk_writeConstant(&methodWrapper->chunk, nativeFunction, 1); - - /* Stack silliness */ - krk_writeChunk(&methodWrapper->chunk, OP_GET_LOCAL, 1); /* Should be bind receiver */ - krk_writeChunk(&methodWrapper->chunk, 0, 1); - for (int i = 0; i < arity; ++i) { - krk_writeChunk(&methodWrapper->chunk, OP_GET_LOCAL, 1); /* Should be arguments */ - krk_writeChunk(&methodWrapper->chunk, i + 1, 1); - } - - /* Call with these arguments */ - if (arity > 255) { - int n = arity + 1; - krk_writeChunk(&methodWrapper->chunk, OP_CALL_LONG, 1); - krk_writeChunk(&methodWrapper->chunk, (n >> 16) & 0xFF, 1); - krk_writeChunk(&methodWrapper->chunk, (n >> 8) & 0xFF, 1); - krk_writeChunk(&methodWrapper->chunk, (n >> 0) & 0xFF, 1); - } else { - krk_writeChunk(&methodWrapper->chunk, OP_CALL, 1); - krk_writeChunk(&methodWrapper->chunk, arity + 1, 1); /* arguments to call with */ - } - - /* Return from the wrapper with whatever result we got from the native method */ - krk_writeChunk(&methodWrapper->chunk, OP_RETURN, 1); - return krk_newClosure(methodWrapper); +static KrkValue _int_to_char(int argc, KrkValue argv[]) { + char tmp[2] = {AS_INTEGER(argv[0]), 0}; + return OBJECT_VAL(krk_copyString(tmp,1)); } static KrkValue _string_length(int argc, KrkValue argv[]) { @@ -944,6 +717,11 @@ static KrkValue _string_length(int argc, KrkValue argv[]) { return INTEGER_VAL(AS_STRING(argv[0])->length); } +static KrkValue _strings_are_immutable(int argc, KrkValue argv[]) { + krk_runtimeError(vm.exceptions.typeError, "Strings are not mutable."); + return NONE_VAL(); +} + static KrkValue _string_get_slice(int argc, KrkValue argv[]) { if (argc < 3) { /* 3 because first is us */ krk_runtimeError(vm.exceptions.argumentError, "slice: expected 2 arguments, got %d", argc-1); @@ -1016,6 +794,396 @@ static KrkValue _string_get(int argc, KrkValue argv[]) { return INTEGER_VAL(AS_CSTRING(argv[0])[asInt]); } +static KrkValue _closure_get_doc(int argc, KrkValue argv[]) { + if (!IS_CLOSURE(argv[0])) return NONE_VAL(); + return AS_CLOSURE(argv[0])->function->docstring ? OBJECT_VAL(AS_CLOSURE(argv[0])->function->docstring) : NONE_VAL(); +} + +static KrkValue _bound_get_doc(int argc, KrkValue argv[]) { + KrkBoundMethod * boundMethod = AS_BOUND_METHOD(argv[0]); + return _closure_get_doc(1, (KrkValue[]){OBJECT_VAL(boundMethod->method)}); +} + +static KrkValue nativeFunctionName(KrkValue func) { + const char * string = ((KrkNative*)AS_OBJECT(func))->name; + size_t len = strlen(string); + return OBJECT_VAL(krk_copyString(string,len)); +} + +static KrkValue _closure_get_name(int argc, KrkValue argv[]) { + if (!IS_CLOSURE(argv[0])) return nativeFunctionName(argv[0]); + return AS_CLOSURE(argv[0])->function->name ? OBJECT_VAL(AS_CLOSURE(argv[0])->function->name) : OBJECT_VAL(S("")); +} + +static KrkValue _bound_get_name(int argc, KrkValue argv[]) { + KrkBoundMethod * boundMethod = AS_BOUND_METHOD(argv[0]); + return _closure_get_name(1, (KrkValue[]){OBJECT_VAL(boundMethod->method)}); +} + +static KrkValue _closure_str(int argc, KrkValue argv[]) { + KrkValue s = _closure_get_name(argc, argv); + krk_push(s); + + size_t len = AS_STRING(s)->length + sizeof(""); + char * tmp = malloc(len); + sprintf(tmp, "", AS_CSTRING(s)); + s = OBJECT_VAL(krk_copyString(tmp,len)); + free(tmp); + krk_pop(); + return s; +} + +static KrkValue _bound_str(int argc, KrkValue argv[]) { + KrkValue s = _bound_get_name(argc, argv); + krk_push(s); + + size_t len = AS_STRING(s)->length + sizeof(""); + char * tmp = malloc(len); + sprintf(tmp, "", AS_CSTRING(s)); + s = OBJECT_VAL(krk_copyString(tmp,len)); + free(tmp); + krk_pop(); + return s; +} + +static KrkValue _closure_get_file(int argc, KrkValue argv[]) { + 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("")); +} + +static KrkValue _bound_get_file(int argc, KrkValue argv[]) { + KrkBoundMethod * boundMethod = AS_BOUND_METHOD(argv[0]); + return _closure_get_file(1, (KrkValue[]){OBJECT_VAL(boundMethod->method)}); +} + +static KrkValue _strBase(int argc, KrkValue argv[]) { + KrkClass * type = AS_CLASS(krk_typeOf(1,(KrkValue[]){argv[0]})); + size_t len = sizeof("") + type->name->length; + char * tmp = malloc(len); + if (IS_OBJECT(argv[0])) { + sprintf(tmp, "", type->name->chars, (void*)AS_OBJECT(argv[0])); + } else { + sprintf(tmp, "", type->name->chars); + } + KrkValue out = OBJECT_VAL(krk_copyString(tmp, strlen(tmp))); + free(tmp); + return out; +} + +static KrkValue _repr_str(int argc, KrkValue argv[]) { + char * str = malloc(3 + AS_STRING(argv[0])->length * 2); + char * tmp = str; + *(tmp++) = '"'; + for (char * c = AS_CSTRING(argv[0]); *c; ++c) { + switch (*c) { + /* XXX: Other non-printables should probably be escaped as well. */ + case '\n': *(tmp++) = '\\'; *(tmp++) = 'n'; break; + case '\r': *(tmp++) = '\\'; *(tmp++) = 'r'; break; + case '\t': *(tmp++) = '\\'; *(tmp++) = 't'; break; + case '"': *(tmp++) = '\\'; *(tmp++) = '"'; break; + case 27: *(tmp++) = '\\'; *(tmp++) = '['; break; + default: *(tmp++) = *c; break; + } + } + *(tmp++) = '"'; + *(tmp++) = '\0'; + KrkString * out = krk_copyString(str, tmp-str-1); + free(str); + return OBJECT_VAL(out); +} + +static KrkValue _int_to_str(int argc, KrkValue argv[]) { + char tmp[100]; + size_t l = sprintf(tmp, "%ld", (long)AS_INTEGER(argv[0])); + return OBJECT_VAL(krk_copyString(tmp, l)); +} + +static KrkValue _float_to_str(int argc, KrkValue argv[]) { + char tmp[100]; + size_t l = sprintf(tmp, "%g", AS_FLOATING(argv[0])); + return OBJECT_VAL(krk_copyString(tmp, l)); +} + +static KrkValue _bool_to_str(int argc, KrkValue argv[]) { + return OBJECT_VAL((AS_BOOLEAN(argv[0]) ? S("True") : S("False"))); +} + +static KrkValue _none_to_str(int argc, KrkValue argv[]) { + return OBJECT_VAL(S("None")); +} + +void krk_initVM(int flags) { + vm.flags = flags; + KRK_PAUSE_GC(); + + resetStack(); + vm.objects = NULL; + vm.bytesAllocated = 0; + vm.nextGC = 1024 * 1024; + vm.grayCount = 0; + vm.grayCapacity = 0; + vm.grayStack = NULL; + krk_initTable(&vm.globals); + krk_initTable(&vm.strings); + memset(vm.specialMethodNames,0,sizeof(vm.specialMethodNames)); + + vm.specialMethodNames[METHOD_INIT] = OBJECT_VAL(S("__init__")); + vm.specialMethodNames[METHOD_STR] = OBJECT_VAL(S("__str__")); + vm.specialMethodNames[METHOD_REPR] = OBJECT_VAL(S("__repr__")); + vm.specialMethodNames[METHOD_GET] = OBJECT_VAL(S("__get__")); + vm.specialMethodNames[METHOD_SET] = OBJECT_VAL(S("__set__")); + vm.specialMethodNames[METHOD_CLASS]= OBJECT_VAL(S("__class__")); + vm.specialMethodNames[METHOD_NAME] = OBJECT_VAL(S("__name__")); + vm.specialMethodNames[METHOD_FILE] = OBJECT_VAL(S("__file__")); + vm.specialMethodNames[METHOD_INT] = OBJECT_VAL(S("__int__")); + vm.specialMethodNames[METHOD_CHR] = OBJECT_VAL(S("__chr__")); + vm.specialMethodNames[METHOD_FLOAT]= OBJECT_VAL(S("__float__")); + vm.specialMethodNames[METHOD_LEN] = OBJECT_VAL(S("__len__")); + vm.specialMethodNames[METHOD_DOC] = OBJECT_VAL(S("__doc__")); + vm.specialMethodNames[METHOD_BASE] = OBJECT_VAL(S("__base__")); + vm.specialMethodNames[METHOD_GETSLICE] = OBJECT_VAL(S("__getslice__")); + + /* Create built-in class `object` */ + vm.objectClass = krk_newClass(S("object")); + krk_attachNamedObject(&vm.globals, "object", (KrkObj*)vm.objectClass); + krk_defineNative(&vm.objectClass->methods, ":__class__", krk_typeOf); + krk_defineNative(&vm.objectClass->methods, ".__dir__", krk_dirObject); + krk_defineNative(&vm.objectClass->methods, ".__str__", _strBase); + krk_defineNative(&vm.objectClass->methods, ".__repr__", _strBase); /* Override if necesary */ + + vm.builtins = krk_newInstance(vm.objectClass); + krk_attachNamedObject(&vm.globals, "__builtins__", (KrkObj*)vm.builtins); + + /* Add exception classes */ + ADD_EXCEPTION_CLASS(vm.exceptions.baseException, "Exception", vm.objectClass); + /* base exception class gets an init that takes an optional string */ + krk_defineNative(&vm.exceptions.baseException->methods, ".__init__", krk_initException); + ADD_EXCEPTION_CLASS(vm.exceptions.typeError, "TypeError", vm.exceptions.baseException); + ADD_EXCEPTION_CLASS(vm.exceptions.argumentError, "ArgumentError", vm.exceptions.baseException); + ADD_EXCEPTION_CLASS(vm.exceptions.indexError, "IndexError", vm.exceptions.baseException); + ADD_EXCEPTION_CLASS(vm.exceptions.keyError, "KeyError", vm.exceptions.baseException); + ADD_EXCEPTION_CLASS(vm.exceptions.attributeError, "AttributeError", vm.exceptions.baseException); + ADD_EXCEPTION_CLASS(vm.exceptions.nameError, "NameError", vm.exceptions.baseException); + ADD_EXCEPTION_CLASS(vm.exceptions.importError, "ImportError", vm.exceptions.baseException); + ADD_EXCEPTION_CLASS(vm.exceptions.ioError, "IOError", vm.exceptions.baseException); + + /* Build classes for basic types */ + ADD_BASE_CLASS(vm.baseClasses.typeClass, "type", vm.objectClass); + krk_defineNative(&vm.baseClasses.typeClass->methods, ":__base__", krk_baseOfClass); + krk_defineNative(&vm.baseClasses.typeClass->methods, ":__file__", krk_fileOfClass); + krk_defineNative(&vm.baseClasses.typeClass->methods, ":__doc__", krk_docOfClass); + krk_defineNative(&vm.baseClasses.typeClass->methods, ":__name__", krk_nameOfClass); + krk_defineNative(&vm.baseClasses.typeClass->methods, ".__str__", _class_to_str); + krk_defineNative(&vm.baseClasses.typeClass->methods, ".__repr__", _class_to_str); + ADD_BASE_CLASS(vm.baseClasses.intClass, "int", vm.objectClass); + krk_defineNative(&vm.baseClasses.intClass->methods, ".__int__", _noop); + krk_defineNative(&vm.baseClasses.intClass->methods, ".__float__", _int_to_floating); + krk_defineNative(&vm.baseClasses.intClass->methods, ".__chr__", _int_to_char); + krk_defineNative(&vm.baseClasses.intClass->methods, ".__str__", _int_to_str); + krk_defineNative(&vm.baseClasses.intClass->methods, ".__repr__", _int_to_str); + ADD_BASE_CLASS(vm.baseClasses.floatClass, "float", vm.objectClass); + krk_defineNative(&vm.baseClasses.floatClass->methods, ".__int__", _floating_to_int); + krk_defineNative(&vm.baseClasses.floatClass->methods, ".__float__", _noop); + krk_defineNative(&vm.baseClasses.floatClass->methods, ".__str__", _float_to_str); + krk_defineNative(&vm.baseClasses.floatClass->methods, ".__repr__", _float_to_str); + ADD_BASE_CLASS(vm.baseClasses.boolClass, "bool", vm.objectClass); + krk_defineNative(&vm.baseClasses.boolClass->methods, ".__str__", _bool_to_str); + krk_defineNative(&vm.baseClasses.boolClass->methods, ".__repr__", _bool_to_str); + ADD_BASE_CLASS(vm.baseClasses.noneTypeClass, "NoneType", vm.objectClass); + krk_defineNative(&vm.baseClasses.noneTypeClass->methods, ".__str__", _none_to_str); + krk_defineNative(&vm.baseClasses.noneTypeClass->methods, ".__repr__", _none_to_str); + ADD_BASE_CLASS(vm.baseClasses.strClass, "str", vm.objectClass); + krk_defineNative(&vm.baseClasses.strClass->methods, ".__str__", _noop); + krk_defineNative(&vm.baseClasses.strClass->methods, ".__repr__", _repr_str); + krk_defineNative(&vm.baseClasses.strClass->methods, ".__len__", _string_length); + krk_defineNative(&vm.baseClasses.strClass->methods, ".__get__", _string_get); + krk_defineNative(&vm.baseClasses.strClass->methods, ".__set__", _strings_are_immutable); + krk_defineNative(&vm.baseClasses.strClass->methods, ".__int__", _string_to_int); + krk_defineNative(&vm.baseClasses.strClass->methods, ".__float__", _string_to_float); + krk_defineNative(&vm.baseClasses.strClass->methods, ".__getslice__", _string_get_slice); + ADD_BASE_CLASS(vm.baseClasses.functionClass, "function", vm.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); + ADD_BASE_CLASS(vm.baseClasses.methodClass, "method", vm.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.builtins->fields, "hash_new", krk_expose_hash_new); + krk_defineNative(&vm.builtins->fields, "hash_set", krk_expose_hash_set); + krk_defineNative(&vm.builtins->fields, "hash_get", krk_expose_hash_get); + krk_defineNative(&vm.builtins->fields, "hash_key_at_index", krk_expose_hash_key_at_index); + krk_defineNative(&vm.builtins->fields, "hash_capacity", krk_expose_hash_capacity); + krk_defineNative(&vm.builtins->fields, "hash_count", krk_expose_hash_count); + + krk_defineNative(&vm.builtins->fields, "list_new", krk_expose_list_new); + krk_defineNative(&vm.builtins->fields, "list_get", krk_expose_list_get); + krk_defineNative(&vm.builtins->fields, "list_set", krk_expose_list_set); + krk_defineNative(&vm.builtins->fields, "list_append", krk_expose_list_append); + krk_defineNative(&vm.builtins->fields, "list_length", krk_expose_list_length); + + krk_defineNative(&vm.builtins->fields, "set_tracing", krk_set_tracing); + +#ifndef NO_SYSTEM_BINDS + /* Set some other built-ins for the system module */ + krk_defineNative(&vm.builtins->fields, "sleep", krk_sleep); + krk_defineNative(&vm.builtins->fields, "uname", krk_uname); +#endif + + krk_defineNative(&vm.globals, "listOf", krk_list_of); + krk_defineNative(&vm.globals, "dictOf", krk_dict_of); + krk_defineNative(&vm.globals, "isinstance", krk_isinstance); + krk_defineNative(&vm.globals, "type", krk_typeOf); + + /* Now read the builtins module */ + KrkValue builtinsModule = krk_interpret(_builtins_src,1,"__builtins__","__builtins__"); + if (!IS_OBJECT(builtinsModule)) { + fprintf(stderr, "VM startup failure: Failed to load __builtins__ module.\n"); + } + + resetStack(); + KRK_RESUME_GC(); +} + +void krk_freeVM() { + krk_freeTable(&vm.globals); + krk_freeTable(&vm.strings); + krk_freeTable(&vm.modules); + memset(vm.specialMethodNames,0,sizeof(vm.specialMethodNames)); + krk_freeObjects(); + FREE_ARRAY(size_t, vm.stack, vm.stackSize); +} + +static int isFalsey(KrkValue value) { + return IS_NONE(value) || (IS_BOOLEAN(value) && !AS_BOOLEAN(value)) || + (IS_INTEGER(value) && !AS_INTEGER(value)); + /* Objects in the future: */ + /* IS_STRING && length == 0; IS_ARRAY && length == 0; IS_INSTANCE && __bool__ returns 0... */ +} + +const char * krk_typeName(KrkValue value) { + return AS_CLASS(krk_typeOf(1, (KrkValue[]){value}))->name->chars; +} + +#define MAKE_BIN_OP(name,operator) \ + static KrkValue name (KrkValue a, KrkValue b) { \ + if (IS_INTEGER(a) && IS_INTEGER(b)) return INTEGER_VAL(AS_INTEGER(a) operator AS_INTEGER(b)); \ + if (IS_FLOATING(a)) { \ + if (IS_INTEGER(b)) return FLOATING_VAL(AS_FLOATING(a) operator (double)AS_INTEGER(b)); \ + else if (IS_FLOATING(b)) return FLOATING_VAL(AS_FLOATING(a) operator AS_FLOATING(b)); \ + } else if (IS_FLOATING(b)) { \ + if (IS_INTEGER(a)) return FLOATING_VAL((double)AS_INTEGER(a) operator AS_FLOATING(b)); \ + } \ + krk_runtimeError(vm.exceptions.typeError, "Incompatible types for binary operand %s: %s and %s", #operator, krk_typeName(a), krk_typeName(b)); \ + return NONE_VAL(); \ + } + +MAKE_BIN_OP(add,+) +MAKE_BIN_OP(subtract,-) +MAKE_BIN_OP(multiply,*) +MAKE_BIN_OP(divide,/) + +#define MAKE_BIT_OP(name,operator) \ + static KrkValue name (KrkValue a, KrkValue b) { \ + if (IS_INTEGER(a) && IS_INTEGER(b)) return INTEGER_VAL(AS_INTEGER(a) operator AS_INTEGER(b)); \ + krk_runtimeError(vm.exceptions.typeError, "Incompatible types for binary operand %s: %s and %s", #operator, krk_typeName(a), krk_typeName(b)); \ + return NONE_VAL(); \ + } + +MAKE_BIT_OP(bitor,|) +MAKE_BIT_OP(bitxor,^) +MAKE_BIT_OP(bitand,&) +MAKE_BIT_OP(shiftleft,<<) +MAKE_BIT_OP(shiftright,>>) +MAKE_BIT_OP(modulo,%) /* not a bit op, but doesn't work on floating point */ + +#define MAKE_COMPARATOR(name, operator) \ + static KrkValue name (KrkValue a, KrkValue b) { \ + if (IS_INTEGER(a) && IS_INTEGER(b)) return BOOLEAN_VAL(AS_INTEGER(a) operator AS_INTEGER(b)); \ + if (IS_FLOATING(a)) { \ + if (IS_INTEGER(b)) return BOOLEAN_VAL(AS_FLOATING(a) operator AS_INTEGER(b)); \ + else if (IS_FLOATING(b)) return BOOLEAN_VAL(AS_FLOATING(a) operator AS_FLOATING(b)); \ + } else if (IS_FLOATING(b)) { \ + if (IS_INTEGER(a)) return BOOLEAN_VAL(AS_INTEGER(a) operator AS_INTEGER(b)); \ + } \ + krk_runtimeError(vm.exceptions.typeError, "Can not compare types %s and %s", krk_typeName(a), krk_typeName(b)); \ + return NONE_VAL(); \ + } + +MAKE_COMPARATOR(less, <) +MAKE_COMPARATOR(greater, >) + +static void concatenate(const char * a, const char * b, size_t al, size_t bl) { + size_t length = al + bl; + char * chars = ALLOCATE(char, length + 1); + memcpy(chars, a, al); + memcpy(chars + al, b, bl); + chars[length] = '\0'; + + KrkString * result = krk_takeString(chars, length); + krk_pop(); + krk_pop(); + krk_push(OBJECT_VAL(result)); +} + +static void addObjects() { + KrkValue _b = krk_peek(0); + KrkValue _a = krk_peek(1); + + if (IS_STRING(_a)) { + KrkString * a = AS_STRING(_a); + if (IS_STRING(_b)) { + KrkString * b = AS_STRING(_b); + concatenate(a->chars,b->chars,a->length,b->length); + return; + } + if (krk_bindMethod(AS_CLASS(krk_typeOf(1,(KrkValue[]){_b})), AS_STRING(vm.specialMethodNames[METHOD_STR]))) { + KrkValue result; + int t = krk_callValue(krk_peek(0), 0); + if (t == 2) { + result = krk_pop(); + } else if (t == 1) { + result = krk_runNext(); + } else { + krk_runtimeError(vm.exceptions.typeError, "__str__ failed to call str on %s", krk_typeName(_b)); + return; + } + if (!IS_STRING(result)) { + krk_runtimeError(vm.exceptions.typeError, "__str__ produced something that wasn't a string: %s", krk_typeName(result)); + return; + } + krk_push(result); + concatenate(a->chars,AS_STRING(result)->chars,a->length,AS_STRING(result)->length); + return; + } else { + char tmp[256] = {0}; + sprintf(tmp, "<%s>", krk_typeName(_b)); + concatenate(a->chars,tmp,a->length,strlen(tmp)); + } + } else { + krk_runtimeError(vm.exceptions.typeError, "Can not concatenate types %s and %s", krk_typeName(_a), krk_typeName(_b)); \ + } +} + +#define READ_BYTE() (*frame->ip++) +#define BINARY_OP(op) { KrkValue b = krk_pop(); KrkValue a = krk_pop(); krk_push(op(a,b)); break; } +#define READ_CONSTANT(s) (frame->closure->function->chunk.constants.values[readBytes(frame,s)]) +#define READ_STRING(s) AS_STRING(READ_CONSTANT(s)) + +static inline size_t readBytes(CallFrame * frame, int num) { + size_t out = READ_BYTE(); + while (--num) { + out <<= 8; + out |= (READ_BYTE() & 0xFF); + } + return out; +} + static int handleException() { int stackOffset, frameOffset; int exitSlot = (vm.exitOnFrame >= 0) ? vm.frames[vm.exitOnFrame].slots : 0; @@ -1041,38 +1209,13 @@ static int handleException() { return 0; } -static KrkValue _noop(int argc, KrkValue argv[]) { - return argv[0]; -} - -static KrkValue _floating_to_int(int argc, KrkValue argv[]) { - return INTEGER_VAL((long)AS_FLOATING(argv[0])); -} - -static KrkValue _int_to_floating(int argc, KrkValue argv[]) { - return FLOATING_VAL((double)AS_INTEGER(argv[0])); -} - -static KrkValue _int_to_char(int argc, KrkValue argv[]) { - char tmp[2] = {AS_INTEGER(argv[0]), 0}; - return OBJECT_VAL(krk_copyString(tmp,1)); -} - -static void bindSpecialMethod(NativeFn method, int arity) { - KRK_PAUSE_GC(); - KrkBoundMethod * bound = krk_newBoundMethod(krk_peek(0), (KrkObj*)boundNative(method,arity)); - krk_pop(); /* The original object */ - krk_push(OBJECT_VAL(bound)); - KRK_RESUME_GC(); -} - static void dumpStack(CallFrame * frame) { fprintf(stderr, " | "); size_t i = 0; for (KrkValue * slot = vm.stack; slot < vm.stackTop; slot++) { fprintf(stderr, "[ "); if (i == frame->slots) fprintf(stderr, "*"); - krk_printValue(stderr, *slot); + krk_printValueSafe(stderr, *slot); fprintf(stderr, " ]"); i++; } @@ -1203,6 +1346,29 @@ int krk_loadModule(KrkString * name, KrkValue * moduleOut) { return 0; } +static int valueGetProperty(KrkString * name) { + KrkClass * objectClass; + if (IS_INSTANCE(krk_peek(0))) { + KrkInstance * instance = AS_INSTANCE(krk_peek(0)); + KrkValue value; + if (krk_tableGet(&instance->fields, OBJECT_VAL(name), &value)) { + krk_pop(); + krk_push(value); + return 1; + } + objectClass = instance->_class; + } else { + objectClass = AS_CLASS(krk_typeOf(1, (KrkValue[]){krk_peek(0)})); + } + + /* See if the base class for this non-instance type has a method available */ + if (krk_bindMethod(objectClass, name)) { + return 1; + } + + return 0; +} + static KrkValue run() { CallFrame* frame = &vm.frames[vm.frameCount - 1]; @@ -1223,16 +1389,12 @@ static KrkValue run() { uint32_t args = readBytes(frame, operandWidth); for (uint32_t i = 0; i < args; ++i) { KrkValue printable = krk_peek(args-i-1); - if (!IS_STRING(printable)) { - krk_push(OBJECT_VAL(S(""))); - krk_push(printable); - addObjects(); - printable = krk_peek(0); + if (IS_STRING(printable)) { /* krk_printValue runs repr */ + fprintf(stdout, "%s", AS_CSTRING(printable)); } else { - krk_push(printable); + krk_printValue(stdout, printable); } - fprintf(stdout, "%s%s", AS_CSTRING(printable), (i == args - 1) ? "\n" : " "); - krk_pop(); + fputc((i == args - 1) ? '\n' : ' ', stdout); } for (uint32_t i = 0; i < args; ++i) { krk_pop(); @@ -1394,7 +1556,7 @@ static KrkValue run() { case OP_CALL_LONG: case OP_CALL: { int argCount = readBytes(frame, operandWidth); - if (!callValue(krk_peek(argCount), argCount)) { + if (!krk_callValue(krk_peek(argCount), argCount)) { if (vm.flags & KRK_HAS_EXCEPTION) goto _finishException; return NONE_VAL(); } @@ -1405,7 +1567,7 @@ static KrkValue run() { * top of the stack, so we don't have calculate arity at compile time. */ case OP_CALL_STACK: { int argCount = AS_INTEGER(krk_pop()); - if (!callValue(krk_peek(argCount), argCount)) { + if (!krk_callValue(krk_peek(argCount), argCount)) { if (vm.flags & KRK_HAS_EXCEPTION) goto _finishException; return NONE_VAL(); } @@ -1466,126 +1628,11 @@ static KrkValue run() { case OP_GET_PROPERTY_LONG: case OP_GET_PROPERTY: { KrkString * name = READ_STRING(operandWidth); - switch (krk_peek(0).type) { - case VAL_OBJECT: - switch (OBJECT_TYPE(krk_peek(0))) { - case OBJ_INSTANCE: { - KrkInstance * instance = AS_INSTANCE(krk_peek(0)); - KrkValue value; - if (krk_tableGet(&instance->fields, OBJECT_VAL(name), &value)) { - krk_pop(); - krk_push(value); - } else if (!bindMethod(instance->_class, name)) { - /* Try synthentic properties */ - if (krk_valuesEqual(OBJECT_VAL(name), vm.specialMethodNames[METHOD_CLASS])) { - krk_pop(); - krk_push(OBJECT_VAL(instance->_class)); - } else { - goto _undefined; - } - } - break; - } - case OBJ_CLASS: { - KrkClass * _class = AS_CLASS(krk_peek(0)); - if (krk_valuesEqual(OBJECT_VAL(name), vm.specialMethodNames[METHOD_NAME])) { - krk_pop(); /* class */ - krk_push(OBJECT_VAL(_class->name)); - } else if (krk_valuesEqual(OBJECT_VAL(name), vm.specialMethodNames[METHOD_FILE])) { - krk_pop(); - krk_push(OBJECT_VAL(_class->filename)); - } else if (krk_valuesEqual(OBJECT_VAL(name), vm.specialMethodNames[METHOD_DOC])) { - KrkValue out = _class->docstring ? OBJECT_VAL(_class->docstring) : NONE_VAL(); - krk_pop(); - krk_push(out); - } else if (krk_valuesEqual(OBJECT_VAL(name), vm.specialMethodNames[METHOD_BASE])) { - KrkValue out = _class->base ? OBJECT_VAL(_class->base) : NONE_VAL(); - krk_pop(); - krk_push(out); - } else { - goto _undefined; - } - break; - } - case OBJ_STRING: { - if (krk_valuesEqual(OBJECT_VAL(name), vm.specialMethodNames[METHOD_LEN])) { - bindSpecialMethod(_string_length,0); - } else if (krk_valuesEqual(OBJECT_VAL(name), vm.specialMethodNames[METHOD_GET])) { - bindSpecialMethod(_string_get,1); - } else if (krk_valuesEqual(OBJECT_VAL(name), vm.specialMethodNames[METHOD_INT])) { - bindSpecialMethod(_string_to_int,0); - } else if (krk_valuesEqual(OBJECT_VAL(name), vm.specialMethodNames[METHOD_FLOAT])) { - bindSpecialMethod(_string_to_float,0); - } else if (krk_valuesEqual(OBJECT_VAL(name), vm.specialMethodNames[METHOD_GETSLICE])) { - bindSpecialMethod(_string_get_slice,2); - } else if (krk_valuesEqual(OBJECT_VAL(name), vm.specialMethodNames[METHOD_SET])) { - krk_runtimeError(vm.exceptions.typeError, "Strings are not mutable."); - goto _finishException; - } else { - goto _undefined; - } - break; - } - case OBJ_CLOSURE: { - if (krk_valuesEqual(OBJECT_VAL(name), vm.specialMethodNames[METHOD_DOC])) { - KrkClosure * closure = AS_CLOSURE(krk_peek(0)); - KrkValue out = closure->function->docstring ? OBJECT_VAL(closure->function->docstring) : NONE_VAL(); - krk_pop(); - krk_push(out); - } else { - goto _undefined; - } - break; - } - case OBJ_BOUND_METHOD: { - if (krk_valuesEqual(OBJECT_VAL(name), vm.specialMethodNames[METHOD_DOC])) { - KrkBoundMethod * boundMethod = AS_BOUND_METHOD(krk_peek(0)); - KrkObj * method = boundMethod->method; - KrkValue out = NONE_VAL(); - switch (method->type) { - case OBJ_CLOSURE: out = ((KrkClosure*)method)->function->docstring ? - OBJECT_VAL(((KrkClosure*)method)->function->docstring) : NONE_VAL(); - break; - default: - break; - } - krk_pop(); - krk_push(out); - } else { - goto _undefined; - } - break; - } - default: - goto _undefined; - } - break; - case VAL_FLOATING: { - if (krk_valuesEqual(OBJECT_VAL(name), vm.specialMethodNames[METHOD_INT])) { - bindSpecialMethod(_floating_to_int,0); - } else if (krk_valuesEqual(OBJECT_VAL(name), vm.specialMethodNames[METHOD_FLOAT])) { - bindSpecialMethod(_noop,0); - } else goto _undefined; - break; - } - case VAL_INTEGER: { - if (krk_valuesEqual(OBJECT_VAL(name), vm.specialMethodNames[METHOD_FLOAT])) { - bindSpecialMethod(_int_to_floating,0); - } else if (krk_valuesEqual(OBJECT_VAL(name), vm.specialMethodNames[METHOD_INT])) { - bindSpecialMethod(_noop,0); - } else if (krk_valuesEqual(OBJECT_VAL(name), vm.specialMethodNames[METHOD_CHR])) { - bindSpecialMethod(_int_to_char,0); - } else goto _undefined; - break; - } - default: - krk_runtimeError(vm.exceptions.attributeError, "Don't know how to retreive properties for %s yet", krk_typeName(krk_peek(0))); - goto _finishException; + if (!valueGetProperty(name)) { + krk_runtimeError(vm.exceptions.attributeError, "'%s' object has no attribute '%s'", krk_typeName(krk_peek(0)), name->chars); + goto _finishException; } break; -_undefined: - krk_runtimeError(vm.exceptions.attributeError, "'%s' object has no attribute '%s'", krk_typeName(krk_peek(0)), name->chars); - goto _finishException; } case OP_SET_PROPERTY_LONG: case OP_SET_PROPERTY: { @@ -1627,7 +1674,7 @@ _undefined: case OP_GET_SUPER: { KrkString * name = READ_STRING(operandWidth); KrkClass * superclass = AS_CLASS(krk_pop()); - if (!bindMethod(superclass, name)) { + if (!krk_bindMethod(superclass, name)) { return NONE_VAL(); } break; @@ -1671,7 +1718,7 @@ KrkValue krk_interpret(const char * src, int newScope, char * fromName, char * f krk_pop(); krk_push(OBJECT_VAL(closure)); - callValue(OBJECT_VAL(closure), 0); + krk_callValue(OBJECT_VAL(closure), 0); return run(); } diff --git a/vm.h b/vm.h index 5d5e500..c4f7cc7 100644 --- a/vm.h +++ b/vm.h @@ -19,6 +19,7 @@ typedef struct { typedef enum { METHOD_INIT, METHOD_STR, + METHOD_REPR, METHOD_GET, METHOD_SET, METHOD_CLASS, @@ -101,6 +102,7 @@ extern KrkValue krk_interpret(const char * src, int newScope, char *, char *); extern KrkValue krk_runfile(const char * fileName, int newScope, char *, char *); extern void krk_push(KrkValue value); extern KrkValue krk_pop(void); +extern KrkValue krk_peek(int distance); extern const char * krk_typeName(KrkValue value); extern void krk_defineNative(KrkTable * table, const char * name, NativeFn function); extern void krk_attachNamedObject(KrkTable * table, const char name[], KrkObj * obj); @@ -114,3 +116,6 @@ extern KrkValue krk_dictGet(KrkValue dictClass, KrkInstance * dict, KrkValue key extern void krk_dictSet(KrkValue dictClass, KrkInstance * dict, KrkValue key, KrkValue value); extern KrkInstance * krk_dictCreate(KrkValue * outClass); extern KrkValue krk_runNext(void); +extern KrkValue krk_typeOf(int argc, KrkValue argv[]); +extern int krk_bindMethod(KrkClass * _class, KrkString * name); +extern int krk_callValue(KrkValue callee, int argCount);