diff --git a/newtest.cosmo b/newtest.cosmo index 7471ded..e80efd4 100644 --- a/newtest.cosmo +++ b/newtest.cosmo @@ -1,8 +1,15 @@ --- makes a hundred thousand entries in the hashtable -var test = {} +class test + function __init(self, x) + self.x = x + end -for (var i = 0; i < 100000; i=i+1) do - test[i] = "" .. i*i + function print(self) + print(self.x) + + return self.x + end end -print(test[4]) -- should print '16' +var t = test("hello") + +print(t.print()) \ No newline at end of file diff --git a/src/cbaselib.c b/src/cbaselib.c index a4247e5..3e7f4db 100644 --- a/src/cbaselib.c +++ b/src/cbaselib.c @@ -4,7 +4,7 @@ #include "cobj.h" #include "cmem.h" -void cosmoB_loadlibrary(CState *state) { +void cosmoB_loadLibrary(CState *state) { cosmoM_freezeGC(state); cosmoV_register(state, "print", cosmoV_newObj(cosmoO_newCFunction(state, cosmoB_print))); cosmoM_unfreezeGC(state); @@ -18,4 +18,28 @@ CValue cosmoB_print(CState *state, int nargs, CValue *args) { printf("\n"); return cosmoV_newNil(); // print doesn't return any args +} + +CValue cosmoB_dsetMeta(CState *state, int nargs, CValue *args) { + if (nargs == 2) { + CObjObject *obj = cosmoV_readObject(args[0]); // object to set meta too + CObjObject *meta = cosmoV_readObject(args[1]); + + obj->meta = meta; // boom done + } + + return cosmoV_newNil(); // nothing +} +CValue cosmoB_dgetMeta(CState *state, int nargs, CValue *args) { + return cosmoV_newObj(cosmoV_readObject(args[0])->meta); // just return the meta +} + +void cosmoB_loadDebug(CState *state) { + cosmoV_pushString(state, "getMeta"); + cosmoV_pushCFunction(state, cosmoB_dgetMeta); + cosmoV_pushString(state, "setMeta"); + cosmoV_pushCFunction(state, cosmoB_dsetMeta); + cosmoV_pushObject(state, 2); + + state->metaObj = cosmoV_readObject(*cosmoV_pop(state)); } \ No newline at end of file diff --git a/src/cbaselib.h b/src/cbaselib.h index b3aa377..f1b9ec0 100644 --- a/src/cbaselib.h +++ b/src/cbaselib.h @@ -3,7 +3,9 @@ #include "cstate.h" -COSMO_API void cosmoB_loadlibrary(CState *state); + +COSMO_API void cosmoB_loadLibrary(CState *state); +COSMO_API void cosmoB_loadDebug(CState *state); COSMO_API CValue cosmoB_print(CState *state, int nargs, CValue *args); #endif \ No newline at end of file diff --git a/src/cmem.c b/src/cmem.c index 6a0cc13..706caf5 100644 --- a/src/cmem.c +++ b/src/cmem.c @@ -4,6 +4,7 @@ #include "ctable.h" #include "cparse.h" #include "cobj.h" +#include "cbaselib.h" /* copy buffer to new larger buffer, and free the old buffer @@ -213,6 +214,8 @@ void markRoots(CState *state) { for (int i = 0; i < INTERNALSTRING_MAX; i++) markObject(state, (CObj*)state->internalStrings[i]); + // mark our meta object + markObject(state, (CObj*)state->metaObj); traceGrays(state); } diff --git a/src/cobj.c b/src/cobj.c index eef38c8..111daae 100644 --- a/src/cobj.c +++ b/src/cobj.c @@ -94,7 +94,7 @@ bool cosmoO_equal(CObj* obj1, CObj* obj2) { CObjObject *cosmoO_newObject(CState *state) { CObjObject *obj = (CObjObject*)cosmoO_allocateBase(state, sizeof(CObjObject), COBJ_OBJECT); - obj->meta = NULL; + obj->meta = state->metaObj; cosmoV_pushValue(state, cosmoV_newObj(obj)); // so out GC can keep track of it cosmoT_initTable(state, &obj->tbl, ARRAY_START); cosmoV_pop(state); @@ -118,10 +118,19 @@ CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func) { return cfunc; } +CObjMethod *cosmoO_newCMethod(CState *state, CObjCFunction *func, CObjObject *obj) { + CObjMethod *method = (CObjMethod*)cosmoO_allocateBase(state, sizeof(CObjMethod), COBJ_METHOD); + method->cfunc = func; + method->obj = obj; + method->isCFunc = true; + return method; +} + CObjMethod *cosmoO_newMethod(CState *state, CObjClosure *func, CObjObject *obj) { CObjMethod *method = (CObjMethod*)cosmoO_allocateBase(state, sizeof(CObjMethod), COBJ_METHOD); method->closure = func; method->obj = obj; + method->isCFunc = false; return method; } diff --git a/src/cobj.h b/src/cobj.h index f38624c..0c00d16 100644 --- a/src/cobj.h +++ b/src/cobj.h @@ -65,7 +65,11 @@ typedef struct CObjClosure { typedef struct CObjMethod { CommonHeader; // "is a " CObj CObjObject *obj; // obj this method is bound too - CObjClosure *closure; // TODO: change this to a union to a pointer to a closure object or a c function object + union { + CObjClosure *closure; + CObjCFunction *cfunc; + }; + bool isCFunc; } CObjMethod; typedef struct CObjUpval { @@ -82,12 +86,12 @@ typedef struct CObjUpval { #define IS_METHOD(x) isObjType(x, COBJ_METHOD) #define IS_CLOSURE(x) isObjType(x, COBJ_CLOSURE) -#define cosmoV_readString(x) ((CObjString*)cosmoV_readObj(x)) -#define cosmoV_readObject(x) ((CObjObject*)cosmoV_readObj(x)) -#define cosmoV_readFunction(x) ((CObjFunction*)cosmoV_readObj(x)) -#define cosmoV_readCFunction(x) (((CObjCFunction*)cosmoV_readObj(x))->cfunc) -#define cosmoV_readMethod(x) ((CObjMethod*)cosmoV_readObj(x)) -#define cosmoV_readClosure(x) ((CObjClosure*)cosmoV_readObj(x)) +#define cosmoV_readString(x) ((CObjString*)cosmoV_readObj((x))) +#define cosmoV_readObject(x) ((CObjObject*)cosmoV_readObj((x))) +#define cosmoV_readFunction(x) ((CObjFunction*)cosmoV_readObj((x))) +#define cosmoV_readCFunction(x) (((CObjCFunction*)cosmoV_readObj((x)))->cfunc) +#define cosmoV_readMethod(x) ((CObjMethod*)cosmoV_readObj((x))) +#define cosmoV_readClosure(x) ((CObjClosure*)cosmoV_readObj((x))) static inline bool isObjType(CValue val, CObjType type) { return IS_OBJ(val) && cosmoV_readObj(val)->type == type; @@ -102,6 +106,7 @@ CObjObject *cosmoO_newObject(CState *state); CObjFunction *cosmoO_newFunction(CState *state); CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func); CObjMethod *cosmoO_newMethod(CState *state, CObjClosure *func, CObjObject *obj); +CObjMethod *cosmoO_newCMethod(CState *state, CObjCFunction *func, CObjObject *obj); CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func); CObjString *cosmoO_toString(CState *state, CObj *val); CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val); diff --git a/src/cparse.c b/src/cparse.c index 7adfb18..abb2fcf 100644 --- a/src/cparse.c +++ b/src/cparse.c @@ -955,7 +955,7 @@ static void functionDeclaration(CParseState *pstate) { } static void returnStatement(CParseState *pstate) { - if (pstate->compiler->type != FTYPE_FUNCTION) { + if (pstate->compiler->type != FTYPE_FUNCTION && pstate->compiler->type != FTYPE_METHOD) { error(pstate, "Expected 'return' in function!"); return; } diff --git a/src/cstate.c b/src/cstate.c index 116708a..929dc7c 100644 --- a/src/cstate.c +++ b/src/cstate.c @@ -30,6 +30,7 @@ CState *cosmoV_newState() { state->top = state->stack; state->frameCount = 0; state->openUpvalues = NULL; + state->metaObj = NULL; cosmoT_initTable(state, &state->strings, 8); // init string table cosmoT_initTable(state, &state->globals, 8); // init global table diff --git a/src/cstate.h b/src/cstate.h index 2b4f3b8..ad70f4d 100644 --- a/src/cstate.h +++ b/src/cstate.h @@ -38,6 +38,7 @@ typedef struct CState { int frameCount; CObjString *internalStrings[INTERNALSTRING_MAX]; // strings used internally by the VM, eg. __init + CObjObject *metaObj; // start met obj for all objects (NULL by default) } CState; COSMO_API CState *cosmoV_newState(); diff --git a/src/cvm.c b/src/cvm.c index fe934ad..5d68dbd 100644 --- a/src/cvm.c +++ b/src/cvm.c @@ -137,6 +137,43 @@ bool call(CState *state, CObjClosure *closure, int args) { return true; } +bool callMethod(CState* state, CObjMethod *method, int args) { + if (method->isCFunc) { + StkPtr savedBase = state->top - args - 1; + + cosmoM_freezeGC(state); + CValue res = method->cfunc->cfunc(state, args+1, savedBase); + cosmoM_unfreezeGC(state); + + state->top = savedBase; + cosmoV_pushValue(state, res); + } else { + CObjClosure *closure = method->closure; + + StkPtr val = state->top - args - 1; + + if (args+1 != closure->function->args) { + runtimeError(state, "Expected %d parameters for %s, got %d!", closure->function->args, closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str, args+1); + return false; + } + + // load function into callframe + pushCallFrame(state, closure, closure->function->args); + + // execute + if (!cosmoV_execute(state)) + return false; + CValue* result = state->top - 1; + + // pop the callframe and return result + popCallFrame(state); + state->top++; + cosmoV_pushValue(state, *result); // and push return value + } + + return true; +} + // args = # of pass parameters COSMOVMRESULT cosmoV_call(CState *state, int args) { StkPtr val = cosmoV_getTop(state, args); // function will always be right above the args @@ -157,26 +194,7 @@ COSMOVMRESULT cosmoV_call(CState *state, int args) { case COBJ_METHOD: { CObjMethod *method = (CObjMethod*)val->val.obj; *val = cosmoV_newObj(method->obj); // sets the object on the stack so the function can reference it in it's first argument - - CObjClosure *closure = method->closure; - - if (args+1 != closure->function->args) { - runtimeError(state, "Expected %d parameters for %s, got %d!", closure->function->args, closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str, args+1); - return COSMOVM_RUNTIME_ERR; - } - - // load function into callframe - pushCallFrame(state, closure, closure->function->args); - - // execute - if (!cosmoV_execute(state)) - return COSMOVM_RUNTIME_ERR; - CValue* result = state->top - 1; - - // pop the callframe and return result - popCallFrame(state); - state->top++; // adjust stack back into place - cosmoV_pushValue(state, *result); // and push return value + callMethod(state, method, args); break; } case COBJ_OBJECT: { @@ -187,25 +205,9 @@ COSMOVMRESULT cosmoV_call(CState *state, int args) { CValue ret; // check if they defined an initalizer - if (cosmoO_getObject(state, metaObj, cosmoV_newObj(state->internalStrings[INTERNALSTRING_INIT]), &ret) && IS_CLOSURE(ret)) { - CObjClosure *closure = cosmoV_readClosure(ret); - - if (args+1 != closure->function->args) { - runtimeError(state, "Expected %d parameters for %s, got %d!", closure->function->args, closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str, args+1); - return COSMOVM_RUNTIME_ERR; - } - - // load function into callframe - pushCallFrame(state, closure, closure->function->args); - - // execute - if (!cosmoV_execute(state)) - return COSMOVM_RUNTIME_ERR; - - // we throw away the return result, it's ignored - // pop the callframe and return the new object :) - popCallFrame(state); - state->top++; // adjust stack back into place + if (cosmoV_getObject(state, metaObj, cosmoV_newObj(state->internalStrings[INTERNALSTRING_INIT]), &ret) && IS_METHOD(ret)) { + callMethod(state, cosmoV_readMethod(ret), args); + cosmoV_pop(state); } else { // no default initalizer if (args != 0) { @@ -221,13 +223,13 @@ COSMOVMRESULT cosmoV_call(CState *state, int args) { case COBJ_CFUNCTION: { // it's a C function, so call it CosmoCFunction cfunc = ((CObjCFunction*)(val->val.obj))->cfunc; - CValue *savedBase = state->top - args - 1; + CValue *savedBase = state->top - args; cosmoM_freezeGC(state); // we don't want a GC event during c api because we don't actually trust the user to know how to evade the GC - CValue res = cfunc(state, args, state->top - args); + CValue res = cfunc(state, args, savedBase); cosmoM_unfreezeGC(state); - state->top = savedBase; + state->top = savedBase - 1; cosmoV_pushValue(state, res); break; } @@ -262,6 +264,24 @@ COSMO_API void cosmoV_pushObject(CState *state, int pairs) { cosmoV_pushValue(state, cosmoV_newObj(newObj)); } +COSMO_API bool cosmoV_getObject(CState *state, CObjObject *object, CValue key, CValue *val) { + if (cosmoO_getObject(state, object, key, val)) { + if (val->type == COSMO_TOBJ ) { + if (val->val.obj->type == COBJ_CLOSURE) { // is it a function? if so, make it a method to the current object + CObjMethod *method = cosmoO_newMethod(state, (CObjClosure*)val->val.obj, object); + *val = cosmoV_newObj(method); + } else if (val->val.obj->type == COBJ_CFUNCTION) { + CObjMethod *method = cosmoO_newCMethod(state, (CObjCFunction*)val->val.obj, object); + *val = cosmoV_newObj(method); + } + } + + return true; + } + + return false; +} + #define BINARYOP(typeConst, op) \ StkPtr valA = cosmoV_getTop(state, 1); \ StkPtr valB = cosmoV_getTop(state, 0); \ @@ -403,11 +423,7 @@ bool cosmoV_execute(CState *state) { CObjObject *object = (CObjObject*)temp->val.obj; CValue val; // to hold our value - cosmoO_getObject(state, object, *key, &val); - if (IS_CLOSURE(val)) { // is it a function? if so, make it a method to the current object - CObjMethod *method = cosmoO_newMethod(state, cosmoV_readClosure(val), object); - val = cosmoV_newObj(method); - } + cosmoV_getObject(state, object, *key, &val); cosmoV_setTop(state, 2); // pops the object & the key cosmoV_pushValue(state, val); // pushes the field result break; diff --git a/src/cvm.h b/src/cvm.h index b56f83e..cf49e0b 100644 --- a/src/cvm.h +++ b/src/cvm.h @@ -15,6 +15,7 @@ typedef enum { // args = # of pass parameters, nresults = # of expected results COSMO_API COSMOVMRESULT cosmoV_call(CState *state, int args); COSMO_API void cosmoV_pushObject(CState *state, int pairs); +COSMO_API bool cosmoV_getObject(CState *state, CObjObject *object, CValue key, CValue *val); // nice to have wrappers diff --git a/src/main.c b/src/main.c index 6e0be75..e3edafc 100644 --- a/src/main.c +++ b/src/main.c @@ -48,7 +48,8 @@ static void repl() { _ACTIVE = true; CState *state = cosmoV_newState(); - cosmoB_loadlibrary(state); + cosmoB_loadLibrary(state); + cosmoB_loadDebug(state); // TODO: there's gotta be a better way to do this cosmoV_register(state, "quit", cosmoV_newObj(cosmoO_newCFunction(state, cosmoB_quitRepl))); @@ -103,7 +104,7 @@ static char *readFile(const char* path) { static void runFile(const char* fileName) { char* script = readFile(fileName); CState *state = cosmoV_newState(); - cosmoB_loadlibrary(state); + cosmoB_loadLibrary(state); cosmoV_register(state, "input", cosmoV_newObj(cosmoO_newCFunction(state, cosmoB_input)));