mirror of
https://github.com/CPunch/Cosmo.git
synced 2024-12-22 22:40:03 +00:00
added __tostring IString, added expressionPrecedence() to cparse.c
additional minor refactoring
This commit is contained in:
parent
cb1d287c93
commit
adb2381b4f
@ -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;
|
||||
}
|
||||
|
||||
|
63
src/cobj.c
63
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, "<obj> %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, "<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: {
|
||||
char buf[64];
|
||||
|
19
src/cparse.c
19
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;
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
41
src/cvm.c
41
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
|
||||
|
Loading…
Reference in New Issue
Block a user