Aul: Add new nil-coalescing operator ??

This short-circuiting operator will evaluate to its first operand if the operand
is not nil, or to its second operand otherwise. Its intended use is to simplify
defaulting expressions that may evaluate to nil to a valid value.
rope
Nicolas Hake 2012-04-15 15:44:01 +02:00
parent 8c6530c0d7
commit 7efd9a9b9a
4 changed files with 38 additions and 9 deletions

View File

@ -208,6 +208,13 @@
<col>postfix</col>
<col>any, any/any</col>
</row>
<row>
<col>3l</col>
<col>??</col>
<col>Returns the left-hand operand if the operand is not <code>nil</code>, or the right-hand operand otherwise.</col>
<col>postfix</col>
<col>any, any/any</col>
</row>
<row>
<col>2r</col>
<col>*=</col>
@ -292,13 +299,17 @@ while(<strong>++somevar</strong> &lt; 10)
<text>Yet there is an important difference between the two versions: when using the postfix version, the <strong>previous</strong> value of the variable is returned. The first example will result in a count from 1 to <strong>10</strong>, since at beginning of the last loop repetition is value of <code>somevar</code> is 9. It is then increased by one (<code>somevar</code> is now 10) but the previous value of 9 is returned and compared to 10. Thus the loop will be repeated one more time and then the value of 10 is printed.</text>
<text>In the second example, on the other hand, the loop runs from 1 to <strong>9</strong>. When <code>somevar</code> is 9 and is increased, the new value of 10 is returned immediately. The result is not less than 10 and the loop ends.</text>
</part>
<h id="andor">The Operators &amp;&amp; and ||</h>
<h id="shortcircuiting">The Short-Circuiting Operators &amp;&amp;, || and ??</h>
<part>
<text>These two operators are special. If the result can be determined by the first parameter alone, the second parameter is not computed at all. For example, this script does not explode an object, because the overall result would be <code>false</code>, regardless of the result of Explode:</text>
<text>These operators are special. If the result can be determined by the first parameter alone, the second parameter is not computed at all. For example, this script does not explode an object, because the overall result would be <code>false</code>, regardless of the result of Explode:</text>
<code>0 &amp;&amp; Explode(20);</code>
<text>Further, the result is the value of the first or second parameter, depending on whether one or both were evaluated. For example, one can create a knight if possible or else a Clonk:</text>
<code>CreateObject(Knight,0,0,GetOwner()) || CreateObject(Clonk,0,0,GetOwner())</code>
</part>
<h id="nilcoalesce">The operator <code>??</code></h>
<part>
<text>The <code>??</code> operator is called the nil-coalescing operator. It can be used to specify a default value for an expression or function call that may evaluate to <code>nil</code>.</text>
</part>
<h id="prio">Priority and Associating</h>
<part>
<text>This subject shows how operator priority is evaluated in detail.</text>

View File

@ -157,8 +157,9 @@ enum C4AulBCCType
AB_NEW_ARRAY, // semi-constant: array
AB_NEW_PROPLIST, // create a new proplist
AB_JUMP, // jump
AB_JUMPAND, // jump if zero, else pop the stack
AB_JUMPOR, // jump if not zero, else pop the stack
AB_JUMPAND, // jump if convertible to false, else pop the stack
AB_JUMPOR, // jump if convertible to true, else pop the stack
AB_JUMPNNIL, // jump if not nil, else pop the stack
AB_CONDN, // conditional jump (negated, pops stack)
AB_COND, // conditional jump (pops stack)
AB_FOREACH_NEXT, // foreach: next element

View File

