From adb2381b4fd20cd87839ec3afec11d9e7bf74eeb Mon Sep 17 00:00:00 2001 From: CPunch Date: Sun, 3 Jan 2021 17:33:10 -0600 Subject: [PATCH] added __tostring IString, added expressionPrecedence() to cparse.c additional minor refactoring --- src/cbaselib.c | 2 +- src/cobj.c | 63 ++++++++++++++++++++++++++++++++++---------------- src/cparse.c | 19 +++++++++------ src/cstate.c | 1 + src/cstate.h | 1 + src/cvm.c | 41 ++++++++++++++++++++------------ 6 files changed, 84 insertions(+), 43 deletions(-) diff --git a/src/cbaselib.c b/src/cbaselib.c index ea883bd..baf6e66 100644 --- a/src/cbaselib.c +++ b/src/cbaselib.c @@ -81,7 +81,7 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args) { cosmoV_pushLString(state, str->str + ((int)indx), ((int)length)); } else { - cosmoV_error(state, "Expected 2 or 3 parameters, got %d!", nargs); + cosmoV_error(state, "Expected 2 or 3 arguments, got %d!", nargs); return 0; } diff --git a/src/cobj.c b/src/cobj.c index 8053be7..0001cec 100644 --- a/src/cobj.c +++ b/src/cobj.c @@ -181,33 +181,34 @@ CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val) { return upval; } -CObjString *cosmoO_copyString(CState *state, const char *str, size_t sz) { - uint32_t hash = hashString(str, sz); - CObjString *lookup = cosmoT_lookupString(&state->strings, str, sz, hash); +CObjString *cosmoO_copyString(CState *state, const char *str, size_t length) { + uint32_t hash = hashString(str, length); + CObjString *lookup = cosmoT_lookupString(&state->strings, str, length, hash); // have we already interned this string? if (lookup != NULL) return lookup; - char *buf = cosmoM_xmalloc(state, sizeof(char) * (sz + 1)); // +1 for null terminator - memcpy(buf, str, sz); // copy string to heap - buf[sz] = '\0'; // don't forget our null terminator + char *buf = cosmoM_xmalloc(state, sizeof(char) * (length + 1)); // +1 for null terminator + memcpy(buf, str, length); // copy string to heap + buf[length] = '\0'; // don't forget our null terminator - return cosmoO_allocateString(state, buf, sz, hash); + return cosmoO_allocateString(state, buf, length, hash); } -CObjString *cosmoO_takeString(CState *state, char *str, size_t sz) { - uint32_t hash = hashString(str, sz); +// length shouldn't include the null terminator! (char array should also have been allocated using cosmoM_xmalloc!) +CObjString *cosmoO_takeString(CState *state, char *str, size_t length) { + uint32_t hash = hashString(str, length); - CObjString *lookup = cosmoT_lookupString(&state->strings, str, sz, hash); + CObjString *lookup = cosmoT_lookupString(&state->strings, str, length, hash); // have we already interned this string? if (lookup != NULL) { - cosmoM_freearray(state, char, str, sz); // free our passed character array, it's unneeded! + cosmoM_freearray(state, char, str, length + 1); // free our passed character array, it's unneeded! return lookup; } - return cosmoO_allocateString(state, str, sz, hash); + return cosmoO_allocateString(state, str, length, hash); } CObjString *cosmoO_allocateString(CState *state, const char *str, size_t sz, uint32_t hash) { @@ -270,7 +271,8 @@ bool cosmoO_getRawObject(CState *state, CObjObject *object, CValue key, CValue * if (cosmoO_getIString(state, object, ISTRING_GETTER, val) && IS_OBJECT(*val) && cosmoO_getRawObject(state, cosmoV_readObject(*val), key, val)) { cosmoV_pushValue(state, *val); // push function cosmoV_pushValue(state, cosmoV_newObj(object)); // push object - cosmoV_call(state, 1, 1); // call the function with the 1 argument + if (cosmoV_call(state, 1, 1) != COSMOVM_OK) // call the function with the 1 argument + return false; *val = *cosmoV_pop(state); // set value to the return value of __index return true; } @@ -352,7 +354,8 @@ bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *v cosmoV_pushValue(state, *val); // push function cosmoV_pushValue(state, cosmoV_newObj(object)); // push object cosmoV_pushValue(state, key); // push key - cosmoV_call(state, 2, 1); // call the function with the 2 arguments + if (cosmoV_call(state, 2, 1) != COSMOVM_OK) // call the function with the 2 arguments + return false; *val = *cosmoV_pop(state); // set value to the return value of __index return true; } else { // there's no __index function defined! @@ -370,8 +373,7 @@ bool cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue cosmoV_pushValue(state, cosmoV_newObj(object)); // push object cosmoV_pushValue(state, key); // push key & value pair cosmoV_pushValue(state, val); - cosmoV_call(state, 3, 0); - return true; + return cosmoV_call(state, 3, 0) == COSMOVM_OK; } else { // there's no __newindex function defined cosmoV_error(state, "Couldn't set index on object without __newindex function!"); } @@ -392,10 +394,31 @@ CObjString *cosmoO_toString(CState *state, CObj *obj) { CObjFunction *func = (CObjFunction*)obj; return func->name != NULL ? func->name : cosmoO_copyString(state, UNNAMEDCHUNK, strlen(UNNAMEDCHUNK)); } - case COBJ_OBJECT: { // TODO: maybe not safe?? - char buf[64]; - int sz = sprintf(buf, " %p", (void*)obj) + 1; // +1 for the null character - return cosmoO_copyString(state, buf, sz); + case COBJ_OBJECT: { + CObjObject *object = (CObjObject*)obj; + CValue res; + + if (cosmoO_getIString(state, object, ISTRING_TOSTRING, &res)) { + cosmoV_pushValue(state, res); + cosmoV_pushValue(state, cosmoV_newObj(object)); + if (cosmoV_call(state, 1, 1) != COSMOVM_OK) + return cosmoO_copyString(state, "", 5); + + // make sure the __tostring function returned a string + StkPtr ret = cosmoV_getTop(state, 0); + if (!IS_STRING(*ret)) { + cosmoV_error(state, "__tostring expected to return , got %s!", cosmoV_typeStr(*ret)); + return cosmoO_copyString(state, "", 5); + } + + // return string + cosmoV_pop(state); + return (CObjString*)cosmoV_readObj(*ret); + } else { + char buf[64]; + int sz = sprintf(buf, " %p", (void*)obj) + 1; // +1 for the null character + return cosmoO_copyString(state, buf, sz); + } } case COBJ_DICT: { char buf[64]; diff --git a/src/cparse.c b/src/cparse.c index 4217798..52d5909 100644 --- a/src/cparse.c +++ b/src/cparse.c @@ -85,6 +85,7 @@ typedef struct { static void parsePrecedence(CParseState*, Precedence); static void variable(CParseState *pstate, bool canAssign); +static int expressionPrecedence(CParseState *pstate, int needed, Precedence prec, bool forceNeeded); static int expression(CParseState *pstate, int needed, bool forceNeeded); static void statement(CParseState *pstate); static void declaration(CParseState *pstate); @@ -412,7 +413,7 @@ static void unary(CParseState *pstate, bool canAssign) { int cachedLine = pstate->previous.line; // eval'ing the next expression might change the line number // only eval the next *value* - parsePrecedence(pstate, PREC_UNARY); + expressionPrecedence(pstate, 1, PREC_UNARY, true); switch(type) { case TOKEN_MINUS: writeu8Chunk(pstate->state, getChunk(pstate), OP_NEGATE, cachedLine); break; @@ -428,7 +429,7 @@ static void binary(CParseState *pstate, bool canAssign) { CTokenType type = pstate->previous.type; // already consumed int cachedLine = pstate->previous.line; // eval'ing the next expression might change the line number - parsePrecedence(pstate, getRule(type)->level + 1); + expressionPrecedence(pstate, 1, getRule(type)->level + 1, true); switch (type) { // ARITH @@ -519,7 +520,7 @@ static void and_(CParseState *pstate, bool canAssign) { int jump = writeJmp(pstate, OP_EJMP); // conditional jump without popping writePop(pstate, 1); - parsePrecedence(pstate, PREC_AND); + expressionPrecedence(pstate, 1, PREC_AND, true); patchJmp(pstate, jump); } @@ -531,7 +532,7 @@ static void or_(CParseState *pstate, bool canAssign) { patchJmp(pstate, elseJump); writePop(pstate, 1); - parsePrecedence(pstate, PREC_OR); + expressionPrecedence(pstate, 1, PREC_OR, true); patchJmp(pstate, endJump); } @@ -549,7 +550,7 @@ static void concat(CParseState *pstate, bool canAssign) { int vars = 1; // we already have something on the stack do { - parsePrecedence(pstate, getRule(type)->level + 1); // parse until next concat + expressionPrecedence(pstate, 1, getRule(type)->level + 1, true); // parse until next concat vars++; } while (match(pstate, TOKEN_DOT_DOT)); @@ -1377,12 +1378,12 @@ static void synchronize(CParseState *pstate) { } } -static int expression(CParseState *pstate, int needed, bool forceNeeded) { +static int expressionPrecedence(CParseState *pstate, int needed, Precedence prec, bool forceNeeded) { int lastExpected = pstate->compiler->expectedValues; int saved = pstate->compiler->pushedValues + needed; pstate->compiler->expectedValues = needed; - parsePrecedence(pstate, PREC_ASSIGNMENT); + parsePrecedence(pstate, prec); if (pstate->compiler->pushedValues > saved) { writePop(pstate, pstate->compiler->pushedValues - saved); @@ -1396,6 +1397,10 @@ static int expression(CParseState *pstate, int needed, bool forceNeeded) { return pstate->compiler->pushedValues - (saved - needed); } +static int expression(CParseState *pstate, int needed, bool forceNeeded) { + return expressionPrecedence(pstate, needed, PREC_ASSIGNMENT, forceNeeded); +} + static void expressionStatement(CParseState *pstate) { int savedPushed = pstate->compiler->pushedValues; diff --git a/src/cstate.c b/src/cstate.c index 8af581f..a3de789 100644 --- a/src/cstate.c +++ b/src/cstate.c @@ -42,6 +42,7 @@ CState *cosmoV_newState() { // setup all strings used by the VM state->iStrings[ISTRING_INIT] = cosmoO_copyString(state, "__init", 6); + state->iStrings[ISTRING_TOSTRING] = cosmoO_copyString(state, "__tostring", 10); state->iStrings[ISTRING_INDEX] = cosmoO_copyString(state, "__index", 7); state->iStrings[ISTRING_NEWINDEX] = cosmoO_copyString(state, "__newindex", 10); diff --git a/src/cstate.h b/src/cstate.h index e271a3c..9b540fc 100644 --- a/src/cstate.h +++ b/src/cstate.h @@ -14,6 +14,7 @@ typedef struct CCallFrame { typedef enum IStringEnum { ISTRING_INIT, // __init + ISTRING_TOSTRING, // __tostring ISTRING_INDEX, // __index ISTRING_NEWINDEX, // __newindex ISTRING_GETTER, // __getter diff --git a/src/cvm.c b/src/cvm.c index a70279c..d58851c 100644 --- a/src/cvm.c +++ b/src/cvm.c @@ -139,13 +139,17 @@ void cosmoV_concat(CState *state, int vals) { int cosmoV_execute(CState *state); -static inline void callCFunction(CState *state, CosmoCFunction cfunc, int args, int nresults, int offset) { +static inline bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nresults, int offset) { StkPtr savedBase = cosmoV_getTop(state, 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 int nres = cfunc(state, args, savedBase + 1); cosmoM_unfreezeGC(state); + // if the state paniced during the c function, return false and leave the stack preserved (so the error string is left) + if (state->panic) + return false; + if (nres > nresults) // caller function wasn't expecting this many return values, cap it nres = nresults; @@ -161,9 +165,11 @@ static inline void callCFunction(CState *state, CosmoCFunction cfunc, int args, // now, if the caller function expected more return values, push nils onto the stack for (int i = nres; i < nresults; i++) cosmoV_pushValue(state, cosmoV_newNil()); + + return true; } -bool call(CState *state, CObjClosure *closure, int args, int nresults, int offset) { +static inline bool call(CState *state, CObjClosure *closure, int args, int nresults, int offset) { CObjFunction *func = closure->function; // if the function is variadic and theres more args than parameters, push the args into a dictionary @@ -221,14 +227,14 @@ bool invokeMethod(CState* state, CObjObject *obj, CValue func, int args, int nre *temp = cosmoV_newObj(obj); if (IS_CFUNCTION(func)) { - callCFunction(state, cosmoV_readCFunction(func), args+1, nresults, offset); + return callCFunction(state, cosmoV_readCFunction(func), args+1, nresults, offset); } else if (IS_CLOSURE(func)) { - call(state, cosmoV_readClosure(func), args+1, nresults, offset); // offset = 1 so our stack is properly reset + return call(state, cosmoV_readClosure(func), args+1, nresults, offset); // offset = 1 so our stack is properly reset } else { cosmoV_error(state, "Cannot invoke non-function type %s!", cosmoV_typeStr(func)); } - return true; + return false; } // args = # of pass parameters @@ -243,9 +249,9 @@ COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults) { switch (cosmoV_readObj(*val)->type) { case COBJ_CLOSURE: { CObjClosure *closure = (CObjClosure*)cosmoV_readObj(*val); - if (!call(state, closure, args, nresults, 0)) { + if (!call(state, closure, args, nresults, 0)) return COSMOVM_RUNTIME_ERR; - } + break; } case COBJ_METHOD: { @@ -275,7 +281,9 @@ COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults) { case COBJ_CFUNCTION: { // it's a C function, so call it CosmoCFunction cfunc = ((CObjCFunction*)cosmoV_readObj(*val))->cfunc; - callCFunction(state, cfunc, args, nresults, 0); + if (!callCFunction(state, cfunc, args, nresults, 0)) + return COSMOVM_RUNTIME_ERR; + break; } default: @@ -484,9 +492,8 @@ int cosmoV_execute(CState *state) { case OP_CALL: { uint8_t args = READBYTE(); uint8_t nres = READBYTE(); - COSMOVMRESULT result = cosmoV_call(state, args, nres); - if (result != COSMOVM_OK) { - return result; + if (cosmoV_call(state, args, nres) != COSMOVM_OK) { + return -1; } break; } @@ -682,9 +689,11 @@ int cosmoV_execute(CState *state) { // call closure/cfunction if (IS_CFUNCTION(val)) { - callCFunction(state, cosmoV_readCFunction(val), args, nres, -1); + if (!callCFunction(state, cosmoV_readCFunction(val), args, nres, -1)) + return -1; } else if (IS_CLOSURE(val)) { - call(state, cosmoV_readClosure(val), args, nres, -1); + if (!call(state, cosmoV_readClosure(val), args, nres, -1)) + return -1; } else { cosmoV_error(state, "Cannot call non-function value %s!", cosmoV_typeStr(val)); return -1; @@ -751,7 +760,8 @@ int cosmoV_execute(CState *state) { cosmoV_pop(state); // pop the object from the stack cosmoV_pushValue(state, val); cosmoV_pushValue(state, cosmoV_newObj(obj)); - cosmoV_call(state, 1, 1); // we expect 1 return value on the stack, the iterable object + if (cosmoV_call(state, 1, 1) != COSMOVM_OK) // we expect 1 return value on the stack, the iterable object + return -1; StkPtr iObj = cosmoV_getTop(state, 0); @@ -787,7 +797,8 @@ int cosmoV_execute(CState *state) { } cosmoV_pushValue(state, *temp); - cosmoV_call(state, 0, nresults); + if (cosmoV_call(state, 0, nresults) != COSMOVM_OK) + return -1; if (IS_NIL(*(cosmoV_getTop(state, 0)))) { // __next returned a nil, which means to exit the loop cosmoV_setTop(state, nresults); // pop the return values