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 afac75753f
commit 66d77bc54b
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 (!IS_BOOLEAN(args[0]) || (nargs == 2 && !IS_STRING(args[1]))) {
if (nargs == 2) { if (nargs == 2) {
cosmoV_typeError(state, "assert()", "<boolean>, <string>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); 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])); cosmoV_typeError(state, "assert()", "<boolean>", "%s", cosmoV_typeStr(args[0]));
} }
return 0; return 0;
} }
if (!cosmoV_readBoolean(args[0])) { // expression passed was false, error! if (!cosmoV_readBoolean(args[0])) // expression passed was false, error!
if (nargs == 2) { cosmoV_error(state, "%s", nargs == 2 ? cosmoV_readCString(args[1]) : "assert() failed!");
cosmoV_error(state, "%s", cosmoV_readCString(args[1]));
}
else { // optional custom error message
cosmoV_error(state, "%s", "assert() failed!");
}
} // else do nothing :)
return 0; return 0;
} }

View File

@ -39,7 +39,7 @@ void freeChunk(CState* state, CChunk *chunk) {
int addConstant(CState* state, CChunk *chunk, CValue value) { int addConstant(CState* state, CChunk *chunk, CValue value) {
// before adding the constant, check if we already have it // before adding the constant, check if we already have it
for (size_t i = 0; i < chunk->constants.count; i++) { 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! 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 // mark all references associated with the object
// black = keep, white = discard
void blackenObject(CState *state, CObj *obj) { void blackenObject(CState *state, CObj *obj) {
markObject(state, (CObj*)obj->proto); markObject(state, (CObj*)obj->proto);
switch (obj->type) { switch (obj->type) {

View File

@ -90,25 +90,48 @@ void cosmoO_free(CState *state, CObj* obj) {
cosmoM_free(state, CObjClosure, closure); cosmoM_free(state, CObjClosure, closure);
break; 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) if (obj1->type != obj2->type)
return false; goto _eqFail;
switch (obj1->type) { switch (obj1->type) {
case COBJ_STRING:
return obj1 == obj2; // compare pointers because we already intern all strings :)
case COBJ_CFUNCTION: { case COBJ_CFUNCTION: {
CObjCFunction *cfunc1 = (CObjCFunction*)obj1; CObjCFunction *cfunc1 = (CObjCFunction*)obj1;
CObjCFunction *cfunc2 = (CObjCFunction*)obj2; 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: default:
return false; goto _eqFail;
} }
_eqFail:
// TODO: add support for an '__equal' metamethod
return false;
} }
CObjObject *cosmoO_newObject(CState *state) { CObjObject *cosmoO_newObject(CState *state) {
@ -312,8 +335,8 @@ bool cosmoO_isDescendant(CObj *obj, CObjObject *proto) {
// returns false if error thrown // returns false if error thrown
bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *val, CObj *obj) { 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 (!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(&cosmoV_readTable(*val)->tbl, key, val)) { 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_pushValue(state, *val); // push function
cosmoV_pushRef(state, (CObj*)obj); // push object cosmoV_pushRef(state, (CObj*)obj); // push object
if (cosmoV_call(state, 1, 1) != COSMOVM_OK) // call the function with the 1 argument 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 // 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_pushValue(state, ret); // push function
cosmoV_pushRef(state, (CObj*)obj); // push object cosmoV_pushRef(state, (CObj*)obj); // push object
cosmoV_pushValue(state, val); // push new value 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)) if (readFlag(object->istringFlags, flag))
return false; // it's been cached as bad 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! // mark it bad!
setFlagOn(object->istringFlags, flag); setFlagOn(object->istringFlags, flag);
return false; return false;

View File

@ -136,7 +136,7 @@ static inline bool IS_CALLABLE(CValue val) {
} }
void cosmoO_free(CState *state, CObj* obj); 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 // walks the protos of obj and checks for proto
bool cosmoO_isDescendant(CObj *obj, CObjObject *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) // 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 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 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; CTableEntry *tomb = NULL;
@ -104,7 +104,7 @@ static CTableEntry *findEntry(CTableEntry *entries, int mask, CValue key) {
// its a tombstone! // its a tombstone!
tomb = entry; tomb = entry;
} }
} else if (cosmoV_equal(entry->key, key)) { } else if (cosmoV_equal(state, entry->key, key)) {
return entry; return entry;
} }
@ -142,7 +142,7 @@ static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrin
continue; // skip empty keys continue; // skip empty keys
// get new entry location & update the node // 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->key = oldEntry->key;
newEntry->val = oldEntry->val; newEntry->val = oldEntry->val;
newCount++; // inc count newCount++; // inc count
@ -178,7 +178,7 @@ COSMO_API CValue* cosmoT_insert(CState *state, CTable *tbl, CValue key) {
} }
// insert into the table // 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->key)) {
if (IS_NIL(entry->val)) // is it empty? 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; 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 // sanity check
if (tbl->count == 0) { if (tbl->count == 0) {
*val = cosmoV_newNil(); *val = cosmoV_newNil();
return false; return false;
} }
CTableEntry *entry = findEntry(tbl->table, tbl->capacityMask, key); CTableEntry *entry = findEntry(state, tbl->table, tbl->capacityMask, key);
*val = entry->val; *val = entry->val;
// return if get was successful // 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) { bool cosmoT_remove(CState* state, CTable *tbl, CValue key) {
if (tbl->count == 0) return 0; // sanity check 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 if (IS_NIL(entry->key)) // sanity check
return false; 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); CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32_t hash);
CValue *cosmoT_insert(CState *state, CTable *tbl, CValue key); 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); bool cosmoT_remove(CState *state, CTable *tbl, CValue key);
void cosmoT_printTable(CTable *tbl, const char *name); 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; 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? if (GET_TYPE(valA) != GET_TYPE(valB)) // are they the same type?
return false; return false;
@ -27,7 +27,7 @@ bool cosmoV_equal(CValue valA, CValue valB) {
switch (GET_TYPE(valA)) { switch (GET_TYPE(valA)) {
case COSMO_TBOOLEAN: return cosmoV_readBoolean(valA) == cosmoV_readBoolean(valB); case COSMO_TBOOLEAN: return cosmoV_readBoolean(valA) == cosmoV_readBoolean(valB);
case COSMO_TNUMBER: return cosmoV_readNumber(valA) == cosmoV_readNumber(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; case COSMO_TNIL: return true;
default: default:
return false; 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 appendValArray(CState *state, CValueArray *array, CValue val);
void printValue(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 CObjString *cosmoV_toString(CState *state, CValue val);
COSMO_API cosmo_Number cosmoV_toNumber(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 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(); uint16_t indx = READUINT();
CValue ident = constants[indx]; // grabs identifier CValue ident = constants[indx]; // grabs identifier
CValue val; // to hold our value 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 cosmoV_pushValue(state, val); // pushes the value to the stack
continue; continue;
} }
@ -766,7 +766,7 @@ int cosmoV_execute(CState *state) {
} else if (obj->type == COBJ_TABLE) { } else if (obj->type == COBJ_TABLE) {
CObjTable *tbl = (CObjTable*)obj; CObjTable *tbl = (CObjTable*)obj;
cosmoT_get(&tbl->tbl, *key, &val); cosmoT_get(state, &tbl->tbl, *key, &val);
} else { } else {
cosmoV_error(state, "No proto defined! Couldn't __index from type %s", cosmoV_typeStr(*temp)); cosmoV_error(state, "No proto defined! Couldn't __index from type %s", cosmoV_typeStr(*temp));
return -1; return -1;
@ -1182,7 +1182,7 @@ int cosmoV_execute(CState *state) {
StkPtr valA = cosmoV_pop(state); StkPtr valA = cosmoV_pop(state);
// compare & push // compare & push
cosmoV_pushBoolean(state, cosmoV_equal(*valA, *valB)); cosmoV_pushBoolean(state, cosmoV_equal(state, *valA, *valB));
continue; continue;
} }
case OP_GREATER: { 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 // 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); 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 // 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) // pushes a raw CValue to the stack, might throw an error if the stack is overflowed (with the SAFE_STACK macro on)