diff --git a/src/cbaselib.c b/src/cbaselib.c index 54b8a5f..1ac1a72 100644 --- a/src/cbaselib.c +++ b/src/cbaselib.c @@ -153,22 +153,15 @@ void cosmoB_loadLibrary(CState *state) { cosmoV_pushString(state, "sub"); cosmoV_pushCFunction(state, cosmoB_sSub); - cosmoV_makeTable(state, 1); + cosmoV_makeObject(state, 1); // string.* - // register "string" to that table - cosmoV_register(state, 1); - - // make string object for CObjStrings - // sub - cosmoV_pushString(state, "sub"); - cosmoV_pushCFunction(state, cosmoB_sSub); - - cosmoV_makeObject(state, 1); - // grab the object from the stack and set the base protoObject - StkPtr obj = cosmoV_pop(state); + StkPtr obj = cosmoV_getTop(state, 0); state->protoObjects[COBJ_STRING] = cosmoV_readObject(*obj); + + // register "string" to the global table + cosmoV_register(state, 1); } // ================================================================ [DEBUG] ================================================================ diff --git a/src/cmem.h b/src/cmem.h index 7e65b94..ca0eb0e 100644 --- a/src/cmem.h +++ b/src/cmem.h @@ -5,7 +5,7 @@ #include "cstate.h" -//#define GC_STRESS +#define GC_STRESS //#define GC_DEBUG // arrays will grow by a factor of 2 #define GROW_FACTOR 2 diff --git a/src/cobj.c b/src/cobj.c index 50f8e15..08a960d 100644 --- a/src/cobj.c +++ b/src/cobj.c @@ -154,6 +154,7 @@ CObjError *cosmoO_newError(CState *state, CValue err) { CObjError *cerror = (CObjError*)cosmoO_allocateBase(state, sizeof(CObjError), COBJ_ERROR); cerror->err = err; cerror->frameCount = state->frameCount; + cerror->parserError = false; // allocate the callframe cerror->frames = cosmoM_xmalloc(state, sizeof(CCallFrame) * cerror->frameCount); @@ -462,7 +463,7 @@ void printObject(CObj *o) { switch (o->type) { case COBJ_STRING: { CObjString *objStr = (CObjString*)o; - printf("\"%.*s\"", objStr->length, objStr->str); + printf("%.*s", objStr->length, objStr->str); break; } case COBJ_OBJECT: { @@ -474,17 +475,6 @@ void printObject(CObj *o) { printf(" %p", (void*)tbl); break; } - case COBJ_UPVALUE: { - CObjUpval *upval = (CObjUpval*)o; - printf(" -> ", (void*)upval->val); - printValue(*upval->val); - break; - } - case COBJ_CLOSURE: { - CObjClosure *closure = (CObjClosure*)o; - printObject((CObj*)closure->function); // just print the function - break; - } case COBJ_FUNCTION: { CObjFunction *objFunc = (CObjFunction*)o; if (objFunc->name != NULL) @@ -498,12 +488,30 @@ void printObject(CObj *o) { printf(" %p", (void*)objCFunc->cfunc); break; } + case COBJ_ERROR: { + CObjError *err = (CObjError*)o; + printf(" %p -> ", (void*)o); + printValue(err->err); + break; + } case COBJ_METHOD: { CObjMethod *method = (CObjMethod*)o; - printf(" %p : ", (void*)method->obj); + printf(" %p -> ", (void*)method); printValue(method->func); break; } + case COBJ_CLOSURE: { + CObjClosure *closure = (CObjClosure*)o; + printf(" %p -> ", (void*)closure); + printObject((CObj*)closure->function); // just print the function + break; + } + case COBJ_UPVALUE: { + CObjUpval *upval = (CObjUpval*)o; + printf(" %p -> ", (void*)upval->val); + printValue(*upval->val); + break; + } default: printf("", (void*)o); } diff --git a/src/cparse.c b/src/cparse.c index 1a937e9..29a7736 100644 --- a/src/cparse.c +++ b/src/cparse.c @@ -631,8 +631,8 @@ static void table(CParseState *pstate, bool canAssign, Precedence prec) { // grab value/key expression(pstate, 1, true); - // they want to make a table with key:value - if (match(pstate, TOKEN_COLON) && tblType != 1) { + // they want to make a table with key = value + if (match(pstate, TOKEN_EQUAL) && tblType != 1) { tblType = 2; // dictionary-like // grab value @@ -718,14 +718,6 @@ static void dot(CParseState *pstate, bool canAssign, Precedence prec) { writeu8(pstate, OP_INCOBJECT); writeu8(pstate, 128 - 1); writeu16(pstate, name); - } else if (match(pstate, TOKEN_LEFT_PAREN)) { // it's an invoked call - uint8_t args = parseArguments(pstate); - writeu8(pstate, OP_INVOKE); - writeu8(pstate, args); - writeu8(pstate, pstate->compiler->expectedValues); - writeu16(pstate, name); - valuePopped(pstate, args+1); // args + function - valuePushed(pstate, pstate->compiler->expectedValues); } else { writeu8(pstate, OP_GETOBJECT); writeu16(pstate, name); @@ -754,6 +746,31 @@ static void _index(CParseState *pstate, bool canAssign, Precedence prec) { valuePopped(pstate, 1); // pops key & object but also pushes the value so total popped is 1 } +static void invoke(CParseState *pstate, bool canAssign, Precedence prec) { + int returnNum = pstate->compiler->expectedValues; + uint16_t name; + uint8_t args; + + consume(pstate, TOKEN_IDENTIFIER, "Expected method name after ':'."); + name = identifierConstant(pstate, &pstate->previous); + + consume(pstate, TOKEN_LEFT_PAREN, "Expected '(' after method identifier!"); + args = parseArguments(pstate); + + // if we're not the last token in this expression or we're expecting multiple values, we should return only 1 value!! + if (!isLast(pstate, prec) || (returnNum > 1 && check(pstate, TOKEN_COMMA))) + returnNum = 1; + + writeu8(pstate, OP_INVOKE); + writeu8(pstate, args); + writeu8(pstate, returnNum); + writeu16(pstate, name); + + valuePopped(pstate, args+1); // args + function + valuePushed(pstate, returnNum); + +} + // ++test.field[1] // this function is kind of spaghetti, feel free to rewrite (if you dare!) static void walkIndexes(CParseState *pstate, int lastIndexType, uint16_t lastIdent, int val) { @@ -879,7 +896,7 @@ ParseRule ruleTable[] = { [TOKEN_LEFT_BRACKET] = {table, _index, PREC_CALL}, [TOKEN_RIGHT_BRACKET] = {NULL, NULL, PREC_NONE}, [TOKEN_COMMA] = {NULL, NULL, PREC_NONE}, - [TOKEN_COLON] = {NULL, NULL, PREC_NONE}, + [TOKEN_COLON] = {NULL, invoke, PREC_CALL}, [TOKEN_DOT] = {NULL, dot, PREC_CALL}, [TOKEN_DOT_DOT] = {NULL, concat, PREC_CONCAT}, [TOKEN_DOT_DOT_DOT] = {NULL, NULL, PREC_NONE}, diff --git a/src/cvm.c b/src/cvm.c index 645641b..11819e1 100644 --- a/src/cvm.c +++ b/src/cvm.c @@ -439,21 +439,16 @@ COSMO_API bool cosmoV_get(CState *state, CObj *_obj, CValue key, CValue *val) { return false; } + // push the object onto the stack so the GC can find it cosmoV_pushValue(state, cosmoV_newObj(object)); - if (cosmoO_getRawObject(state, object, key, val)) { - // is it a function? if so, make it a method to the current object - if (IS_OBJ(*val) && (cosmoV_readObj(*val)->type == COBJ_CLOSURE || cosmoV_readObj(*val)->type == COBJ_CFUNCTION)) { - CObjMethod *method = cosmoO_newMethod(state, *val, _obj); - *val = cosmoV_newObj(method); - } // else, just pass the raw object - + // *val now equals the response, pop the object cosmoV_pop(state); return true; } cosmoV_pop(state); - return true; + return false; } COSMO_API bool cosmoV_set(CState *state, CObj *_obj, CValue key, CValue val) { @@ -469,6 +464,19 @@ COSMO_API bool cosmoV_set(CState *state, CObj *_obj, CValue key, CValue val) { return true; } +COSMO_API bool cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val) { + if (!cosmoV_get(state, obj, key, val)) + return false; + + // if the result is callable, wrap it in an method + if (IS_CALLABLE(*val)) { + CObjMethod *method = cosmoO_newMethod(state, *val, obj); + *val = cosmoV_newObj(method); + } + + return true; +} + int _tbl__next(CState *state, int nargs, CValue *args) { if (nargs != 1) { cosmoV_error(state, "Expected 1 parameter, %d received!", nargs); @@ -689,7 +697,7 @@ int cosmoV_execute(CState *state) { } } - cosmoV_setTop(state, 2); // pops the object & the key + cosmoV_setTop(state, 2); // pops the table & the key cosmoV_pushValue(state, val); // pushes the field result } else { cosmoV_error(state, "Couldn't index type %s!", cosmoV_typeStr(*temp)); @@ -700,7 +708,7 @@ int cosmoV_execute(CState *state) { case OP_NEWINDEX: { StkPtr value = cosmoV_getTop(state, 0); // value is at the top of the stack StkPtr key = cosmoV_getTop(state, 1); - StkPtr temp = cosmoV_getTop(state, 2); // object is after the key + StkPtr temp = cosmoV_getTop(state, 2); // table is after the key // sanity check if (IS_OBJ(*temp)) { @@ -748,19 +756,11 @@ int cosmoV_execute(CState *state) { // sanity check if (IS_OBJ(*temp)) { - switch (cosmoV_readObj(*temp)->type) { - case COBJ_TABLE: { // syntax sugar, makes "namespaces" possible - CObjTable *tbl = (CObjTable*)cosmoV_readObj(*temp); - - cosmoT_get(&tbl->tbl, constants[ident], &val); - break; - } - default: - if (!cosmoV_get(state, cosmoV_readObj(*temp), constants[ident], &val)) - return -1; - } + if (!cosmoV_get(state, cosmoV_readObj(*temp), constants[ident], &val)) + return -1; } else { - cosmoV_error(state, "Couldn't get from type %s!", cosmoV_typeStr(*temp)); + CObjString *field = cosmoV_toString(state, constants[ident]); + cosmoV_error(state, "Couldn't get field '%s' from type %s!", field->str, cosmoV_typeStr(*temp)); return -1; } @@ -775,20 +775,11 @@ int cosmoV_execute(CState *state) { // sanity check if (IS_OBJ(*temp)) { - switch (cosmoV_readObj(*temp)->type) { - case COBJ_TABLE: { // index and set the table - CObjTable *tbl = (CObjTable*)cosmoV_readObj(*temp); - CValue *newVal = cosmoT_insert(state, &tbl->tbl, constants[ident]); - - *newVal = *value; // set the index - break; - } - default: - if (!cosmoV_set(state, cosmoV_readObj(*temp), constants[ident], *value)) - return -1; - } + if (!cosmoV_set(state, cosmoV_readObj(*temp), constants[ident], *value)) + return -1; } else { - cosmoV_error(state, "Couldn't set a field on type %s!", cosmoV_typeStr(*temp)); + CObjString *field = cosmoV_toString(state, constants[ident]); + cosmoV_error(state, "Couldn't set field '%s' on type %s!", field->str, cosmoV_typeStr(*temp)); return -1; } @@ -796,7 +787,7 @@ int cosmoV_execute(CState *state) { cosmoV_setTop(state, 2); break; } - case OP_INVOKE: { // this is an optimization made by the parser, instead of allocating a CObjMethod every time we want to invoke a method, we shrink it down into one minimal instruction! + case OP_INVOKE: { uint8_t args = READBYTE(); uint8_t nres = READBYTE(); uint16_t ident = READUINT(); @@ -805,32 +796,12 @@ int cosmoV_execute(CState *state) { // sanity check if (IS_OBJ(*temp)) { - switch (cosmoV_readObj(*temp)->type) { - case COBJ_TABLE: { // again, syntax sugar ("""""namespaces""""") - CObjTable *tbl = (CObjTable*)cosmoV_readObj(*temp); - - cosmoT_get(&tbl->tbl, constants[ident], &val); - - // call closure/cfunction - if (!callCValue(state, val, args, nres, 0)) - return -1; - break; - } - default: { - CObjObject *object = cosmoO_grabProto(cosmoV_readObj(*temp)); - - if (object == NULL) { - cosmoV_error(state, "No proto defined! Couldn't get from type %s!", cosmoV_typeStr(*temp)); - return -1; - } - - cosmoO_getRawObject(state, object, constants[ident], &val); // we use cosmoO_getRawObject instead of the wrapper so we get the raw value from the object instead of the CObjMethod wrapper - - // now invoke the method! - invokeMethod(state, cosmoV_readObj(*temp), val, args, nres, 1); - break; - } - } + // get the field from the object + if (!cosmoV_get(state, cosmoV_readObj(*temp), constants[ident], &val)) + return -1; + + // now invoke the method! + invokeMethod(state, cosmoV_readObj(*temp), val, args, nres, 1); } else { cosmoV_error(state, "Couldn't get from type %s!", cosmoV_typeStr(*temp)); return -1; @@ -895,7 +866,7 @@ int cosmoV_execute(CState *state) { } // get __next method and place it at the top of the stack - cosmoV_get(state, cosmoV_readObj(*iObj), cosmoV_newObj(state->iStrings[ISTRING_NEXT]), iObj); + cosmoV_getMethod(state, cosmoV_readObj(*iObj), cosmoV_newObj(state->iStrings[ISTRING_NEXT]), iObj); } else { cosmoV_error(state, "Expected iterable object! '__iter' not defined!"); return -1; diff --git a/src/cvm.h b/src/cvm.h index c824fbc..25072e9 100644 --- a/src/cvm.h +++ b/src/cvm.h @@ -35,6 +35,8 @@ COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char * COSMO_API bool cosmoV_get(CState *state, CObj *obj, CValue key, CValue *val); COSMO_API bool cosmoV_set(CState *state, CObj *obj, CValue key, CValue val); +// wraps the closure into a CObjMethod, so the function is called as an invoked method +COSMO_API bool cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val); // nice to have wrappers