Refactored cosmoO_equals

This sets up room for the '__equal' metamethod to be added

- cosmoO_equals now requires the state to be passed
- cosmoV_equals now requires the state to be passed
- cosmoT_get now requires the state to be passed
This commit is contained in:
cpunch 2021-02-19 17:04:23 -06:00
parent 40739e9bea
commit 3890c9dd1e
11 changed files with 58 additions and 37 deletions

View File

@ -32,21 +32,14 @@ int cosmoB_assert(CState *state, int nargs, CValue *args) {
if (!IS_BOOLEAN(args[0]) || (nargs == 2 && !IS_STRING(args[1]))) {
if (nargs == 2) {
cosmoV_typeError(state, "assert()", "<boolean>, <string>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
}
else {
} else {
cosmoV_typeError(state, "assert()", "<boolean>", "%s", cosmoV_typeStr(args[0]));
}
return 0;
}
if (!cosmoV_readBoolean(args[0])) { // expression passed was false, error!
if (nargs == 2) {
cosmoV_error(state, "%s", cosmoV_readCString(args[1]));
}
else { // optional custom error message
cosmoV_error(state, "%s", "assert() failed!");
}
} // else do nothing :)
if (!cosmoV_readBoolean(args[0])) // expression passed was false, error!
cosmoV_error(state, "%s", nargs == 2 ? cosmoV_readCString(args[1]) : "assert() failed!");
return 0;
}

View File

@ -39,7 +39,7 @@ void freeChunk(CState* state, CChunk *chunk) {
int addConstant(CState* state, CChunk *chunk, CValue value) {
// before adding the constant, check if we already have it
for (size_t i = 0; i < chunk->constants.count; i++) {
if (cosmoV_equal(value, chunk->constants.values[i]))
if (cosmoV_equal(state, value, chunk->constants.values[i]))
return i; // we already have a matching constant!
}

View File

@ -86,6 +86,7 @@ void markArray(CState *state, CValueArray *array) {
}
// mark all references associated with the object
// black = keep, white = discard
void blackenObject(CState *state, CObj *obj) {
markObject(state, (CObj*)obj->proto);
switch (obj->type) {

View File

@ -90,25 +90,48 @@ void cosmoO_free(CState *state, CObj* obj) {
cosmoM_free(state, CObjClosure, closure);
break;
}
case COBJ_MAX: { /* stubbed, should never happen */ }
case COBJ_MAX:
default: { /* stubbed, should never happen */ }
}
}
bool cosmoO_equal(CObj* obj1, CObj* obj2) {
bool cosmoO_equal(CState *state, CObj* obj1, CObj* obj2) {
if (obj1 == obj2) // its the same object, this compares strings for us since they're interned anyways :)
return true;
if (obj1->type != obj2->type)
return false;
goto _eqFail;
switch (obj1->type) {
case COBJ_STRING:
return obj1 == obj2; // compare pointers because we already intern all strings :)
case COBJ_CFUNCTION: {
CObjCFunction *cfunc1 = (CObjCFunction*)obj1;
CObjCFunction *cfunc2 = (CObjCFunction*)obj2;
return cfunc1->cfunc == cfunc2->cfunc;
if (cfunc1->cfunc == cfunc2->cfunc)
return true;
goto _eqFail;
}
case COBJ_METHOD: {
CObjMethod *method1 = (CObjMethod*)obj1;
CObjMethod *method2 = (CObjMethod*)obj2;
if (cosmoV_equal(state, method1->func, method2->func))
return true;
goto _eqFail;
}
case COBJ_CLOSURE: {
CObjClosure *closure1 = (CObjClosure*)obj1;
CObjClosure *closure2 = (CObjClosure*)obj2;
// we just compare the function pointer
if (closure1->function == closure2->function)
return true;
goto _eqFail;
}
default:
return false;
goto _eqFail;
}
_eqFail:
// TODO: add support for an '__equal' metamethod
return false;
}
CObjObject *cosmoO_newObject(CState *state) {
@ -312,8 +335,8 @@ bool cosmoO_isDescendant(CObj *obj, CObjObject *proto) {
// returns false if error thrown
bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *val, CObj *obj) {
if (!cosmoT_get(&proto->tbl, key, val)) { // if the field doesn't exist in the object, check the proto
if (cosmoO_getIString(state, proto, ISTRING_GETTER, val) && IS_TABLE(*val) && cosmoT_get(&cosmoV_readTable(*val)->tbl, key, val)) {
if (!cosmoT_get(state, &proto->tbl, key, val)) { // if the field doesn't exist in the object, check the proto
if (cosmoO_getIString(state, proto, ISTRING_GETTER, val) && IS_TABLE(*val) && cosmoT_get(state, &cosmoV_readTable(*val)->tbl, key, val)) {
cosmoV_pushValue(state, *val); // push function
cosmoV_pushRef(state, (CObj*)obj); // push object
if (cosmoV_call(state, 1, 1) != COSMOVM_OK) // call the function with the 1 argument
@ -342,7 +365,7 @@ void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue va
}
// check for __setters
if (cosmoO_getIString(state, proto, ISTRING_SETTER, &ret) && IS_TABLE(ret) && cosmoT_get(&cosmoV_readTable(ret)->tbl, key, &ret)) {
if (cosmoO_getIString(state, proto, ISTRING_SETTER, &ret) && IS_TABLE(ret) && cosmoT_get(state, &cosmoV_readTable(ret)->tbl, key, &ret)) {
cosmoV_pushValue(state, ret); // push function
cosmoV_pushRef(state, (CObj*)obj); // push object
cosmoV_pushValue(state, val); // push new value
@ -398,7 +421,7 @@ bool rawgetIString(CState *state, CObjObject *object, int flag, CValue *val) {
if (readFlag(object->istringFlags, flag))
return false; // it's been cached as bad
if (!cosmoT_get(&object->tbl, cosmoV_newRef(state->iStrings[flag]), val)) {
if (!cosmoT_get(state, &object->tbl, cosmoV_newRef(state->iStrings[flag]), val)) {
// mark it bad!
setFlagOn(object->istringFlags, flag);
return false;

View File

@ -136,7 +136,7 @@ static inline bool IS_CALLABLE(CValue val) {
}
void cosmoO_free(CState *state, CObj* obj);
bool cosmoO_equal(CObj* obj1, CObj* obj2);
bool cosmoO_equal(CState *state, CObj* obj1, CObj* obj2);
// walks the protos of obj and checks for proto
bool cosmoO_isDescendant(CObj *obj, CObjObject *proto);

View File

@ -86,7 +86,7 @@ uint32_t getValueHash(CValue *val) {
}
// mask should always be (capacity - 1)
static CTableEntry *findEntry(CTableEntry *entries, int mask, CValue key) {
static CTableEntry *findEntry(CState *state, CTableEntry *entries, int mask, CValue key) {
uint32_t hash = getValueHash(&key);
uint32_t indx = hash & mask; // since we know the capacity will *always* be a power of 2, we can use bitwise & to perform a MUCH faster mod operation
CTableEntry *tomb = NULL;
@ -104,7 +104,7 @@ static CTableEntry *findEntry(CTableEntry *entries, int mask, CValue key) {
// its a tombstone!
tomb = entry;
}
} else if (cosmoV_equal(entry->key, key)) {
} else if (cosmoV_equal(state, entry->key, key)) {
return entry;
}
@ -142,7 +142,7 @@ static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrin
continue; // skip empty keys
// get new entry location & update the node
CTableEntry *newEntry = findEntry(entries, newCapacity - 1, oldEntry->key);
CTableEntry *newEntry = findEntry(state, entries, newCapacity - 1, oldEntry->key);
newEntry->key = oldEntry->key;
newEntry->val = oldEntry->val;
newCount++; // inc count
@ -178,7 +178,7 @@ COSMO_API CValue* cosmoT_insert(CState *state, CTable *tbl, CValue key) {
}
// insert into the table
CTableEntry *entry = findEntry(tbl->table, tbl->capacityMask, key); // -1 for our capacity mask
CTableEntry *entry = findEntry(state, tbl->table, tbl->capacityMask, key); // -1 for our capacity mask
if (IS_NIL(entry->key)) {
if (IS_NIL(entry->val)) // is it empty?
@ -191,14 +191,14 @@ COSMO_API CValue* cosmoT_insert(CState *state, CTable *tbl, CValue key) {
return &entry->val;
}
bool cosmoT_get(CTable *tbl, CValue key, CValue *val) {
bool cosmoT_get(CState *state, CTable *tbl, CValue key, CValue *val) {
// sanity check
if (tbl->count == 0) {
*val = cosmoV_newNil();
return false;
}
CTableEntry *entry = findEntry(tbl->table, tbl->capacityMask, key);
CTableEntry *entry = findEntry(state, tbl->table, tbl->capacityMask, key);
*val = entry->val;
// return if get was successful
@ -208,7 +208,7 @@ bool cosmoT_get(CTable *tbl, CValue key, CValue *val) {
bool cosmoT_remove(CState* state, CTable *tbl, CValue key) {
if (tbl->count == 0) return 0; // sanity check
CTableEntry *entry = findEntry(tbl->table, tbl->capacityMask, key);
CTableEntry *entry = findEntry(state, tbl->table, tbl->capacityMask, key);
if (IS_NIL(entry->key)) // sanity check
return false;

View File

@ -26,7 +26,7 @@ bool cosmoT_checkShrink(CState *state, CTable *tbl);
CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32_t hash);
CValue *cosmoT_insert(CState *state, CTable *tbl, CValue key);
bool cosmoT_get(CTable *tbl, CValue key, CValue *val);
bool cosmoT_get(CState *state, CTable *tbl, CValue key, CValue *val);
bool cosmoT_remove(CState *state, CTable *tbl, CValue key);
void cosmoT_printTable(CTable *tbl, const char *name);

View File

@ -19,7 +19,7 @@ void appendValArray(CState *state, CValueArray *array, CValue val) {
array->values[array->count++] = val;
}
bool cosmoV_equal(CValue valA, CValue valB) {
bool cosmoV_equal(CState *state, CValue valA, CValue valB) {
if (GET_TYPE(valA) != GET_TYPE(valB)) // are they the same type?
return false;
@ -27,7 +27,7 @@ bool cosmoV_equal(CValue valA, CValue valB) {
switch (GET_TYPE(valA)) {
case COSMO_TBOOLEAN: return cosmoV_readBoolean(valA) == cosmoV_readBoolean(valB);
case COSMO_TNUMBER: return cosmoV_readNumber(valA) == cosmoV_readNumber(valB);
case COSMO_TREF: return cosmoO_equal(cosmoV_readRef(valA), cosmoV_readRef(valB));
case COSMO_TREF: return cosmoO_equal(state, cosmoV_readRef(valA), cosmoV_readRef(valB));
case COSMO_TNIL: return true;
default:
return false;

View File

@ -114,7 +114,7 @@ void cleanValArray(CState *state, CValueArray *array); // cleans array
void appendValArray(CState *state, CValueArray *array, CValue val);
void printValue(CValue val);
COSMO_API bool cosmoV_equal(CValue valA, CValue valB);
COSMO_API bool cosmoV_equal(CState *state, CValue valA, CValue valB);
COSMO_API CObjString *cosmoV_toString(CState *state, CValue val);
COSMO_API cosmo_Number cosmoV_toNumber(CState *state, CValue val);
COSMO_API const char *cosmoV_typeStr(CValue val); // return constant char array for corresponding type

View File

@ -633,7 +633,7 @@ int cosmoV_execute(CState *state) {
uint16_t indx = READUINT();
CValue ident = constants[indx]; // grabs identifier
CValue val; // to hold our value
cosmoT_get(&state->globals->tbl, ident, &val);
cosmoT_get(state, &state->globals->tbl, ident, &val);
cosmoV_pushValue(state, val); // pushes the value to the stack
continue;
}
@ -766,7 +766,7 @@ int cosmoV_execute(CState *state) {
} else if (obj->type == COBJ_TABLE) {
CObjTable *tbl = (CObjTable*)obj;
cosmoT_get(&tbl->tbl, *key, &val);
cosmoT_get(state, &tbl->tbl, *key, &val);
} else {
cosmoV_error(state, "No proto defined! Couldn't __index from type %s", cosmoV_typeStr(*temp));
return -1;
@ -1182,7 +1182,7 @@ int cosmoV_execute(CState *state) {
StkPtr valA = cosmoV_pop(state);
// compare & push
cosmoV_pushBoolean(state, cosmoV_equal(*valA, *valB));
cosmoV_pushBoolean(state, cosmoV_equal(state, *valA, *valB));
continue;
}
case OP_GREATER: {

View File

@ -62,6 +62,10 @@ COSMO_API bool cosmoV_set(CState *state);
// 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);
// clears the stack, callstack and restores the state into a usable state after a calloverflow or another hard to recover error
// (keeps the global table intact)
COSMO_API bool cosmoV_restore(CState *state);
// nice to have wrappers
// pushes a raw CValue to the stack, might throw an error if the stack is overflowed (with the SAFE_STACK macro on)