@ -576,6 +576,20 @@ C4Value C4AulExec::Exec(C4AulBCC *pCPos, bool fPassErrors)
}
break;
case AB_JUMPNNIL: // ??
{
if (pCurVal[0].GetType() != C4V_Nil)
{
fJump = true;
pCPos += pCPos->Par.i;
}
else
{
PopValue();
}
break;
}
case AB_CONDN:
if (!pCurVal[0])
{

View File

@ -394,6 +394,7 @@ static C4ScriptOpDef C4ScriptOpMap[] =
{ 6, "|", AB_BitOr, AB_ERR, 1, 0, 0, C4V_Int, C4V_Int, C4V_Int},
{ 5, "&&", AB_JUMPAND, AB_ERR, 1, 0, 0, C4V_Bool, C4V_Bool, C4V_Bool},
{ 4, "||", AB_JUMPOR, AB_ERR, 1, 0, 0, C4V_Bool, C4V_Bool, C4V_Bool},
{ 3, "??", AB_JUMPNNIL, AB_ERR, 1, 0, 0, C4V_Bool, C4V_Any, C4V_Any},
// changers
{ 2, "*=", AB_Mul, AB_ERR, 1, 1, 0, C4V_Int, C4V_Int, C4V_Int},
@ -670,6 +671,7 @@ static const char * GetTTName(C4AulBCCType e)
case AB_JUMP: return "JUMP"; // jump
case AB_JUMPAND: return "JUMPAND";
case AB_JUMPOR: return "JUMPOR";
case AB_JUMPNNIL: return "JUMPNNIL"; // nil-coalescing operator ("??")
case AB_CONDN: return "CONDN"; // conditional jump (negated, pops stack)
case AB_COND: return "COND"; // conditional jump (pops stack)
case AB_FOREACH_NEXT: return "FOREACH_NEXT"; // foreach: next element
@ -828,10 +830,11 @@ int C4AulParse::GetStackValue(C4AulBCCType eType, intptr_t X)
case AB_COND:
case AB_POP_TO:
case AB_RETURN:
// JUMPAND/JUMPOR are special: They either jump over instructions adding one to the stack
// JUMPAND/JUMPOR/JUMPNNIL are special: They either jump over instructions adding one to the stack
// or decrement the stack. Thus, for stack counting purposes, they decrement.
case AB_JUMPAND:
case AB_JUMPOR:
case AB_JUMPNNIL:
return -1;
case AB_FUNC:
@ -1083,7 +1086,7 @@ int C4AulParse::JumpHere()
static bool IsJump(C4AulBCCType t)
{
return t == AB_JUMP || t == AB_JUMPAND || t == AB_JUMPOR || t == AB_CONDN || t == AB_COND;
return t == AB_JUMP || t == AB_JUMPAND || t == AB_JUMPOR || t == AB_JUMPNNIL || t == AB_CONDN || t == AB_COND;
}
void C4AulParse::SetJumpHere(int iJumpOp)
@ -2435,7 +2438,7 @@ void C4AulParse::Parse_Expression2(int iParentPrio)
}
Shift();
if (C4ScriptOpMap[OpID].Code == AB_JUMPAND || C4ScriptOpMap[OpID].Code == AB_JUMPOR)
if (C4ScriptOpMap[OpID].Code == AB_JUMPAND || C4ScriptOpMap[OpID].Code == AB_JUMPOR || C4ScriptOpMap[OpID].Code == AB_JUMPNNIL)
{
// create bytecode, remember position
// Jump or discard first parameter
@ -2966,7 +2969,7 @@ bool C4ScriptHost::Parse()
{
switch (pBCC->bccType)
{
case AB_JUMP: case AB_JUMPAND: case AB_JUMPOR: case AB_CONDN: case AB_COND:
case AB_JUMP: case AB_JUMPAND: case AB_JUMPOR: case AB_JUMPNNIL: case AB_CONDN: case AB_COND:
labels[pBCC + pBCC->Par.i] = ++labeln; break;
default: break;
}
@ -2992,7 +2995,7 @@ bool C4ScriptHost::Parse()
assert(!pBCC->Par.X); fprintf(stderr, "\n"); break;
case AB_CARRAY: case AB_CPROPLIST:
fprintf(stderr, "\t%p\n", reinterpret_cast<void *>(pBCC->Par.X)); break;
case AB_JUMP: case AB_JUMPAND: case AB_JUMPOR: case AB_CONDN: case AB_COND:
case AB_JUMP: case AB_JUMPAND: case AB_JUMPOR: case AB_JUMPNNIL: case AB_CONDN: case AB_COND:
fprintf(stderr, "\t%d\n", labels[pBCC + pBCC->Par.i]); break;
default:
fprintf(stderr, "\t%d\n", pBCC->Par.i); break;