int spn_values_comparable(const SpnValue *lhs, const SpnValue *rhs) { if (isnum(lhs) && isnum(rhs)) { return 1; } if (isobject(lhs) && isobject(rhs)) { SpnObject *obl = objvalue(lhs), *obr = objvalue(rhs); if (!class_equal(obl->isa, obr->isa)) { return 0; } return obl->isa->compare != NULL; } return 0; }
void spn_value_print(const SpnValue *val) { switch (valtype(val)) { case SPN_TTAG_NIL: { fputs("nil", stdout); break; } case SPN_TTAG_BOOL: { fputs(boolvalue(val) ? "true" : "false", stdout); break; } case SPN_TTAG_NUMBER: { if (isfloat(val)) { printf("%.*g", DBL_DIG, floatvalue(val)); } else { printf("%ld", intvalue(val)); } break; } case SPN_TTAG_STRING: { SpnString *s = stringvalue(val); fputs(s->cstr, stdout); break; } case SPN_TTAG_ARRAY: { SpnArray *array = objvalue(val); print_array(array, 0); break; } case SPN_TTAG_HASHMAP: { SpnHashMap *hashmap = objvalue(val); print_hashmap(hashmap, 0); break; } case SPN_TTAG_FUNC: { SpnFunction *func = funcvalue(val); void *p; if (func->native) { p = (void *)(ptrdiff_t)(func->repr.fn); } else { p = func->repr.bc; } printf("<function %p>", p); break; } case SPN_TTAG_USERINFO: { void *ptr = isobject(val) ? objvalue(val) : ptrvalue(val); printf("<userinfo %p>", ptr); break; } default: SHANT_BE_REACHED(); break; } }
void spn_value_release(const SpnValue *val) { if (isobject(val)) { assert(isstring(val) || isarray(val) || ishashmap(val) || isfunc(val) || isuserinfo(val)); spn_object_release(objvalue(val)); } }
int spn_value_equal(const SpnValue *lhs, const SpnValue *rhs) { /* first, make sure that we compare values of the same type * (values of different types cannot possibly be equal) */ if (valtype(lhs) != valtype(rhs)) { return 0; } switch (valtype(lhs)) { case SPN_TTAG_NIL: { return 1; /* nil can only be nil */ } case SPN_TTAG_BOOL: { return boolvalue(lhs) == boolvalue(rhs); } case SPN_TTAG_NUMBER: { return numeric_equal(lhs, rhs); } case SPN_TTAG_STRING: case SPN_TTAG_ARRAY: case SPN_TTAG_HASHMAP: case SPN_TTAG_FUNC: { return spn_object_equal(objvalue(lhs), objvalue(rhs)); } case SPN_TTAG_USERINFO: { /* an object can not equal a non-object */ if (isobject(lhs) != isobject(rhs)) { return 0; } if (isobject(lhs)) { return spn_object_equal(objvalue(lhs), objvalue(rhs)); } else { return ptrvalue(lhs) == ptrvalue(rhs); } } default: SHANT_BE_REACHED(); } return 0; }
unsigned long spn_hash_value(const SpnValue *key) { switch (valtype(key)) { case SPN_TTAG_NIL: { return 0; } case SPN_TTAG_BOOL: { return boolvalue(key); /* 0 or 1 */ } case SPN_TTAG_NUMBER: { if (isfloat(key)) { double f = floatvalue(key); /* only test for integer if it fits into one (anti-UB) */ if (LONG_MIN <= f && f <= LONG_MAX) { long i = f; /* truncate */ if (f == i) { /* it's really an integer. * This takes care of the +/- 0 problem too * (since 0 itself is an integer) */ return i; } } else { return spn_hash_bytes(&f, sizeof f); } } /* the hash value of an integer is itself */ return intvalue(key); } case SPN_TTAG_STRING: case SPN_TTAG_ARRAY: case SPN_TTAG_HASHMAP: case SPN_TTAG_FUNC: { SpnObject *obj = objvalue(key); unsigned long (*hashfn)(void *) = obj->isa->hashfn; return hashfn ? hashfn(obj) : (unsigned long)(obj); } case SPN_TTAG_USERINFO: { if (isobject(key)) { SpnObject *obj = objvalue(key); unsigned long (*hashfn)(void *) = obj->isa->hashfn; return hashfn ? hashfn(obj) : (unsigned long)(obj); } return (unsigned long)(ptrvalue(key)); } default: SHANT_BE_REACHED(); } return 0; }
unsigned long spn_hash_value(const SpnValue *key) { switch (valtype(key)) { case SPN_TTAG_NIL: { return 0; } case SPN_TTAG_BOOL: { return boolvalue(key); /* 0 or 1 */ } case SPN_TTAG_NUMBER: { if (isfloat(key)) { double f = floatvalue(key); long i = f; /* truncate */ if (f == i) { return i; /* it's really an integer */ } else { return spn_hash_bytes(&f, sizeof f); } } /* the hash value of an integer is itself */ return intvalue(key); } case SPN_TTAG_STRING: case SPN_TTAG_ARRAY: case SPN_TTAG_HASHMAP: case SPN_TTAG_FUNC: { SpnObject *obj = objvalue(key); unsigned long (*hashfn)(void *) = obj->isa->hashfn; return hashfn ? hashfn(obj) : (unsigned long)(obj); } case SPN_TTAG_USERINFO: { if (isobject(key)) { SpnObject *obj = objvalue(key); unsigned long (*hashfn)(void *) = obj->isa->hashfn; return hashfn ? hashfn(obj) : (unsigned long)(obj); } return (unsigned long)(ptrvalue(key)); } default: SHANT_BE_REACHED(); } return 0; }