Change == and != to do pointer comparison on arrays and proplists (formerly === and !==). Remove === and !=== and introduce function DeepEqual for contents comparison.

There have been some bugs and crashes related to unwanted deep comparison (e.g. in the maze scenario). Scripters very rarely need deep comparison, so it should not be the default for the most commonly used operator.

This also changes behaviour of GetIndexOf to do pointer comparison.

This change has Guenther's seal of approval.
heavy-resources
Sven Eberhardt 2014-04-19 22:33:31 +02:00
parent 04e1c74c05
commit 445d759a72
9 changed files with 58 additions and 48 deletions

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE funcs
SYSTEM '../../../clonk.dtd'>
<?xml-stylesheet type="text/xsl" href="../../../clonk.xsl"?>
<funcs>
<func>
<title>DeepEqual</title>
<category>Script</category>
<version>5.5 OC</version>
<syntax>
<rtype>bool</rtype>
<params>
<param>
<type>any</type>
<name>value1</name>
<desc>First value for comparison.</desc>
</param>
<param>
<type>any</type>
<name>value2</name>
<desc>Second value for comparison.</desc>
</param>
</params>
</syntax>
<desc>Compares two values. Unlike the <emlink href="script/operatoren.html#equality">==-operator</emlink>, DeepEqual compares the elements of proplists and arrays if two non-equal proplists are passed.</desc>
<examples>
<example>
<code>var foo={a=1};
var bar={a=1};
<funclink>Log</funclink>("Pointer comparison: %v, deep comparison: %v", foo==bar, DeepEqual(foo, bar));</code>
<text>Logs "Pointer comparison: false, deep comparison: true".</text>
</example>
</examples>
</func>
<author>Sven2</author><date>2014-04</date>
</funcs>

View File

@ -162,28 +162,14 @@
<row>
<col>9l</col>
<col id="==">==</col>
<col>Returns whether a equals b.</col>
<col>Returns whether a equals b. For proplists and arrays, pointers are compared. Use <funclink>DeepEqual</funclink> to compare contents.</col>
<col>postfix</col>
<col>bool, any/any</col>
</row>
<row>
<col>9l</col>
<col id="!=">!=</col>
<col>Returns whether a is unequal to b.</col>
<col>postfix</col>
<col>bool, any/any</col>
</row>
<row>
<col>9l</col>
<col id="===">===</col>
<col>Returns whether a and b refer to the same thing.</col>
<col>postfix</col>
<col>bool, any/any</col>
</row>
<row>
<col>9l</col>
<col id="!==">!==</col>
<col>Returns whether a and b do not refer to the same thing.</col>
<col>Returns whether a is unequal to b. For proplists and arrays, pointers are compared. Use !<funclink>DeepEqual</funclink> to compare contents.</col>
<col>postfix</col>
<col>bool, any/any</col>
</row>

View File

@ -765,17 +765,17 @@ func FxFallTimer(object target, effect, int timer)
/* Replaces the named action by an instance with a different speed */
func PushActionSpeed(string action, int n)
{
if (ActMap === this.Prototype.ActMap)
if (ActMap == this.Prototype.ActMap)
ActMap = { Prototype = this.Prototype.ActMap };
ActMap[action] = { Prototype = ActMap[action], Speed = n };
if (this.Action === ActMap[action].Prototype)
if (this.Action == ActMap[action].Prototype)
this.Action = ActMap[action];
}
/* Resets the named action to the previous one */
func PopActionSpeed(string action, int n) {
// FIXME: This only works if PushActionSpeed and PopActionSpeed are the only functions manipulating the ActMap
if (this.Action === ActMap[action])
if (this.Action == ActMap[action])
this.Action = ActMap[action].Prototype;
ActMap[action] = ActMap[action].Prototype;
}

View File

