forked from Mirrors/openclonk
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
parent
04e1c74c05
commit
445d759a72
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -109,8 +109,6 @@ enum C4AulBCCType
|
|||
AB_LessThanEqual, // <=
|
||||
AB_GreaterThan, // >
|
||||
AB_GreaterThanEqual, // >=
|
||||
AB_Identical, // ===
|
||||
AB_NotIdentical, // !==
|
||||
AB_Equal, // ==
|
||||
AB_NotEqual, // !=
|
||||
AB_BitAnd, // &
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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; }
|
||||
|
|
Loading…
Reference in New Issue