diff --git a/doc/source/reference/embedding/the_stack.rst b/doc/source/reference/embedding/the_stack.rst index 9c5f9aef..570cecd9 100644 --- a/doc/source/reference/embedding/the_stack.rst +++ b/doc/source/reference/embedding/the_stack.rst @@ -102,3 +102,7 @@ The following functions convert a squirrel value in the stack to a C value:: The function sq_cmp compares 2 values from the stack and returns their relation (like strcmp() in ANSI C).:: SQInteger sq_cmp(HSQUIRRELVM v); + +The function sq_cmpex works like sq_cmp, but reports error conditions and sets alwaysfalse to SQTrue if the comparison would always return false, as is the case with floating-point NAN.:: + + SQRESULT sq_cmpex(HSQUIRRELVM v,SQInteger *res,SQBool *alwaysfalse); diff --git a/doc/source/reference/language/metamethods.rst b/doc/source/reference/language/metamethods.rst index 260b0308..0b016f5f 100644 --- a/doc/source/reference/language/metamethods.rst +++ b/doc/source/reference/language/metamethods.rst @@ -198,6 +198,8 @@ returns an integer as follow: | < 0 | if ``this`` < ``other`` | +-----------+----------------------------+ +can also return a floating-point NAN value, which results in ``< > <= >=`` returning false and ``<=>`` returning NAN. + ^^^^^^^^^^^^^^^^^^^^^^^^ _call ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/include/squirrel.h b/include/squirrel.h index 251128ac..d8675b66 100644 --- a/include/squirrel.h +++ b/include/squirrel.h @@ -235,6 +235,7 @@ SQUIRREL_API SQInteger sq_gettop(HSQUIRRELVM v); SQUIRREL_API void sq_settop(HSQUIRRELVM v,SQInteger newtop); SQUIRREL_API SQRESULT sq_reservestack(HSQUIRRELVM v,SQInteger nsize); SQUIRREL_API SQInteger sq_cmp(HSQUIRRELVM v); +SQUIRREL_API SQRESULT sq_cmpex(HSQUIRRELVM v, SQInteger *res, SQBool *alwaysfalse); SQUIRREL_API void sq_move(HSQUIRRELVM dest,HSQUIRRELVM src,SQInteger idx); /*object creation handling*/ diff --git a/squirrel/sqapi.cpp b/squirrel/sqapi.cpp index 54eecac4..75e1e79a 100644 --- a/squirrel/sqapi.cpp +++ b/squirrel/sqapi.cpp @@ -859,10 +859,20 @@ void sq_remove(HSQUIRRELVM v, SQInteger idx) SQInteger sq_cmp(HSQUIRRELVM v) { SQInteger res; - v->ObjCmp(stack_get(v, -1), stack_get(v, -2),res); + bool alwaysfalse; + v->ObjCmp(stack_get(v, -1), stack_get(v, -2),res,alwaysfalse); return res; } +SQRESULT sq_cmpex(HSQUIRRELVM v, SQInteger *res, SQBool *alwaysfalse) +{ + if (!res || !alwaysfalse) return SQ_ERROR; + bool af; + SQRESULT result = v->ObjCmp(stack_get(v, -1), stack_get(v, -2), *res, af) ? SQ_OK : SQ_ERROR; + *alwaysfalse = af ? SQTrue : SQFalse; + return result; +} + SQRESULT sq_newslot(HSQUIRRELVM v, SQInteger idx, SQBool bstatic) { sq_aux_paramscheck(v, 3); @@ -1197,7 +1207,7 @@ SQRESULT sq_tailcall(HSQUIRRELVM v, SQInteger nparams) { return sq_throwerror(v, _SC("generators cannot be tail called")); } - + SQInteger stackbase = (v->_top - nparams) - v->_stackbase; if (!v->TailCall(clo, stackbase, nparams)) { return SQ_ERROR; diff --git a/squirrel/sqbaselib.cpp b/squirrel/sqbaselib.cpp index 5055f18a..ca299bf0 100644 --- a/squirrel/sqbaselib.cpp +++ b/squirrel/sqbaselib.cpp @@ -784,7 +784,8 @@ static SQInteger array_find(HSQUIRRELVM v) static bool _sort_compare(HSQUIRRELVM v, SQArray *arr, SQObjectPtr &a,SQObjectPtr &b,SQInteger func,SQInteger &ret) { if(func < 0) { - if(!v->ObjCmp(a,b,ret)) return false; + bool alwaysfalse; + if(!v->ObjCmp(a,b,ret,alwaysfalse)) return false; } else { SQInteger top = sq_gettop(v); diff --git a/squirrel/sqvm.cpp b/squirrel/sqvm.cpp index 25249f65..9215d6ab 100644 --- a/squirrel/sqvm.cpp +++ b/squirrel/sqvm.cpp @@ -204,9 +204,14 @@ bool SQVM::NEG_OP(SQObjectPtr &trg,const SQObjectPtr &o) } #define _RET_SUCCEED(exp) { result = (exp); return true; } -bool SQVM::ObjCmp(const SQObjectPtr &o1,const SQObjectPtr &o2,SQInteger &result) +bool SQVM::ObjCmp(const SQObjectPtr &o1,const SQObjectPtr &o2,SQInteger &result, bool &alwaysfalse) { SQObjectType t1 = sq_type(o1), t2 = sq_type(o2); + alwaysfalse = false; + if((t1 == OT_FLOAT && isnan(_float(o1))) || (t2 == OT_FLOAT && isnan(_float(o2)))) { + alwaysfalse = true; + _RET_SUCCEED(0); + } if(t1 == t2) { if(_rawval(o1) == _rawval(o2))_RET_SUCCEED(0); SQObjectPtr res; @@ -225,8 +230,12 @@ bool SQVM::ObjCmp(const SQObjectPtr &o1,const SQObjectPtr &o2,SQInteger &result) if(_delegable(o1)->GetMetaMethod(this, MT_CMP, closure)) { Push(o1);Push(o2); if(CallMetaMethod(closure,MT_CMP,2,res)) { + if(sq_type(res) == OT_FLOAT && isnan(_float(res))) { + alwaysfalse = true; + _RET_SUCCEED(0); + } if(sq_type(res) != OT_INTEGER) { - Raise_Error(_SC("_cmp must return an integer")); + Raise_Error(_SC("_cmp must return an integer or NAN")); return false; } _RET_SUCCEED(_integer(res)) @@ -236,7 +245,12 @@ bool SQVM::ObjCmp(const SQObjectPtr &o1,const SQObjectPtr &o2,SQInteger &result) } //continues through (no break needed) default: +#ifdef NO_POINTER_CMP + Raise_CompareError(o1,o2); + return false; +#else _RET_SUCCEED( _userpointer(o1) < _userpointer(o2)?-1:1 ); +#endif } assert(0); //if(type(res)!=OT_INTEGER) { Raise_CompareError(o1,o2); return false; } @@ -259,7 +273,6 @@ bool SQVM::ObjCmp(const SQObjectPtr &o1,const SQObjectPtr &o2,SQInteger &result) else if(t1==OT_NULL) {_RET_SUCCEED(-1);} else if(t2==OT_NULL) {_RET_SUCCEED(1);} else { Raise_CompareError(o1,o2); return false; } - } assert(0); _RET_SUCCEED(0); //cannot happen @@ -268,13 +281,14 @@ bool SQVM::ObjCmp(const SQObjectPtr &o1,const SQObjectPtr &o2,SQInteger &result) bool SQVM::CMP_OP(CmpOP op, const SQObjectPtr &o1,const SQObjectPtr &o2,SQObjectPtr &res) { SQInteger r; - if(ObjCmp(o1,o2,r)) { + bool alwaysfalse; + if(ObjCmp(o1,o2,r,alwaysfalse)) { switch(op) { - case CMP_G: res = (r > 0); return true; - case CMP_GE: res = (r >= 0); return true; - case CMP_L: res = (r < 0); return true; - case CMP_LE: res = (r <= 0); return true; - case CMP_3W: res = r; return true; + case CMP_G: res = (!alwaysfalse && r > 0); return true; + case CMP_GE: res = (!alwaysfalse && r >= 0); return true; + case CMP_L: res = (!alwaysfalse && r < 0); return true; + case CMP_LE: res = (!alwaysfalse && r <= 0); return true; + case CMP_3W: if (alwaysfalse) res = NAN; else res = r; return true; } assert(0); } diff --git a/squirrel/sqvm.h b/squirrel/sqvm.h index 50927507..a65de56a 100644 --- a/squirrel/sqvm.h +++ b/squirrel/sqvm.h @@ -77,7 +77,7 @@ typedef sqvector CallInfoVec; bool NewSlotA(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val,const SQObjectPtr &attrs,bool bstatic,bool raw); bool DeleteSlot(const SQObjectPtr &self, const SQObjectPtr &key, SQObjectPtr &res); bool Clone(const SQObjectPtr &self, SQObjectPtr &target); - bool ObjCmp(const SQObjectPtr &o1, const SQObjectPtr &o2,SQInteger &res); + bool ObjCmp(const SQObjectPtr &o1, const SQObjectPtr &o2,SQInteger &res,bool &alwaysfalse); bool StringCat(const SQObjectPtr &str, const SQObjectPtr &obj, SQObjectPtr &dest); static bool IsEqual(const SQObjectPtr &o1,const SQObjectPtr &o2,bool &res); bool ToString(const SQObjectPtr &o,SQObjectPtr &res);