@ -79,8 +79,8 @@ func FindCaveConnections()
// But since nothing really "breaks" on this occasion, just stick with the simple check for now.
var has_overlap = false;
for (check_link in all_links)
if (check_link[0] !== cave && check_link[1] !== cave)
if (check_link[0] !== cave2 && check_link[1] !== cave2)
if (check_link[0] != cave && check_link[1] != cave)
if (check_link[0] != cave2 && check_link[1] != cave2)
if (IsLineOverlap(cave.X, cave.Y, cave2.X, cave2.Y, check_link[0].X, check_link[0].Y, check_link[1].X, check_link[1].Y))
{ has_overlap=true; break; }
if (has_overlap) continue;
@ -121,7 +121,7 @@ func MakeMaze()
cave.depth = path_length;
for (var cave2 in cave.links[:]) // force a copy because cave.links is modified in the loop
{
if (path_length && cave2 === path_to_cave[path_length-1]) continue;
if (path_length && cave2 == path_to_cave[path_length-1]) continue;
// Only first path survives
if (cave2.path)
{

View File

@ -109,8 +109,6 @@ enum C4AulBCCType
AB_LessThanEqual, // <=
AB_GreaterThan, // >
AB_GreaterThanEqual, // >=
AB_Identical, // ===
AB_NotIdentical, // !==
AB_Equal, // ==
AB_NotEqual, // !=
AB_BitAnd, // &

View File

@ -402,31 +402,17 @@ C4Value C4AulExec::Exec(C4AulBCC *pCPos, bool fPassErrors)
PopValue();
break;
}
case AB_Identical: // ===
{
C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
pPar1->SetBool(pPar1->GetType() == pPar2->GetType() && pPar1->GetData() == pPar2->GetData());
PopValue();
break;
}
case AB_NotIdentical: // !==
{
C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
pPar1->SetBool(pPar1->GetType() != pPar2->GetType() || pPar1->GetData() != pPar2->GetData());
PopValue();
break;
}
case AB_Equal: // ==
{
C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
pPar1->SetBool(*pPar1 == *pPar2);
pPar1->SetBool(pPar1->IsIdenticalTo(*pPar2));
PopValue();
break;
}
case AB_NotEqual: // !=
{
C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
pPar1->SetBool(*pPar1 != *pPar2);
pPar1->SetBool(!pPar1->IsIdenticalTo(*pPar2));
PopValue();
break;
}

View File

@ -397,8 +397,6 @@ static C4ScriptOpDef C4ScriptOpMap[] =
{ 10, "<=", AB_LessThanEqual, 1, 0, 0, C4V_Bool, C4V_Int, C4V_Int},
{ 10, ">", AB_GreaterThan, 1, 0, 0, C4V_Bool, C4V_Int, C4V_Int},
{ 10, ">=", AB_GreaterThanEqual, 1, 0, 0, C4V_Bool, C4V_Int, C4V_Int},
{ 9, "===", AB_Identical, 1, 0, 0, C4V_Bool, C4V_Any, C4V_Any},
{ 9, "!==", AB_NotIdentical, 1, 0, 0, C4V_Bool, C4V_Any, C4V_Any},
{ 9, "==", AB_Equal, 1, 0, 0, C4V_Bool, C4V_Any, C4V_Any},
{ 9, "!=", AB_NotEqual, 1, 0, 0, C4V_Bool, C4V_Any, C4V_Any},
{ 8, "&", AB_BitAnd, 1, 0, 0, C4V_Int, C4V_Int, C4V_Int},
@ -662,8 +660,6 @@ static const char * GetTTName(C4AulBCCType e)
case AB_LessThanEqual: return "LessThanEqual"; // <=
case AB_GreaterThan: return "GreaterThan"; // >
case AB_GreaterThanEqual: return "GreaterThanEqual"; // >=
case AB_Identical: return "Identical"; // ===
case AB_NotIdentical: return "NotIdentical"; // !==
case AB_Equal: return "Equal"; // ==
case AB_NotEqual: return "NotEqual"; // !=
case AB_BitAnd: return "BitAnd"; // &
@ -812,8 +808,6 @@ int C4AulParse::GetStackValue(C4AulBCCType eType, intptr_t X)
case AB_LessThanEqual:
case AB_GreaterThan:
case AB_GreaterThanEqual:
case AB_Identical:
case AB_NotIdentical:
case AB_Equal:
case AB_NotEqual:
case AB_BitAnd:
@ -1026,7 +1020,7 @@ C4V_Type C4AulParse::GetLastRetType(C4V_Type to)
case AB_LeftShift: case AB_RightShift: case AB_BitAnd: case AB_BitXOr: case AB_BitOr:
from = C4V_Int; break;
case AB_Not: case AB_LessThan: case AB_LessThanEqual: case AB_GreaterThan: case AB_GreaterThanEqual:
case AB_Identical: case AB_NotIdentical: case AB_Equal: case AB_NotEqual:
case AB_Equal: case AB_NotEqual:
from = C4V_Bool; break;
default:
from = C4V_Any; break;

View File

@ -403,13 +403,19 @@ static int FnGetIndexOf(C4PropList * _this, C4ValueArray * pArray, const C4Value
if (!pArray) return -1;
int32_t iSize = pArray->GetSize();
for (int32_t i = 0; i < iSize; ++i)
if (Needle == pArray->GetItem(i))
if (Needle.IsIdenticalTo(pArray->GetItem(i)))
// element found
return i;
// element not found
return -1;
}
static bool FnDeepEqual(C4PropList * _this, const C4Value & v1, const C4Value & v2)
{
// return if v1==v2 with deep comparison on arrays and proplists
return v1 == v2;
}
static C4Void FnSetLength(C4PropList * _this, C4ValueArray *pArray, int iNewSize)
{
// safety
@ -670,6 +676,7 @@ void InitCoreFunctionMap(C4AulScriptEngine *pEngine)
F(GetLength);
F(SetLength);
F(GetIndexOf);
F(DeepEqual);
F(FatalError);
F(StartCallTrace);
F(StartScriptProfiler);

View File

@ -125,6 +125,9 @@ public:
bool operator == (const C4Value& Value2) const;
bool operator != (const C4Value& Value2) const;
// Identical comparison
bool IsIdenticalTo(const C4Value &cmp) const { return GetType()==cmp.GetType() && GetData()==cmp.GetData(); }
// Change and set Type to int in case it was nil or bool before
C4Value & operator += (int32_t by) { Data.Int += by; Type=C4V_Int; return *this; }
C4Value & operator -= (int32_t by) { Data.Int -= by; Type=C4V_Int; return *this; }