added __tostring IString, added expressionPrecedence() to cparse.c

additional minor refactoring
This commit is contained in:
CPunch 2021-01-03 17:33:10 -06:00
parent cb1d287c93
commit adb2381b4f
6 changed files with 84 additions and 43 deletions

View File

@ -81,7 +81,7 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args) {
cosmoV_pushLString(state, str->str + ((int)indx), ((int)length)); cosmoV_pushLString(state, str->str + ((int)indx), ((int)length));
} else { } 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; return 0;
} }

View File

@ -181,33 +181,34 @@ CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val) {
return upval; return upval;
} }
CObjString *cosmoO_copyString(CState *state, const char *str, size_t sz) { CObjString *cosmoO_copyString(CState *state, const char *str, size_t length) {
uint32_t hash = hashString(str, sz); 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? // have we already interned this string?
if (lookup != NULL) if (lookup != NULL)
return lookup; return lookup;
char *buf = cosmoM_xmalloc(state, sizeof(char) * (sz + 1)); // +1 for null terminator char *buf = cosmoM_xmalloc(state, sizeof(char) * (length + 1)); // +1 for null terminator
memcpy(buf, str, sz); // copy string to heap memcpy(buf, str, length); // copy string to heap
buf[sz] = '\0'; // don't forget our null terminator 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) { // length shouldn't include the null terminator! (char array should also have been allocated using cosmoM_xmalloc!)
uint32_t hash = hashString(str, sz); 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? // have we already interned this string?
if (lookup != NULL) { 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 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) { 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)) { 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, *val); // push function
cosmoV_pushValue(state, cosmoV_newObj(object)); // push object 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 *val = *cosmoV_pop(state); // set value to the return value of __index
return true; 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, *val); // push function
cosmoV_pushValue(state, cosmoV_newObj(object)); // push object cosmoV_pushValue(state, cosmoV_newObj(object)); // push object
cosmoV_pushValue(state, key); // push key 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 *val = *cosmoV_pop(state); // set value to the return value of __index
return true; return true;
} else { // there's no __index function defined! } 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, cosmoV_newObj(object)); // push object
cosmoV_pushValue(state, key); // push key & value pair cosmoV_pushValue(state, key); // push key & value pair
cosmoV_pushValue(state, val); cosmoV_pushValue(state, val);
cosmoV_call(state, 3, 0); return cosmoV_call(state, 3, 0) == COSMOVM_OK;
return true;
} else { // there's no __newindex function defined } else { // there's no __newindex function defined
cosmoV_error(state, "Couldn't set index on object without __newindex function!"); 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; CObjFunction *func = (CObjFunction*)obj;
return func->name != NULL ? func->name : cosmoO_copyString(state, UNNAMEDCHUNK, strlen(UNNAMEDCHUNK)); return func->name != NULL ? func->name : cosmoO_copyString(state, UNNAMEDCHUNK, strlen(UNNAMEDCHUNK));
} }
case COBJ_OBJECT: { // TODO: maybe not safe?? case COBJ_OBJECT: {
char buf[64]; CObjObject *object = (CObjObject*)obj;
int sz = sprintf(buf, "<obj> %p", (void*)obj) + 1; // +1 for the null character CValue res;
return cosmoO_copyString(state, buf, sz);
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, "<err>", 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 <string>, got %s!", cosmoV_typeStr(*ret));
return cosmoO_copyString(state, "<err>", 5);
}
// return string
cosmoV_pop(state);
return (CObjString*)cosmoV_readObj(*ret);
} else {
char buf[64];
int sz = sprintf(buf, "<obj> %p", (void*)obj) + 1; // +1 for the null character
return cosmoO_copyString(state, buf, sz);
}
} }
case COBJ_DICT: { case COBJ_DICT: {
char buf[64]; char buf[64];

View File

@ -85,6 +85,7 @@ typedef struct {
static void parsePrecedence(CParseState*, Precedence); static void parsePrecedence(CParseState*, Precedence);
static void variable(CParseState *pstate, bool canAssign); 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 int expression(CParseState *pstate, int needed, bool forceNeeded);
static void statement(CParseState *pstate); static void statement(CParseState *pstate);
static void declaration(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 int cachedLine = pstate->previous.line; // eval'ing the next expression might change the line number
// only eval the next *value* // only eval the next *value*
parsePrecedence(pstate, PREC_UNARY); expressionPrecedence(pstate, 1, PREC_UNARY, true);
switch(type) { switch(type) {
case TOKEN_MINUS: writeu8Chunk(pstate->state, getChunk(pstate), OP_NEGATE, cachedLine); break; 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 CTokenType type = pstate->previous.type; // already consumed
int cachedLine = pstate->previous.line; // eval'ing the next expression might change the line number 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) { switch (type) {
// ARITH // ARITH
@ -519,7 +520,7 @@ static void and_(CParseState *pstate, bool canAssign) {
int jump = writeJmp(pstate, OP_EJMP); // conditional jump without popping int jump = writeJmp(pstate, OP_EJMP); // conditional jump without popping
writePop(pstate, 1); writePop(pstate, 1);
parsePrecedence(pstate, PREC_AND); expressionPrecedence(pstate, 1, PREC_AND, true);
patchJmp(pstate, jump); patchJmp(pstate, jump);
} }
@ -531,7 +532,7 @@ static void or_(CParseState *pstate, bool canAssign) {
patchJmp(pstate, elseJump); patchJmp(pstate, elseJump);
writePop(pstate, 1); writePop(pstate, 1);
parsePrecedence(pstate, PREC_OR); expressionPrecedence(pstate, 1, PREC_OR, true);
patchJmp(pstate, endJump); patchJmp(pstate, endJump);
} }
@ -549,7 +550,7 @@ static void concat(CParseState *pstate, bool canAssign) {
int vars = 1; // we already have something on the stack int vars = 1; // we already have something on the stack
do { do {
parsePrecedence(pstate, getRule(type)->level + 1); // parse until next concat expressionPrecedence(pstate, 1, getRule(type)->level + 1, true); // parse until next concat
vars++; vars++;
} while (match(pstate, TOKEN_DOT_DOT)); } 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 lastExpected = pstate->compiler->expectedValues;
int saved = pstate->compiler->pushedValues + needed; int saved = pstate->compiler->pushedValues + needed;
pstate->compiler->expectedValues = needed; pstate->compiler->expectedValues = needed;
parsePrecedence(pstate, PREC_ASSIGNMENT); parsePrecedence(pstate, prec);
if (pstate->compiler->pushedValues > saved) { if (pstate->compiler->pushedValues > saved) {
writePop(pstate, 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); 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) { static void expressionStatement(CParseState *pstate) {
int savedPushed = pstate->compiler->pushedValues; int savedPushed = pstate->compiler->pushedValues;

View File

@ -42,6 +42,7 @@ CState *cosmoV_newState() {
// setup all strings used by the VM // setup all strings used by the VM
state->iStrings[ISTRING_INIT] = cosmoO_copyString(state, "__init", 6); 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_INDEX] = cosmoO_copyString(state, "__index", 7);
state->iStrings[ISTRING_NEWINDEX] = cosmoO_copyString(state, "__newindex", 10); state->iStrings[ISTRING_NEWINDEX] = cosmoO_copyString(state, "__newindex", 10);

View File

@ -14,6 +14,7 @@ typedef struct CCallFrame {
typedef enum IStringEnum { typedef enum IStringEnum {
ISTRING_INIT, // __init ISTRING_INIT, // __init
ISTRING_TOSTRING, // __tostring
ISTRING_INDEX, // __index ISTRING_INDEX, // __index
ISTRING_NEWINDEX, // __newindex ISTRING_NEWINDEX, // __newindex
ISTRING_GETTER, // __getter ISTRING_GETTER, // __getter

View File

@ -139,13 +139,17 @@ void cosmoV_concat(CState *state, int vals) {
int cosmoV_execute(CState *state); 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); 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 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); int nres = cfunc(state, args, savedBase + 1);
cosmoM_unfreezeGC(state); 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 if (nres > nresults) // caller function wasn't expecting this many return values, cap it
nres = nresults; 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 // now, if the caller function expected more return values, push nils onto the stack
for (int i = nres; i < nresults; i++) for (int i = nres; i < nresults; i++)
cosmoV_pushValue(state, cosmoV_newNil()); 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; CObjFunction *func = closure->function;
// if the function is variadic and theres more args than parameters, push the args into a dictionary // 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); *temp = cosmoV_newObj(obj);
if (IS_CFUNCTION(func)) { 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)) { } 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 { } else {
cosmoV_error(state, "Cannot invoke non-function type %s!", cosmoV_typeStr(func)); cosmoV_error(state, "Cannot invoke non-function type %s!", cosmoV_typeStr(func));
} }
return true; return false;
} }
// args = # of pass parameters // args = # of pass parameters
@ -243,9 +249,9 @@ COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults) {
switch (cosmoV_readObj(*val)->type) { switch (cosmoV_readObj(*val)->type) {
case COBJ_CLOSURE: { case COBJ_CLOSURE: {
CObjClosure *closure = (CObjClosure*)cosmoV_readObj(*val); CObjClosure *closure = (CObjClosure*)cosmoV_readObj(*val);
if (!call(state, closure, args, nresults, 0)) { if (!call(state, closure, args, nresults, 0))
return COSMOVM_RUNTIME_ERR; return COSMOVM_RUNTIME_ERR;
}
break; break;
} }
case COBJ_METHOD: { case COBJ_METHOD: {
@ -275,7 +281,9 @@ COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults) {
case COBJ_CFUNCTION: { case COBJ_CFUNCTION: {
// it's a C function, so call it // it's a C function, so call it
CosmoCFunction cfunc = ((CObjCFunction*)cosmoV_readObj(*val))->cfunc; 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; break;
} }
default: default:
@ -484,9 +492,8 @@ int cosmoV_execute(CState *state) {
case OP_CALL: { case OP_CALL: {
uint8_t args = READBYTE(); uint8_t args = READBYTE();
uint8_t nres = READBYTE(); uint8_t nres = READBYTE();
COSMOVMRESULT result = cosmoV_call(state, args, nres); if (cosmoV_call(state, args, nres) != COSMOVM_OK) {
if (result != COSMOVM_OK) { return -1;
return result;
} }
break; break;
} }
@ -682,9 +689,11 @@ int cosmoV_execute(CState *state) {
// call closure/cfunction // call closure/cfunction
if (IS_CFUNCTION(val)) { 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)) { } 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 { } else {
cosmoV_error(state, "Cannot call non-function value %s!", cosmoV_typeStr(val)); cosmoV_error(state, "Cannot call non-function value %s!", cosmoV_typeStr(val));
return -1; return -1;
@ -751,7 +760,8 @@ int cosmoV_execute(CState *state) {
cosmoV_pop(state); // pop the object from the stack cosmoV_pop(state); // pop the object from the stack
cosmoV_pushValue(state, val); cosmoV_pushValue(state, val);
cosmoV_pushValue(state, cosmoV_newObj(obj)); 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); StkPtr iObj = cosmoV_getTop(state, 0);
@ -787,7 +797,8 @@ int cosmoV_execute(CState *state) {
} }
cosmoV_pushValue(state, *temp); 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 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 cosmoV_setTop(state, nresults); // pop the return values